renamed epoch_io plugin to the standard io_epoch naming scheme

This commit is contained in:
Paolo Cignoni cignoni 2008-12-09 11:15:57 +00:00
parent ab63abfa6e
commit 37dec4f42b
19 changed files with 3635 additions and 0 deletions

View File

@ -0,0 +1,88 @@
#include <vector>
#include <vcg/math/matrix33.h>
#include <vcg/math/matrix44.h>
#include <vcg/space/point3.h>
using namespace vcg;
using namespace std;
#include "radial_distortion.h"
#include "epoch_camera.h"
/*
Epoch Camera
*/
// This function take in input a point in image space (e.g. with coords in range [0..1024]x[0..768]
// a depth value and it returns the point in absolute 3D coords
//
void EpochCamera::DepthTo3DPoint(double x, double y, double depth, Point3d &M) const
{
Point3d m_temp = Kinv * Point3d(x,y,1);
double oldx, oldy;
rd.ComputeOldXY(m_temp[0] / m_temp[2], m_temp[1] / m_temp[2], oldx, oldy);
m_temp=Point3d(oldx,oldy,1);
Point3d fp=t;
Point3d end = TRinv*m_temp;
Point3d dir =fp-end;
dir.Normalize();
M = fp-dir*depth;
}
bool EpochCamera::Open(const char *filename)
{
FILE *fp=fopen(filename,"rb");
if(!fp) return false;
fscanf(fp,"%lf %lf %lf",&(K[0][0]),&(K[0][1]),&(K[0][2]));
fscanf(fp,"%lf %lf %lf",&(K[1][0]),&(K[1][1]),&(K[1][2]));
fscanf(fp,"%lf %lf %lf",&(K[2][0]),&(K[2][1]),&(K[2][2]));
k.resize(3);
fscanf(fp,"%lf %lf %lf",&(k[0]),&(k[1]),&(k[2]));
fscanf(fp,"%lf %lf %lf",&(R[0][0]),&(R[0][1]),&(R[0][2]));
fscanf(fp,"%lf %lf %lf",&(R[1][0]),&(R[1][1]),&(R[1][2]));
fscanf(fp,"%lf %lf %lf",&(R[2][0]),&(R[2][1]),&(R[2][2]));
fscanf(fp,"%lf %lf %lf",&(t[0]),&(t[1]),&(t[2]));
fscanf(fp,"%i %i",&width,&height);
fclose(fp);
Kinv=Inverse(K);
rd.SetParameters(k);
// TR = [R | -Rt] 4x4 matrix with upperleft a 3x3 rotation
// on the right the rotated translation -Rt and 0001 in the
// lower line.
R.transposeInPlace();
#ifndef VCG_USE_EIGEN
for(int i=0;i<3;++i)
for(int j=0;j<3;++j)
TR[i][j]= R[i][j];
#else
TR.corner<3,3>(Eigen::TopLeft) = R.transpose();
#endif
Point3d rt= R*(-t);
for(int i=0;i<3;++i)
TR[i][3]=rt[i];
for(int j=0;j<3;++j)
TR[3][j]=0;
TR[3][3]=1;
TRinv=Inverse(TR);
return true;
}

View File

@ -0,0 +1,24 @@
namespace vcg
{
class EpochCamera
{
public:
Matrix33d K; // parametri intriseci camera
Matrix33d Kinv;
std::vector<double> k;
Matrix33d R;
Matrix44d TR; // [R | -Rt] e.g. la matrice in cui
Matrix44d TRinv;
Point3d t;
int width, height;
RadialDistortion rd;
void DepthTo3DPoint(double x, double y, double depth, Point3d &M) const;
bool Open(const char * filename);
};
}

View File

@ -0,0 +1,781 @@
/****************************************************************************
* MeshLab o o *
* An extendible mesh processor o o *
* _ O _ *
* Copyright(C) 2005, 2006 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/****************************************************************************
History
$Log$
Revision 1.22 2008/04/04 14:08:07 cignoni
Solved namespace ambiguities caused by the removal of a silly 'using namespace' in meshmodel.h
Revision 1.21 2008/03/06 08:20:50 cignoni
updated to the new histogram
Revision 1.20 2008/02/12 21:59:02 cignoni
removed mask bug and added scaling of maps
Revision 1.19 2007/11/26 07:35:26 cignoni
Yet another small cosmetic change to the interface of the io filters.
Revision 1.18 2007/10/12 10:09:29 corsini
signed/unsigned warning removed
Revision 1.17 2007/10/12 10:06:58 corsini
fix some warnings
Revision 1.16 2007/10/08 08:55:44 cignoni
Added automatic exporting of ply and aln from the dialog
Revision 1.15 2007/04/16 09:25:29 cignoni
** big change **
Added Layers managemnt.
Interfaces are changing again...
Revision 1.14 2007/03/20 16:23:08 cignoni
Big small change in accessing mesh interface. First step toward layers
Revision 1.13 2007/03/20 15:52:46 cignoni
Patched issue related to path with non ascii chars
Revision 1.12 2007/02/26 11:41:07 corsini
add more control to depth filter through interface paramters
Revision 1.11 2007/01/24 08:33:15 cignoni
Still experiments in filtering depth jumps
Revision 1.10 2007/01/23 14:31:16 corsini
add improved depth filtering to remove artifacts
Revision 1.9 2007/01/23 11:38:55 cignoni
Added depth jump control in laplacian smoothing of featureless areas of depthmap
Revision 1.8 2007/01/23 10:50:44 cignoni
Better comments and variable names
Revision 1.7 2007/01/23 09:21:28 corsini
add mean+erosion filter
Revision 1.6 2007/01/11 11:48:04 cignoni
Reordered include
Revision 1.5 2006/12/06 21:25:00 cignoni
small optimization and logging for profiling
Revision 1.4 2006/11/30 11:40:33 cignoni
Updated the calls to the hole filling functions to the new interface
Revision 1.3 2006/11/29 00:59:16 cignoni
Cleaned plugins interface; changed useless help class into a plain string
Revision 1.2 2006/11/08 15:49:42 cignoni
Added quality to the loaded masks
Revision 1.1 2006/11/07 18:14:21 cignoni
Moved from the epoch svn repository
Revision 1.1 2006/01/20 13:03:27 cignoni
*** empty log message ***
*****************************************************************************/
#include <Qt>
#include <QtGui>
#include <QtXml/QDomDocument>
#include <QtXml/QDomElement>
#include <QtXml/QDomNode>
#include <QMessageBox>
#include <QFileDialog>
#include "epoch_io.h"
#include "epoch_reconstruction.h"
#include <vcg/math/matrix33.h>
#include <vcg/math/histogram.h>
#include <vcg/complex/trimesh/update/bounding.h>
#include <vcg/complex/trimesh/create/platonic.h>
#include <vcg/complex/trimesh/update/bounding.h>
#include <vcg/complex/trimesh/append.h>
#include <vcg/complex/trimesh/clustering.h>
#include <vcg/complex/trimesh/update/normal.h>
#include <vcg/complex/trimesh/update/position.h>
#include <vcg/complex/trimesh/hole.h>
#include <wrap/io_trimesh/io_mask.h>
#include <wrap/io_trimesh/export_ply.h>
#include "../cleanfilter/remove_small_cc.h"
#include <meshlab/alnParser.h>
FILE *logFP=0;
using namespace std;
using namespace vcg;
void EpochModel::depthFilter(FloatImage &depthImgf, FloatImage &countImgf, float depthJumpThr,
bool dilation, int dilationNumPasses, int dilationWinsize,
bool erosion, int erosionNumPasses, int erosionWinsize)
{
FloatImage depth;
FloatImage depth2;
int w = depthImgf.w;
int h = depthImgf.h;
depth=depthImgf;
if (dilation)
{
for (int k = 0; k < dilationNumPasses; k++)
{
depth.Dilate(depth2, dilationWinsize / 2);
depth=depth2;
}
}
if (erosion)
{
for (int k = 0; k < erosionNumPasses; k++)
{
depth.Erode(depth2, erosionWinsize / 2);
depth=depth2;
}
}
Histogramf HH;
HH.Clear();
HH.SetRange(0,depthImgf.MaxVal()-depthImgf.MinVal(),10000);
for(int i=1; i < static_cast<int>(depthImgf.v.size()); ++i)
HH.Add(fabs(depthImgf.v[i]-depth.v[i-1]));
if(logFP) fprintf(logFP,"**** Depth histogram 2 Min %f Max %f Avg %f Percentiles ((10)%f (25)%f (50)%f (75)%f (90)%f)\n",HH.MinV(),HH.MaxV(),HH.Avg(),
HH.Percentile(.1),HH.Percentile(.25),HH.Percentile(.5),HH.Percentile(.75),HH.Percentile(.9));
int deletedCnt=0;
depthJumpThr = static_cast<float>(HH.Percentile(0.8));
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++)
{
if ((depthImgf.Val(x, y) - depth.Val(x, y)) / depthImgf.Val(x, y) > 0.6)
{
countImgf.Val(x, y) = 0.0f;
++deletedCnt;
}
}
countImgf.convertToQImage().save("tmp_filteredcount.jpg","jpg");
if(logFP) fprintf(logFP,"**** depthFilter: deleted %i on %i\n",deletedCnt,w*h);
}
float EpochModel::ComputeDepthJumpThr(FloatImage &depthImgf, float percentile)
{
Histogramf HH;
HH.Clear();
HH.SetRange(0,depthImgf.MaxVal()-depthImgf.MinVal(),10000);
for(unsigned int i=1; i < static_cast<unsigned int>(depthImgf.v.size()); ++i)
HH.Add(fabs(depthImgf.v[i]-depthImgf.v[i-1]));
if(logFP) fprintf(logFP,"**** Depth histogram Min %f Max %f Avg %f Percentiles ((10)%f (25)%f (50)%f (75)%f (90)%f)\n",HH.MinV(),HH.MaxV(),HH.Avg(),
HH.Percentile(.1),HH.Percentile(.25),HH.Percentile(.5),HH.Percentile(.75),HH.Percentile(.9));
return HH.Percentile(percentile);
}
/// Apply the hand drawn mask image
bool EpochModel::CombineHandMadeMaskAndCount(CharImage &CountImg, QString maskName )
{
QImage maskImg(maskName);
qDebug("Trying to read maskname %s",qPrintable(maskName));
if(maskImg.isNull())
return false;
if( (maskImg.width()!= CountImg.w) || (maskImg.height()!= CountImg.h) )
{
qDebug("Warning mask and images does not match! %i %i vs %i %i",maskImg.width(),CountImg.w,maskImg.height(),CountImg.h);
return false;
}
for(int j=0;j<maskImg.height();++j)
for(int i=0;i<maskImg.width();++i)
if(qRed(maskImg.pixel(i,j))>128)
CountImg.Val(i,j)=0;
return true;
}
void EpochModel::SmartSubSample(int factor, FloatImage &fli, CharImage &chi, FloatImage &subD, FloatImage &subQ, int minCount)
{
assert(fli.w==chi.w && fli.h==chi.h);
int w=fli.w/factor;
int h=fli.h/factor;
subQ.resize(w,h);
subD.resize(w,h);
for(int i=0;i<w;++i)
for(int j=0;j<h;++j)
{
float maxcount=0;
int cnt=0;
float bestVal=0;
for(int ki=0;ki<factor;++ki)
for(int kj=0;kj<factor;++kj)
{
float q= chi.Val(i*factor+ki,j*factor+kj) - minCount+1 ;
if(q>0)
{
maxcount+= q;
bestVal +=q*fli.Val(i*factor+ki,j*factor+kj);
cnt++;
}
}
if(cnt>0)
{
subD.Val(i,j)=float(bestVal)/maxcount;
subQ.Val(i,j)=minCount-1 + float(maxcount)/cnt ;
}
else
{
subD.Val(i,j)=0;
subQ.Val(i,j)=0;
}
}
}
/*
This filter average apply a laplacian smoothing over a depth map averaging the samples with a weighting scheme that follows the Counting masks.
The result of the laplacian is applied only on sample with low quality.
*/
void EpochModel::Laplacian2(FloatImage &depthImg, FloatImage &countImg, int minCount, CharImage &featureMask, float depthThr)
{
FloatImage Sum;
int w=depthImg.w,h=depthImg.h;
Sum.resize(w,h);
for(int y=1;y<h-1;++y)
for(int x=1;x<w-1;++x)
{
float curDepth=depthImg.Val(x,y);
int cnt=0;
for(int j=-1;j<=1;++j)
for(int i=-1;i<=1;++i)
{
int q=countImg.Val(x+i,y+j)-minCount+1;
if(q>0 && fabs(depthImg.Val(x+i,y+j)-curDepth) < depthThr) {
Sum.Val(x,y)+=q*depthImg.Val(x+i,y+j);
cnt+=q;
}
}
if(cnt>0) {
Sum.Val(x,y)/=cnt;
}
else Sum.Val(x,y)=depthImg.Val(x,y);
}
for(int y=1;y<h-1;++y)
for(int x=1;x<w-1;++x)
{
float q=(featureMask.Val(x,y)/255.0);
depthImg.Val(x,y) = depthImg.Val(x,y)*q + Sum.Val(x,y)*(1-q);
}
}
// It generate a feature mask that mark the featureless area of the original photo.
// Featureless areas are usually affected by noise and have to be smoothed more
void EpochModel::GenerateGradientSmoothingMask(int subsampleFactor, QImage &OriginalTexture, CharImage &mask)
{
CharImage gray(OriginalTexture);
CharImage grad;
grad.resize(gray.w,gray.h);
int w=gray.w,h=gray.h;
for(int x=1;x<w-1;++x)
for(int y=1;y<h-1;++y)
{
int dx=abs(int(gray.Val(x,y))-int(gray.Val(x-1,y))) + abs(int(gray.Val(x,y))-int(gray.Val(x+1,y)));
int dy=abs(int(gray.Val(x,y))-int(gray.Val(x,y-1))) + abs(int(gray.Val(x,y))-int(gray.Val(x,y+1)));
grad.Val(x,y)=min(255,16*dx+dy);
}
// create subsampled mask
int ws=gray.w/subsampleFactor, hs=gray.h/subsampleFactor;
mask.resize(ws,hs);
for(int x=0;x<ws;++x)
for(int y=0;y<hs;++y)
{
unsigned char maxGrad=0;
for(int si=0;si<subsampleFactor;++si)
for(int sj=0;sj<subsampleFactor;++sj)
maxGrad = max(maxGrad, grad.Val(x*subsampleFactor+sj,y*subsampleFactor+si));
mask.Val(x,y) = maxGrad;
}
CharImage mask2;
mask2.resize(ws, hs);
// average filter (11 x 11)
int avg;
int wsize = 5;
for (int y = wsize; y < hs-wsize; y++)
for (int x = wsize; x < ws-wsize; x++)
{
avg = 0;
for (int yy = y - wsize; yy <= y + wsize; yy++)
for (int xx = x - wsize; xx <= x + wsize; xx++)
avg += mask.Val(xx, yy);
mask2.Val(x, y) = min(255, avg / ((2 * wsize + 1)* (2 * wsize +1)));
}
mask.convertToQImage().save("tmp_testmask.jpg","jpg");
mask2.convertToQImage().save("tmp_testmaskSmooth.jpg","jpg");
// erosion filter (7 x 7)
int minimum;
wsize = 3;
for (int y = wsize; y < hs-wsize; y++)
for (int x = wsize; x < ws-wsize; x++)
{
minimum = mask2.Val(x, y);
for (int yy = y - wsize; yy <= y + wsize; yy++)
for (int xx = x - wsize; xx <= x + wsize; xx++)
if (mask2.Val(xx, yy) < minimum)
minimum = mask2.Val(xx, yy);
mask.Val(x, y) = minimum;
}
grad.convertToQImage().save("tmp_test.jpg","jpg");
mask.convertToQImage().save("tmp_testmaskeroded.jpg","jpg");
}
/*
Main processing function;
it takes a depth map, a count map,
- resample them to a (width/subsample,height/subsample) image
- leave only the faces that are within a given orientation range
- that have a count greater than minCount.
- and smooth them with a count/quality aware laplacian filter
*/
bool EpochModel::BuildMesh(CMeshO &m, int subsampleFactor, int minCount, float minAngleCos, int smoothSteps,
bool dilation, int dilationPasses, int dilationSize,
bool erosion, int erosionPasses, int erosionSize,float scalingFactor)
{
FloatImage depthImgf;
CharImage countImgc;
int ttt0=clock();
depthImgf.Open(depthName.toAscii());
countImgc.Open(countName.toAscii());
QImage TextureImg;
TextureImg.load(textureName);
int ttt1=clock();
if(logFP) fprintf(logFP,"**** Buildmesh: Opening files %i\n",ttt1-ttt0);
CombineHandMadeMaskAndCount(countImgc,maskName); // set count to zero for all masked points
FloatImage depthSubf; // the subsampled depth image
FloatImage countSubf; // the subsampled quality image (quality == count)
SmartSubSample(subsampleFactor,depthImgf,countImgc,depthSubf,countSubf,minCount);
CharImage FeatureMask; // the subsampled image with (quality == features)
GenerateGradientSmoothingMask(subsampleFactor, TextureImg, FeatureMask);
depthSubf.convertToQImage().save("tmp_depth.jpg", "jpg");
int ttt2=clock();
if(logFP) fprintf(logFP,"**** Buildmesh: SubSample and Gradient %i\n",ttt2-ttt1);
float depthThr = ComputeDepthJumpThr(depthSubf,0.8f);
for(int ii=0;ii<smoothSteps;++ii)
Laplacian2(depthSubf,countSubf,minCount,FeatureMask,depthThr);
int ttt3=clock();
if(logFP) fprintf(logFP,"**** Buildmesh: Smoothing %i\n",ttt3-ttt2);
vcg::tri::Grid<CMeshO>(m,depthSubf.w,depthSubf.h,depthImgf.w,depthImgf.h,&*depthSubf.v.begin());
int ttt4=clock();
if(logFP) fprintf(logFP,"**** Buildmesh: trimesh building %i\n",ttt4-ttt3);
// The depth is filtered and the minimum count mask is update accordingly.
// To be more specific the border of the depth map are identified by erosion
// and the relative vertex removed (by setting mincount equal to 0).
float depthThr2 = ComputeDepthJumpThr(depthSubf,0.95f);
depthFilter(depthSubf, countSubf, depthThr2,
dilation, dilationPasses, dilationSize,
erosion, erosionPasses, erosionSize);
int vn = m.vn;
for(int i=0;i<vn;++i)
if(countSubf.v[i]<minCount)
{
m.vert[i].SetD();
m.vn--;
}
cam.Open(cameraName.toAscii());
CMeshO::VertexIterator vi;
Matrix33d Rinv= Inverse(cam.R);
for(vi=m.vert.begin();vi!=m.vert.end();++vi)if(!(*vi).IsD())
{
Point3f in=(*vi).P();
Point3d out;
cam.DepthTo3DPoint(in[0], in[1], in[2], out);
(*vi).P().Import(out);
QRgb c = TextureImg.pixel(int(in[0]), int(in[1]));
(*vi).C().SetRGB(qRed(c),qGreen(c),qBlue(c));
//(*vi).Q()=chi.Val(in[0], in[1]);
//(*vi).Q()=flisubQ.Val(in[0]/subsample, in[1]/subsample);
if(FeatureMask.Val(int(in[0]/subsampleFactor), int(in[1]/subsampleFactor))<200) (*vi).Q()=0;
else (*vi).Q()=1;
(*vi).Q()=float(FeatureMask.Val(in[0]/subsampleFactor, in[1]/subsampleFactor))/255.0;
}
int ttt5=clock();
if(logFP) fprintf(logFP,"**** Buildmesh: Projecting and Coloring %i\n",ttt5-ttt4);
CMeshO::FaceIterator fi;
Point3f CameraPos=Point3f::Construct(cam.t);
for(fi=m.face.begin();fi!=m.face.end();++fi)
{
if((*fi).V(0)->IsD() ||(*fi).V(1)->IsD() ||(*fi).V(2)->IsD() )
{
(*fi).SetD();
--m.fn;
}
else
{
Point3f n=vcg::Normal(*fi);
n.Normalize();
Point3f dir=CameraPos-vcg::Barycenter(*fi);
dir.Normalize();
if(dir.dot(n) < minAngleCos)
{
(*fi).SetD();
--m.fn;
}
}
}
int ttt6=clock();
if(logFP) fprintf(logFP,"**** Buildmesh: Deleting skewed %i\n",ttt6-ttt5);
// Matrix44d Rot;
// Rot.SetRotate(M_PI,Point3d(1,0,0));
// vcg::tri::UpdatePosition<CMeshO>::Matrix(m, Rot);
Matrix44f scaleMat;
scaleMat.SetScale(scalingFactor,scalingFactor,scalingFactor);
vcg::tri::UpdatePosition<CMeshO>::Matrix(m, scaleMat);
return true;
}
void EpochModel::AddCameraIcon(CMeshO &m)
{
tri::Allocator<CMeshO>::AddVertices(m,3);
m.vert[m.vert.size()-3].P()=Point3f::Construct(cam.t+Point3d(0,0,0));
m.vert[m.vert.size()-3].C()=Color4b::Green;
m.vert[m.vert.size()-2].P()=Point3f::Construct(cam.t+Point3d(0,1,0));
m.vert[m.vert.size()-2].C()=Color4b::Green;
m.vert[m.vert.size()-1].P()=Point3f::Construct(cam.t+Point3d(1,0,0));
m.vert[m.vert.size()-1].C()=Color4b::Green;
tri::Allocator<CMeshO>::AddFaces(m,1);
m.face[m.face.size()-1].V(0)= &m.vert[m.vert.size()-3];
m.face[m.face.size()-1].V(1)= &m.vert[m.vert.size()-2];
m.face[m.face.size()-1].V(2)= &m.vert[m.vert.size()-1];
}
bool EpochModel::Init(QDomNode &node)
{
if(!node.hasAttributes()) return false;
QDomNamedNodeMap attr= node.attributes();
QString indexString = (attr.namedItem("index")).nodeValue() ;
qDebug("reading Model with index %i ",indexString.toInt());
for(QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling())
{
if(n.nodeName() == QString("camera")) cameraName = n.attributes().namedItem("filename").nodeValue();
if(n.nodeName() == QString("texture")) textureName= n.attributes().namedItem("filename").nodeValue();
if(n.nodeName() == QString("depth")) depthName = n.attributes().namedItem("filename").nodeValue();
if(n.nodeName() == QString("count")) countName = n.attributes().namedItem("filename").nodeValue();
}
QString tmpName=textureName.left(textureName.length()-4);
maskName = tmpName.append(".mask.png");
return true;
}
QString EpochModel::ThumbName(QString &_imageName)
{
QString tmpName=_imageName.left(_imageName.length()-4);
return tmpName.append(".thumb.jpg");
}
EpochIO::EpochIO()
{
epochDialog = new v3dImportDialog();
epochDialog->hide();
}
EpochIO::~EpochIO()
{
delete epochDialog;
}
bool EpochIO::open(const QString &formatName, const QString &fileName, MeshModel &m, int& mask,const FilterParameterSet & /*par*/, CallBackPos *cb, QWidget *parent)
{
EpochReconstruction er;
mask = vcg::tri::io::Mask::IOM_VERTCOLOR | vcg::tri::io::Mask::IOM_VERTQUALITY;
// just to be sure...
if (fileName.isEmpty()) return false;
// initializing progress bar status
if (cb != NULL) (*cb)(0, "Loading...");
// this change of dir is needed for subsequent texture/material loading
QString FileNameDir = fileName.left(fileName.lastIndexOf("/"));
QDir::setCurrent(FileNameDir);
QString errorMsgFormat = "Error encountered while loading file %1:\n%2";
string stdfilename = QFile::encodeName(fileName).constData ();
//string filename = fileName.toUtf8().data();
QDomDocument doc;
if(formatName.toUpper() == tr("V3D") && fileName.endsWith(".v3d"))
{
QFile file(fileName);
if (file.open(QIODevice::ReadOnly) && doc.setContent(&file))
{
file.close();
QDomElement root = doc.documentElement();
if (root.nodeName() == tr("reconstruction"))
{
QDomNode nhead = root.firstChildElement("head");
for(QDomNode n = nhead.firstChildElement("meta"); !n.isNull(); n = n.nextSiblingElement("meta"))
{
if(!n.hasAttributes()) return false;
QDomNamedNodeMap attr= n.attributes();
if(attr.contains("name")) er.name = (attr.namedItem("name")).nodeValue() ;
if(attr.contains("author")) er.author = (attr.namedItem("author")).nodeValue() ;
if(attr.contains("created")) er.created = (attr.namedItem("created")).nodeValue() ;
}
for(QDomNode n = root.firstChildElement("model"); !n.isNull(); n = n.nextSiblingElement("model"))
{
EpochModel em;
em.Init(n);
er.modelList.push_back(em);
}
}
}
}
epochDialog->setEpochReconstruction( &er, cb);
do
{
epochDialog->exportToPLY=false;
//Here we invoke the modal dialog and wait for its termination
int continueValue = epochDialog->exec();
// The user has pressed the ok button: now start the real processing:
if(epochDialog->exportToPLY == true) qDebug("Starting the ply exporting process");
int t0=clock();
logFP=fopen("epoch.log","w");
int subSampleVal = epochDialog->subsampleSpinBox->value();
int minCountVal= epochDialog->minCountSpinBox->value();
float maxCCDiagVal= epochDialog->maxCCDiagSpinBox->value();
int mergeResolution=epochDialog->mergeResolutionSpinBox->value();
int smoothSteps=epochDialog->smoothSpinBox->value();
bool closeHole = epochDialog->holeCheckBox->isChecked();
int maxHoleSize = epochDialog->holeSpinBox->value();
if (continueValue == QDialog::Rejected)
{
QMessageBox::warning(parent, "Open V3d format","Aborted");
return false;
}
CMeshO mm;
QTableWidget *qtw=epochDialog->imageTableWidget;
float MinAngleCos=cos(vcg::math::ToRad(epochDialog->qualitySpinBox->value()));
bool clustering=epochDialog->fastMergeCheckBox->isChecked();
bool removeSmallCC=epochDialog->removeSmallCCCheckBox->isChecked();
vcg::tri::Clustering<CMeshO, vcg::tri::AverageColorCell<CMeshO> > Grid;
int selectedNum=0,selectedCount=0;
int i;
for(i=0;i<qtw->rowCount();++i) if(qtw->isItemSelected(qtw->item(i,0))) ++selectedNum;
if(selectedNum==0)
{
QMessageBox::warning(parent, "Open V3d format","No range map selected. Nothing loaded");
return false;
}
bool dilationFlag = epochDialog->dilationCheckBox->isChecked();
int dilationN = epochDialog->dilationNumPassSpinBox->value();
int dilationSz = epochDialog->dilationSizeSlider->value() * 2 + 1;
bool erosionFlag = epochDialog->erosionCheckBox->isChecked();
int erosionN = epochDialog->erosionNumPassSpinBox->value();
int erosionSz = epochDialog->erosionSizeSlider->value() * 2 + 1;
float scalingFactor = epochDialog->scaleLineEdit->text().toFloat();
std::vector<string> savedMeshVector;
bool firstTime=true;
QList<EpochModel>::iterator li;
for(li=er.modelList.begin(), i=0;li!=er.modelList.end();++li,++i)
{
if(qtw->isItemSelected(qtw->item(i,0)))
{
++selectedCount;
mm.Clear();
int tt0=clock();
(*li).BuildMesh(mm,subSampleVal,minCountVal,MinAngleCos,smoothSteps,
dilationFlag, dilationN, dilationSz, erosionFlag, erosionN, erosionSz,scalingFactor);
int tt1=clock();
if(logFP) fprintf(logFP,"** Mesh %i : Build in %i\n",selectedCount,tt1-tt0);
if(epochDialog->exportToPLY)
{
QString plyFilename =(*li).textureName.left((*li).textureName.length()-4);
plyFilename.append(".x.ply");
savedMeshVector.push_back(qPrintable(plyFilename));
int mask= tri::io::Mask::IOM_VERTCOORD + tri::io::Mask::IOM_VERTCOLOR + tri::io::Mask::IOM_VERTQUALITY;
int result = tri::io::ExporterPLY<CMeshO>::Save(mm,qPrintable(plyFilename),mask);
}
else
{
if(clustering)
{
if (firstTime)
{
//Grid.Init(mm.bbox,100000);
vcg::tri::UpdateBounding<CMeshO>::Box(mm);
//Grid.Init(mm.bbox,1000.0*pow(10.0,mergeResolution),mm.bbox.Diag()/1000.0f);
Grid.Init(mm.bbox,100000.0*pow(10.0,mergeResolution));
firstTime=false;
}
Grid.Add(mm);
}
else
tri::Append<CMeshO,CMeshO>::Mesh(m.cm,mm); // append mesh mr to ml
}
int tt2=clock();
if(logFP) fprintf(logFP,"** Mesh %i : Append in %i\n",selectedCount,tt2-tt1);
}
if (cb)(*cb)(selectedCount*90/selectedNum, "Building meshes");
}
if (cb != NULL) (*cb)(90, "Final Processing: clustering");
if(clustering)
{
Grid.Extract(m.cm);
}
if(epochDialog->exportToPLY)
{
QString ALNfilename = fileName.left(fileName.length()-4).append(".aln");
ALNParser::SaveALN(qPrintable(ALNfilename), savedMeshVector);
}
int t1=clock();
if(logFP) fprintf(logFP,"Extracted %i meshes in %i\n",selectedCount,t1-t0);
if (cb != NULL) (*cb)(95, "Final Processing: Removing Small Connected Components");
if(removeSmallCC)
{
vcg::tri::UpdateBounding<CMeshO>::Box(m.cm); // updates bounding box
m.updateDataMask(MeshModel::MM_FACEFACETOPO | MeshModel::MM_FACEFLAGBORDER | MeshModel::MM_FACEMARK);
RemoveSmallConnectedComponentsDiameter<CMeshO>(m.cm,m.cm.bbox.Diag()*maxCCDiagVal/100.0);
}
int t2=clock();
if(logFP) fprintf(logFP,"Topology and removed CC in %i\n",t2-t1);
vcg::tri::UpdateBounding<CMeshO>::Box(m.cm); // updates bounding box
if (cb != NULL) (*cb)(97, "Final Processing: Closing Holes");
if(closeHole)
{
m.updateDataMask(MeshModel::MM_FACEFACETOPO | MeshModel::MM_FACEFLAGBORDER | MeshModel::MM_FACEMARK);
tri::UpdateNormals<CMeshO>::PerVertexNormalizedPerFace(m.cm);
vcg::tri::Hole<CMeshO>::EarCuttingFill<vcg::tri::MinimumWeightEar< CMeshO> >(m.cm,maxHoleSize,false);
}
if (cb != NULL) (*cb)(100, "Done");
// vcg::tri::UpdateNormals<CMeshO>::PerVertex(m.cm); // updates normals
int t3=clock();
if(logFP) fprintf(logFP,"---------- Total Processing Time%i\n\n\n",t3-t0);
if(logFP) fclose(logFP);
logFP=0;
} while(epochDialog->exportToPLY);
return true;
}
bool EpochIO::save(const QString &/*formatName*/,const QString &/*fileName*/, MeshModel &/*m*/, const int /*mask*/, const FilterParameterSet &, vcg::CallBackPos * /*cb*/, QWidget *parent)
{
QMessageBox::warning(parent, "Unknown type", "file's extension not supported!!!");
return false;
}
QList<MeshIOInterface::Format> EpochIO::importFormats() const
{
QList<Format> formatList;
formatList << Format("Epoch Reconstructed mesh","V3D");
return formatList;
};
QIcon *EpochModel::getIcon()
{
QString iconName(textureName);
iconName+=QString(".xbm");
QIcon *ico=new QIcon();
return ico;
}
Q_EXPORT_PLUGIN(EpochIO)

View File

@ -0,0 +1,74 @@
/****************************************************************************
* MeshLab o o *
* A versatile mesh processing toolbox o o *
* _ O _ *
* Copyright(C) 2005 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/****************************************************************************
History
$Log$
Revision 1.4 2007/11/26 07:35:26 cignoni
Yet another small cosmetic change to the interface of the io filters.
Revision 1.3 2007/11/25 09:48:39 cignoni
Changed the interface of the io filters. Now also a default bit set for the capabilities has to specified
Revision 1.2 2006/11/29 00:59:16 cignoni
Cleaned plugins interface; changed useless help class into a plain string
Revision 1.1 2006/11/07 18:14:21 cignoni
Moved from the epoch svn repository
Revision 1.1 2006/01/20 13:03:27 cignoni
*** empty log message ***
*****************************************************************************/
#ifndef EXTRAIOPLUGINV3D_H
#define EXTRAIOPLUGINV3D_H
#include <QObject>
#include <QStringList>
#include <QString>
#include <meshlab/meshmodel.h>
#include <meshlab/interfaces.h>
#include "v3dImportDialog.h"
class EpochIO : public QObject, public MeshIOInterface
{
Q_OBJECT
Q_INTERFACES(MeshIOInterface)
public:
QList<Format> importFormats() const;
QList<Format> exportFormats() const {return QList<Format>();};
EpochIO();
~EpochIO();
v3dImportDialog *epochDialog;
QString lastFileName;
void GetExportMaskCapability(QString &, int &, int &) const {assert(0); return ;}
bool open(const QString &formatName, const QString &fileName, MeshModel &m, int& mask,const FilterParameterSet & par, vcg::CallBackPos *cb=0, QWidget *parent=0);
bool save(const QString &formatName, const QString &fileName, MeshModel &m, const int mask, const FilterParameterSet &, vcg::CallBackPos *cb=0, QWidget *parent= 0);
};
#endif

View File

@ -0,0 +1,50 @@
#ifndef _EPOCH_RECONSTRUCTION_H
#define _EPOCH_RECONSTRUCTION_H
#include <QtXml/QDomDocument>
#include <QtXml/QDomElement>
#include <QtXml/QDomNode>
#include <meshlab/meshmodel.h>
#include "radial_distortion.h"
#include "epoch_camera.h"
#include "scalar_image.h"
class EpochModel
{
public:
int index;
QString cameraName;
QString maskName;
QString depthName;
QString textureName;
QString countName;
vcg::EpochCamera cam;
bool Init(QDomNode &node);
static QString ThumbName(QString &imageName);
bool BuildMesh(CMeshO &m, int subsampleFactor, int minCount, float minAngleCos, int smoothSteps,
bool dilation, int dilationPasses, int dilationSize, bool erosion, int erosionPasses, int erosionSize,float scalingFactor);
void SmartSubSample(int subsampleFactor, FloatImage &fli, CharImage &chi, FloatImage &subD,FloatImage &subQ, int minCount);
void AddCameraIcon(CMeshO &m);
bool CombineHandMadeMaskAndCount(CharImage &qualityImg, QString maskName );
void GenerateCountImage();
void GenerateGradientSmoothingMask(int subsampleFactor, QImage &OriginalTexture, CharImage &mask);
void Laplacian2(FloatImage &depth, FloatImage &Q, int minCount, CharImage &mask, float depthThr);
float ComputeDepthJumpThr(FloatImage &depthImgf, float percentile);
void depthFilter(FloatImage &depthImgf, FloatImage &countImgf, float depthJumpThr,
bool dilation, int dilationNumPasses, int dilationWinsize, bool erosion, int erosionNumPasses, int erosionWinsize);
QIcon *getIcon();
};
class EpochReconstruction
{
public:
QString name, author, created;
QList<EpochModel> modelList;
};
#endif

View File

@ -0,0 +1,104 @@
#include "fillImage.h"
#include <cmath>
#include <limits>
namespace ui
{
fillImage::fillImage()
{
}
fillImage::~fillImage()
{
}
void fillImage::Compute(const QImage& input, int x, int y, int threshold_gradient, int threshold_fixed, QImage& output)
{
threshold_gradient_ = threshold_gradient;
threshold_fixed_ = threshold_fixed;
input_ = input;
W = input.width();
H = input.height();
xo = x;
yo = y;
output = QImage(W, H, QImage::Format_Mono);
computed_ = QImage(W, H, QImage::Format_Mono);
output.fill(0);
computed_.fill(0);
ComputeGradient(input, gradient_);
pixels_to_do_.push_back(std::make_pair(x,y));
while(!pixels_to_do_.empty())
{
DealWithPixel(pixels_to_do_.front(),output);
pixels_to_do_.pop_front();
}
}
bool fillImage::ShouldWeCompute(int x, int y)
{
if (input_.isGrayscale())
return (gradient_.get(x,y) < threshold_gradient_ && computed_.pixelIndex(x,y) == 0 && std::abs(qGray(input_.pixel(x,y)) - qGray(input_.pixel(xo,yo))) < threshold_fixed_);
else
return (gradient_.get(x,y) < threshold_gradient_ && computed_.pixelIndex(x,y) == 0 && std::abs(qRed(input_.pixel(x,y)) - qRed(input_.pixel(xo,yo))) < threshold_fixed_ && std::abs(qGreen(input_.pixel(x,y)) - qGreen(input_.pixel(xo,yo))) < threshold_fixed_ && std::abs(qBlue(input_.pixel(x,y)) - qBlue(input_.pixel(xo,yo))) < threshold_fixed_);
}
void fillImage::DealWithPixel(const std::pair<int,int>& xy, QImage& output)
{
int x = xy.first;
int y = xy.second;
if (computed_.pixelIndex(x,y) == 1)
return;
output.setPixel(x,y,1);
computed_.setPixel(x,y,1);
if (x>0 && ShouldWeCompute(x-1,y))
pixels_to_do_.push_back(std::make_pair(x-1,y));
if (x<W-1 && ShouldWeCompute(x+1, y))
pixels_to_do_.push_back(std::make_pair(x+1,y));
if (y>0 && ShouldWeCompute(x, y-1))
pixels_to_do_.push_back(std::make_pair(x,y-1));
if (y<H-1 && ShouldWeCompute(x, y+1))
pixels_to_do_.push_back(std::make_pair(x,y+1));
}
void fillImage::ComputeGradient(const QImage& input, myGSImage& output)
{
input.save("input.jpg", "jpg");
size_t W = input.width();
size_t H = input.height();
float* temp = new float[W*H];
for (size_t i=0; i<W*H; ++i)
temp[i] = 0.0;
float dx, dy;
float min = std::numeric_limits<float>::max();
float max = -std::numeric_limits<float>::max();
for (size_t i=1; i<W; ++i)
for (size_t j=1; j<H; ++j)
{
dx = qGray(input.pixel(i,j)) - qGray(input.pixel(i-1,j));
dy = qGray(input.pixel(i,j)) - qGray(input.pixel(i,j-1));
dx = std::sqrt(dx*dx + dy*dy);
temp[j*W+i] = dx;
if (dx > max)
max = dx;
if (dx < min)
min = dx;
}
output = myGSImage(W, H);
float range = 255./(max-min);
for (size_t i=0; i<W; ++i)
for (size_t j=0; j<H; ++j)
output.put(i, j, (unsigned char)((temp[j*W+i]-min)*range));
}
}

View File

@ -0,0 +1,94 @@
#ifndef __IO_FILL_AREA_INC_
#define __IO_FILL_AREA_INC_
#include <deque>
#include <algorithm>
#include <QtGui/QImage>
namespace ui
{
struct myGSImage
{
unsigned char* data;
size_t w, h;
myGSImage()
{
data = 0;
}
myGSImage(const myGSImage& image)
{
w = image.w;
h = image.h;
if (data)
delete [] data;
data = new unsigned char[w*h];
memcpy(data, image.data, w*h);
}
myGSImage & operator = (const myGSImage& image)
{
w = image.w;
h = image.h;
if (data)
delete [] data;
data = new unsigned char[w*h];
memcpy(data, image.data, w*h);
return *this;
}
myGSImage(size_t width, size_t height): w(width), h(height)
{
data = new unsigned char[width*height];
};
~myGSImage()
{
if (data)
delete [] data;
}
unsigned char get(size_t i, size_t j) const
{
return data[j*w + i];
}
void put(size_t i, size_t j, unsigned char a)
{
data[j*w + i] = a;
}
void fill(unsigned char a)
{
for (size_t i=0; i<w; ++i)
for (size_t j=0; j<h; ++j)
put(i,j,a);
}
};
class fillImage
{
public:
fillImage();
virtual ~fillImage();
void Compute(const QImage& input, int x, int y, int threshold_gradient,
int threshold_fixed, QImage& output);
protected:
bool ShouldWeCompute(int x, int y);
void ComputeGradient(const QImage&, myGSImage& output);
void DealWithPixel(const std::pair<int,int>&, QImage& output);
QImage input_, computed_;
myGSImage gradient_;
int threshold_gradient_, threshold_fixed_;
int W,H;
int xo, yo;
std::deque<std::pair<int,int> > pixels_to_do_;
};
}
#endif

View File

@ -0,0 +1,310 @@
#include "maskImageWidget.h"
#include "maskRenderWidget.h"
#include "fillImage.h"
#include <QtGui/QPen>
#include <QtGui/QBrush>
#include <QtGui/QPolygon>
#include <QtGui/QPixmap>
#include <QtGui/QImage>
#include <QtGui/QPainter>
#include <QtGui/QPalette>
#include <QtGui/QMouseEvent>
#include <QtGui/QPaintEvent>
#include <QtGui/QApplication>
#include <QtGui/QMessageBox>
#include <QtGui/QAction>
#include <QtGui/QVBoxLayout>
#include <QtGui/QToolBar>
#include <QtGui/QSpinBox>
#include <QtGui/QFileDialog>
#include <QtGui/QDesktopWidget>
#include <stack>
#include <cmath>
#include <sstream>
#include <cassert>
#include <iostream>
#ifdef WIN32
#undef min
#undef max
#endif
namespace ui
{
struct maskImageWidget::Impl
{
enum DrawMode { Pen, Eraser } mode_;
maskRenderWidget *render_area_;
int threshold_gradient_, threshold_fixed_;
int realwidth_, realheight_;
Impl();
};
maskImageWidget::Impl::Impl()
{
mode_ = Pen;
threshold_gradient_ = 100;
threshold_fixed_ = 30;
}
maskImageWidget::maskImageWidget(const QImage& image, QWidget *parent) : QDialog(parent), pimpl_(new Impl)
{
init(image);
}
maskImageWidget::~maskImageWidget() throw()
{
delete pimpl_;
}
QImage maskImageWidget::getMask() const
{
return pimpl_->render_area_->getMask(pimpl_->realwidth_, pimpl_->realheight_);
}
void maskImageWidget::loadMask(const QString& filename)
{
pimpl_->render_area_->load(filename);
}
void maskImageWidget::init(const QImage& image)
{
setWindowTitle(tr("Mask Editor"));
QPixmap load("coral_open32x32.png");
QPixmap save("coral_save32x32.png");
QPixmap undo("coral_undo32x32.png");
QPixmap redo("coral_redo32x32.png");
QPixmap pen("coral_pencil32x32.png");
QPixmap eraser("coral_eraser32x32.png");
QAction *canvasloadmask = new QAction(this);
canvasloadmask->setIcon(load);
canvasloadmask->setText(tr("&Load Mask"));
QAction *canvassavemask = new QAction(this);
canvassavemask->setIcon(QIcon(save));
canvassavemask->setText(tr("&Save Mask"));
QAction *canvasundo = new QAction(this);
canvasundo->setIcon(QIcon(undo));
canvasundo->setText(tr("&Undo"));
canvasundo->setShortcut(QKeySequence("Ctrl+Z"));
QAction *canvasredo = new QAction(this);
canvasredo->setIcon(QIcon(redo));
canvasredo->setText(tr("&Redo"));
canvasredo->setShortcut(QKeySequence("Ctrl+Shift+Z"));
QAction *canvasclear = new QAction(tr("&Clear"), this);
canvasclear->setShortcut(QKeySequence("Ctrl+C"));
QAction *canvaspen = new QAction(this);
canvaspen->setIcon(QIcon(pen));
canvaspen->setText(tr("&Pen"));
QAction *canvaseraser = new QAction(this);
canvaseraser->setIcon(QIcon(eraser));
canvaseraser->setText(tr("&Eraser"));
QActionGroup *actions(new QActionGroup(this));
actions->addAction(canvaspen);
actions->addAction(canvaseraser);
canvaspen->setCheckable(true);
canvaseraser->setCheckable(true);
canvaspen->setChecked(true);
actions->setExclusive(true);
QAction *canvasOK = new QAction(this);
canvasOK->setText("OK");
QAction *canvasCancel = new QAction(this);
canvasCancel->setText("Cancel");
QBoxLayout *layout(new QVBoxLayout(this));
// We don't want a real-size image. We will downscale it!
QImage image_to_use = image;
pimpl_->realwidth_ = image.width();
pimpl_->realheight_ = image.height();
qDebug("maskImageWidget::Init real wxh %i x%i",pimpl_->realwidth_,pimpl_->realheight_);
QDesktopWidget *desktop(QApplication::desktop());
if (image.width() > (desktop->width() * .8) ||
image.height() > (desktop->height() * .8))
{
int width(desktop->width()), height(desktop->height());
image_to_use = image.scaled((int)std::floor(width * .75),
(int)std::floor(height * .75), Qt::KeepAspectRatio);
}
pimpl_->render_area_ = new maskRenderWidget(image_to_use, this);
QToolBar *canvas_toolbar(new QToolBar(this));
canvas_toolbar->addSeparator();
canvas_toolbar->addAction(canvasloadmask);
canvas_toolbar->addAction(canvassavemask);
canvas_toolbar->addSeparator();
canvas_toolbar->addAction(canvasundo);
canvas_toolbar->addAction(canvasredo);
canvas_toolbar->addSeparator();
QSpinBox *pen_width(new QSpinBox(canvas_toolbar));
pen_width->setToolTip(tr("Pen Width"));
pen_width->setRange(0, 80);
pen_width->setSingleStep(2);
pen_width->setValue(16);
connect(pen_width, SIGNAL(valueChanged(int)), SLOT(setCanvasPenWidth(int)));
canvas_toolbar->addWidget(pen_width);
canvas_toolbar->addAction(canvaspen);
canvas_toolbar->addAction(canvaseraser);
canvas_toolbar->addSeparator();
QSpinBox *gradient(new QSpinBox(canvas_toolbar));
gradient->setToolTip("Gradient Threshold");
gradient->setRange(0, 255);
gradient->setValue(pimpl_->threshold_gradient_);
connect(gradient, SIGNAL(valueChanged(int)), SLOT(setGradientThreshold(int)));
QSpinBox *fixed(new QSpinBox(canvas_toolbar));
fixed->setToolTip("Fixed Threshold");
fixed->setRange(0, 255);
fixed->setValue(pimpl_->threshold_fixed_);
connect(fixed, SIGNAL(valueChanged(int)), SLOT(setFixedThreshold(int)));
canvas_toolbar->addWidget(gradient);
canvas_toolbar->addWidget(fixed);
canvas_toolbar->addSeparator();
canvas_toolbar->addAction(canvasOK);
canvas_toolbar->addAction(canvasCancel);
layout->addWidget(canvas_toolbar);
layout->addWidget(pimpl_->render_area_);
layout->setSizeConstraint(QLayout::SetFixedSize);
connect(canvasloadmask, SIGNAL(activated()), SLOT(loadMask()));
connect(canvassavemask, SIGNAL(activated()), SLOT(saveMask()));
connect(canvasundo, SIGNAL(activated()), pimpl_->render_area_, SLOT(undo()));
connect(canvasredo, SIGNAL(activated()), pimpl_->render_area_, SLOT(redo()));
connect(canvasclear, SIGNAL(activated()), pimpl_->render_area_, SLOT(clear()));
connect(canvaspen, SIGNAL(activated()), SLOT(setCanvasPen()));
connect(canvaseraser, SIGNAL(activated()), SLOT(setCanvasEraser()));
connect(pimpl_->render_area_, SIGNAL(pointSelected(const QPoint &)), SLOT(automaticMask(const QPoint &)));
connect(canvasOK, SIGNAL(activated()), SLOT(accept()));
connect(canvasCancel, SIGNAL(activated()), SLOT(reject()));
}
void maskImageWidget::setCanvasPenWidth(int width)
{
QPen pen(pimpl_->render_area_->pen());
pen.setWidth(width);
pimpl_->render_area_->setPen(pen);
}
void maskImageWidget::setCanvasPen()
{
QPen pen(pimpl_->render_area_->pen());
pen.setColor(QColor(Qt::black));
pen.setJoinStyle(Qt::RoundJoin);
pimpl_->render_area_->setPen(pen);
}
void maskImageWidget::setCanvasEraser()
{
QPen pen(pimpl_->render_area_->pen());
pen.setColor(QColor(Qt::transparent));
pen.setJoinStyle(Qt::RoundJoin);
pimpl_->render_area_->setPen(pen);
}
void maskImageWidget::setGradientThreshold(int threshold_gradient)
{
pimpl_->threshold_gradient_ = threshold_gradient;
}
void maskImageWidget::setFixedThreshold(int threshold_fixed)
{
pimpl_->threshold_fixed_ = threshold_fixed;
}
void maskImageWidget::loadMask()
{
try
{
QString filename(QFileDialog::getOpenFileName(this, QString("Open mask file"), QString(), QString("*.png")));
if (QString::null != filename)
pimpl_->render_area_->load(filename);
}
catch (std::exception &e)
{
QMessageBox::warning(this, tr("Problem"), e.what());
}
}
namespace
{
bool check_extension(QString &filename, const QString &ext)
{
bool ret(false);
if (ext != filename.section('.', -1))
{
int index(filename.lastIndexOf('.'));
if (-1 == index)
{
filename += '.';
index += filename.size();
}
filename.replace(index + 1, ext.size(), ext);
filename.resize(index + 1 + ext.size());
ret = true;
}
return ret;
}
};
void maskImageWidget::saveMask()
{
try
{
QString filename(QFileDialog::getSaveFileName(this, QString("Save mask file"), QString(), QString("*.png")));
if (QString::null != filename)
{
check_extension(filename, QString("png"));
pimpl_->render_area_->save(filename, pimpl_->realwidth_, pimpl_->realheight_);
}
}
catch (std::exception &e)
{
QMessageBox::warning(this, tr("Epoch 3D Webservice"), e.what());
}
}
void maskImageWidget::automaticMask(const QPoint &p)
{
QImage image = (pimpl_->render_area_->palette().base().texture()).toImage();
QImage out;
fillImage fi;
fi.Compute(image, p.x(), p.y(), pimpl_->threshold_gradient_, pimpl_->threshold_fixed_, out);
const size_t width(image.width()), height(image.height());
QImage temp(pimpl_->render_area_->alphaMask());
for (size_t i = 0; i < width; ++i)
{
for (size_t j = 0; j < height; ++j)
{
if (out.pixelIndex(i, j) > 0)
temp.setPixel(i, j, QColor(Qt::black).rgba());
}
}
//temp.save("temp.jpg","jpg");
pimpl_->render_area_->setAlphaMask(temp);
}
};

View File

@ -0,0 +1,53 @@
#ifndef __IO_MASK_IMAGE_WIDGET_INC__
#define __IO_MASK_IMAGE_WIDGET_INC__
#include <QtGui/QDialog>
namespace ui
{
/*! \class maskImageWidget
\brief A brief description
\author Maarten Vergauwen
*/
class maskImageWidget : public QDialog
{
Q_OBJECT
struct Impl;
Impl* pimpl_;
void init (const QImage &);
public:
/*! \brief Constructor
\param The image.
*/
explicit maskImageWidget(const QImage &, QWidget *parent = 0);
;
/*! \brief Destructor
*/
virtual ~maskImageWidget() throw();
QImage getMask() const;
public slots:
void loadMask(const QString& filename);
private slots:
void setCanvasPenWidth(int);
void setCanvasPen();
void setCanvasEraser();
void setGradientThreshold(int);
void setFixedThreshold(int);
void automaticMask(const QPoint &);
void loadMask();
void saveMask();
};
};
#endif

View File

@ -0,0 +1,362 @@
#include "maskRenderWidget.h"
#include <QtGui/QPen>
#include <QtGui/QBrush>
#include <QtGui/QPolygon>
#include <QtGui/QPixmap>
#include <QtGui/QImage>
#include <QtGui/QPainter>
#include <QtGui/QPalette>
#include <QtGui/QMouseEvent>
#include <QtGui/QPaintEvent>
#include <QtGui/QApplication>
#include <QtGui/QMessageBox>
#include <stack>
#include <cmath>
#include <sstream>
#include <cassert>
#include <iostream>
#ifdef WIN32
#undef min
#undef max
#endif
namespace ui
{
namespace priv
{
template <class T>
inline void unwind(std::stack<T> &stack)
{
while (!stack.empty())
stack.pop();
}
};
struct maskRenderWidget::Impl
{
enum Shape { Nothing, Polyline, Rect, Rubber, Point } shape_;
QPen pen_;
QPolygon polygon_;
QPoint start_point_, last_point_, end_point_;
QRect rubber_band_;
QImage foreground_, band_buffer_;
std::stack<QImage> undo_, redo_;
Impl();
void paintOnDevice(QPaintDevice *);
};
maskRenderWidget::Impl::Impl() : pen_(Qt::black)
{
shape_ = Nothing;
pen_.setWidth(16);
pen_.setCapStyle(Qt::RoundCap);
}
void maskRenderWidget::Impl::paintOnDevice(QPaintDevice *device)
{
assert(device);
QPainter painter(device);
painter.setCompositionMode(QPainter::CompositionMode_Source);
switch (shape_)
{
case Impl::Polyline:
{
painter.setPen(pen_);
painter.drawPolyline(polygon_);
}
break;
case Impl::Point:
{
painter.setPen(pen_);
QPoint p2(end_point_.x() + 1, end_point_.y() + 1);
painter.drawLine(end_point_, p2);
}
break;
case Impl::Rect:
{
QPen pen;
pen.setColor(pen_.color());
painter.setPen(pen);
const int x(rubber_band_.x()), y(rubber_band_.y());
const int w(rubber_band_.width()), h(rubber_band_.height());
for (int i = 0; i < w; ++i)
for (int j = 0; j < h; ++j)
painter.drawPoint(QPoint(x + i, y + j));
rubber_band_ = QRect(0, 0, 0, 0);
}
break;
case Impl::Rubber:
{
QPen pen(Qt::gray);
pen.setWidth(1);
painter.setPen(pen);
painter.drawRect(rubber_band_);
}
break;
default:
break;
}
}
maskRenderWidget::maskRenderWidget(QWidget *parent) : QWidget(parent), pimpl_(new Impl)
{
setAttribute(Qt::WA_StaticContents);
setBackgroundRole(QPalette::Base);
QImage image(640, 480, QImage::Format_ARGB32);
image.fill(Qt::white);
setImage(image);
setFocusPolicy(Qt::StrongFocus);
}
maskRenderWidget::maskRenderWidget(const QImage& image, QWidget *parent) : QWidget(parent), pimpl_(new Impl)
{
qDebug("MaskRenderWidget started with an image %i x %i",image.width(),image.height());
setAttribute(Qt::WA_StaticContents);
setBackgroundRole(QPalette::Base);
setImage(image);
setFocusPolicy(Qt::StrongFocus);
}
maskRenderWidget::~maskRenderWidget() throw()
{
delete pimpl_;
}
void maskRenderWidget::keyPressEvent(QKeyEvent *e)
{
if (e->key() == Qt::Key_Z && (e->modifiers() & Qt::ControlModifier))
{
undo();
}
}
void maskRenderWidget::mousePressEvent(QMouseEvent *e)
{
if (e->button() == Qt::LeftButton)
{
if (e->modifiers() & Qt::ShiftModifier)
{
emit pointSelected(e->pos());
}
else
{
pimpl_->undo_.push(pimpl_->foreground_);
pimpl_->end_point_ = e->pos();
pimpl_->polygon_ = QPolygon();
pimpl_->polygon_ << e->pos();
priv::unwind(pimpl_->redo_);
pimpl_->shape_ = Impl::Point;
update();
}
}
else if (e->button() == Qt::RightButton)
{
pimpl_->undo_.push(pimpl_->foreground_);
QApplication::setOverrideCursor(QCursor(Qt::CrossCursor));
pimpl_->start_point_ = e->pos();
pimpl_->shape_ = Impl::Rubber;
}
}
void maskRenderWidget::mouseMoveEvent(QMouseEvent *e)
{
if (Impl::Rubber == pimpl_->shape_)
{
pimpl_->band_buffer_ = pimpl_->foreground_;
int x(std::min(e->pos().x(), pimpl_->start_point_.x()));
int y(std::min(e->pos().y(), pimpl_->start_point_.y()));
int w(std::abs((float)e->pos().x() - pimpl_->start_point_.x()));
int h(std::abs((float)e->pos().y() - pimpl_->start_point_.y()));
pimpl_->rubber_band_ = QRect(x, y, w, h);
update();
}
else if (Impl::Point == pimpl_->shape_)
{
pimpl_->shape_ = Impl::Polyline;
}
else if (Impl::Polyline == pimpl_->shape_)
{
pimpl_->last_point_ = pimpl_->end_point_;
pimpl_->end_point_ = e->pos();
pimpl_->polygon_ << e->pos();
update();
}
}
void maskRenderWidget::mouseReleaseEvent(QMouseEvent *e)
{
if (Impl::Rubber == pimpl_->shape_)
{
QApplication::restoreOverrideCursor();
pimpl_->shape_ = Impl::Rect;
update();
return;
}
else if (Impl::Polyline == pimpl_->shape_)
{
pimpl_->last_point_ = pimpl_->end_point_;
pimpl_->end_point_ = e->pos();
update();
}
pimpl_->shape_ = Impl::Nothing;
}
void maskRenderWidget::paintEvent(QPaintEvent *e)
{
QImage * device = &pimpl_->foreground_;
if (Impl::Rubber == pimpl_->shape_)
device = &pimpl_->band_buffer_;
pimpl_->paintOnDevice(device);
QPainter painter(this);
QVector<QRect> rects(e->region().rects());
for (int i = 0; i < rects.count(); ++i)
{
QRect r = rects[i];
painter.drawImage(r, *device, r);
}
}
QSize maskRenderWidget::sizeHint() const
{
return minimumSizeHint();
}
QSize maskRenderWidget::minimumSizeHint() const
{
return pimpl_->foreground_.isNull()? QSize(400, 400) : pimpl_->foreground_.size();
}
void maskRenderWidget::setPen(const QPen &pen)
{
pimpl_->pen_ = pen;
}
QPen maskRenderWidget::pen() const
{
return pimpl_->pen_;
}
void maskRenderWidget::setImage(const QImage &image)
{
QPalette palette;
#if (QT_VERSION >= 0x040100)
setAutoFillBackground(true);
#endif
palette.setBrush(backgroundRole(), QBrush(QPixmap::fromImage(image)));
setPalette(palette);
pimpl_->foreground_ = image;
QImage alpha(image.width(), image.height(),QImage::Format_Mono);
alpha.fill(0);
pimpl_->foreground_.setAlphaChannel(alpha);
priv::unwind(pimpl_->undo_);
priv::unwind(pimpl_->redo_);
update();
}
void maskRenderWidget::load(const QString &filename)
{
QImage alpha(filename);
// I would have liked to use KeepAspectRatio but if someone loads a
// bogus mask with a different ratio, the rest will crash. The output
// is now undefined but it won't crash.
alpha = alpha.scaled(pimpl_->foreground_.width(), pimpl_->foreground_.height(), Qt::IgnoreAspectRatio);
QImage temp(pimpl_->foreground_);
const int width(temp.width()), height(temp.height());
for (int i = 0; i < width; ++i)
for (int j = 0; j < height; ++j)
{
QRgb rgb = temp.pixel(i, j);
temp.setPixel(i, j, QColor(qRed(rgb), qGreen(rgb), qBlue(rgb), qGray(alpha.pixel(i, j))).rgba());
}
setAlphaMask(temp);
}
void maskRenderWidget::save(const QString &filename, int w, int h)
{
pimpl_->foreground_.alphaChannel().scaled(w, h, Qt::KeepAspectRatio).save(filename, "PGM");
}
QImage maskRenderWidget::getMask(int w, int h) const
{
// return pimpl_->foreground_.alphaChannel().scaled(w, h, Qt::KeepAspectRatio);
return pimpl_->foreground_.alphaChannel().scaled(w, h); // changed to this becouse sometimes for rounding error did not create the original size.
}
void maskRenderWidget::setAlphaMask(const QImage &image)
{
pimpl_->undo_.push(pimpl_->foreground_);
pimpl_->foreground_ = image;
update();
}
QImage maskRenderWidget::alphaMask() const
{
return pimpl_->foreground_;
}
void maskRenderWidget::undo()
{
if (!pimpl_->undo_.empty())
{
pimpl_->redo_.push(pimpl_->foreground_);
pimpl_->foreground_ = pimpl_->undo_.top();
pimpl_->undo_.pop();
update();
}
}
void maskRenderWidget::redo()
{
if (!pimpl_->redo_.empty())
{
pimpl_->undo_.push(pimpl_->foreground_);
pimpl_->foreground_ = pimpl_->redo_.top();
pimpl_->redo_.pop();
update();
}
}
void maskRenderWidget::clear()
{
pimpl_->undo_.push(pimpl_->foreground_);
priv::unwind(pimpl_->redo_);
pimpl_->foreground_.fill(QColor(Qt::transparent).rgba());
update();
}
};

View File

@ -0,0 +1,102 @@
#ifndef __IO_MASK_RENDER_WIDGET_INC__
#define __IO_MASK_RENDER_WIDGET_INC__
#include <QtGui/QWidget>
namespace ui
{
/*! \class maskRenderWidget
\brief A brief description
\author gmatthew
*/
class maskRenderWidget : public QWidget
{
Q_OBJECT
struct Impl;
Impl* pimpl_;
virtual void keyPressEvent(QKeyEvent *);
virtual void mousePressEvent(QMouseEvent *);
virtual void mouseMoveEvent(QMouseEvent *);
virtual void mouseReleaseEvent(QMouseEvent *);
virtual void paintEvent(QPaintEvent *);
public:
/*! \brief Constructor
*/
explicit maskRenderWidget(QWidget *parent = 0);
/*! \brief Constructor
*/
explicit maskRenderWidget(const QImage &, QWidget *parent = 0);
/*! \brief Destructor
*/
virtual ~maskRenderWidget() throw();
#ifndef DOXYGEN_SHOULD_SKIP_THIS
virtual QSize sizeHint() const;
virtual QSize minimumSizeHint() const;
#endif
/*! \brief Set the drawable pen.
\param The pen object.
*/
void setPen(const QPen &pen);
/*! \brief Returns the drawable pen.
\return pen The pen object.
*/
QPen pen() const;
/*! \brief Set the background Image.
\param image The image to be set as the background.
*/
void setImage(const QImage &);
/*! \brief Load the alpha mask.
\param filename The path to the image to be used as the mask.
*/
void load(const QString &filename);
/*! \brief Save the alpha mask.
\param filename The path to the image to be used as the mask.
\param w The width of the image to save to.
\param h The height of the image to save to.
*/
void save(const QString &filename, int w, int h);
/*! \brief Get the alpha mask.
\param w The width of the image to return.
\param h The height of the image to return.
*/
QImage getMask(int w, int h) const;
/*! \brief Set the alpha mask.
\param image The image to be set as the mask.
*/
void setAlphaMask(const QImage &image);
/*! \brief Returns the alpha mask.
\return An qimage object.
*/
QImage alphaMask() const;
public slots:
/*! \brief Undoes the last action and adds the current action to the redo stack and updates the display. If no more actions could be undone, does nothing.
The number of times this can be done is limited only by the resources available.
*/
void undo();
/*! \brief Redoes the last action and adds the current action to the undo stack and updates the display. If no more actions could be redone, does nothing.
The number of times this can be done is limited only by the resources available.
*/
void redo();
/*! \brief Clears the display.
This action is also added to the undo actions.
*/
void clear();
signals:
void pointSelected(const QPoint &);
};
};
#endif

View File

@ -0,0 +1,86 @@
#include "radial_distortion.h"
#include <cmath>
using namespace std;
void RadialDistortion::SetParameters(vector<double>& k, double max, int resolution)
{
k_ = k;
max_ = max;
resolution_ = resolution;
SetupLookupTable(max_, resolution_);
}
void RadialDistortion::forward_map(double x1, double y1, double* x2, double* y2) const
{
ComputeNewXY(x1-ocx_, y1-ocy_, *x2, *y2);
*x2 += ncx_;
*y2 += ncy_;
}
void RadialDistortion::inverse_map(double x2, double y2, double* x1, double* y1) const
{
ComputeOldXY(x2-ncx_, y2-ncy_, *x1, *y1);
*x1 += ocx_;
*y1 += ocy_;
}
//----------------------------------------------------------------------------
// Relative w.r.t. center !
void RadialDistortion::ComputeNewXY(double xo, double yo, double& xn, double& yn) const
{
double r = (xo*xo) + (yo*yo); // r is r squared
double f = 1.0;
for (int i = 0; i < static_cast<int>(k_.size()); i++) f += (k_[i] * pow(r, i+1));
xn = f*xo;
yn = f*yo;
}
void RadialDistortion::ComputeOldXY(double xn, double yn, double& xo, double& yo) const
{
double rn = sqrt((xn*xn) + (yn*yn)); //// r is r squared
map<double, double>::const_iterator next = lookup_.upper_bound(rn);
map<double, double>::const_iterator prev = next; prev--;
// The compiler will optimise this.
double a = (*prev).first;
double b = (*next).first;
double c = rn;
double d = (*prev).second;
double e = (*next).second;
double f = ((e-d)/(b-a))*(c-a) + d;
xo = f*xn;
yo = f*yn;
}
void RadialDistortion::SetupLookupTable(double max, int resolution)
{
lookup_.clear();
double incr = max/(double)resolution;
double value = 0.0;
double old=-1.0;
while (value < max)
{
double f = 1.0;
for (int i = 0; i < static_cast<int>(k_.size()); i++)
f += (k_[i] * pow(value*value, i+1));
if ((f*value)>old)
{
//cout << "R = " << value << " ,Rd = " << f*value << " ,f = " << f << endl;
lookup_[f*value] = 1.0/f;
old=f*value;
value += incr;
}
else {
//vstDebug(100)<<"vstUndoRadialDistortion::SetupLookupTable:warning RADIAL DISTORTION FOLDING BACK at "<<value<<endl;
break;
}
}
}

View File

@ -0,0 +1,38 @@
#include <vector>
#include <map>
// using namespace std;
//#include <vil/vil_warp.h>
class RadialDistortion // : public vil_warp_mapping
{
public:
RadialDistortion() { }
RadialDistortion(const RadialDistortion &obj):
k_(obj.k_), ocx_(obj.ocx_), ocy_(obj.ocy_), ncx_(obj.ncx_), ncy_(obj.ncy_), lookup_(obj.lookup_), max_(obj.max_), resolution_(obj.resolution_) {}
void SetParameters(std::vector<double>& k, double max = 2000, int resolution = 10000);
std::vector<double> GetParameters() { return k_; };
void ComputeNewXY(double xo, double yo, double& xn, double& yn) const;
void ComputeOldXY(double xn, double yn, double& xo, double& yo) const;
// for vil_warp, which doesn't work
void forward_map(double x1, double y1, double* x2, double* y2) const;
void inverse_map(double x2, double y2, double* x1, double* y1) const;
protected:
std::vector<double> k_;
double ocx_;
double ocy_;
double ncx_;
double ncy_;
void SetupLookupTable(double max, int resolution);
std::map<double, double> lookup_;
double max_;
int resolution_;
};

View File

@ -0,0 +1,173 @@
// A camera consists of K, R, t and the radial distortion parameters.
// We have an UndoRadialDistortion member (undoraddist_) which is
// initialized with the distortion parameters.
// Furthermore we have an SVD with the P-matrix (R,t, no K). This is used
// to compute the point at infinity (direction of a line) from a point in
// the image.
// We use vnl (of VXL: www.vxl.org) for the math but any other library will
// do.
void vstRadialEuclideanCamera::RecomputeSvd()
{
vnl_matrix_fixed<double, 3, 4> P;
P.update(R_.transpose(), 0, 0);
P.set_column(3, -R_.transpose() * T_);
Psvd_ = vstSvd<double>(P);
}
// from depth and 2D point to a 3D point:
void vstRadialEuclideanCamera::DepthTo3DPoint(const vstPoint2D &m, double depth, vstPoint3D &M) const
{
vnl_vector_fixed<double, 3> m_temp = Kinv_ * m.GetVector();
double x, y;
undoraddist_.ComputeOldXY(m_temp(0) / m_temp(2), m_temp(1) / m_temp(2), x, y);
m_temp(0) = x;
m_temp(1) = y;
m_temp(2) = 1;
vstPoint3D fp(T_);
vnl_vector_fixed<double, 4> end(Psvd_.solve(m_temp));
vstLine3D l(fp, vstPoint3D(end));
vnl_vector_fixed<double, 4> dir(l.GetPointInfinite().GetVector());
dir.normalize();
M.SetVector(fp.GetVector() + depth * dir);
if (IsBehindCamera(M))
M.SetVector(fp.GetVector() - depth * dir);
}
// Undo Radial Distortion: This class has two maps to map undistorted to
// distorted coordinates and vice versa.
#include <vector>
#include <map>
// using namespace std;
//#include <vil/vil_warp.h>
class vstUndoRadialDistortion // : public vil_warp_mapping
{
public:
vstUndoRadialDistortion() { }
vstUndoRadialDistortion(const vstUndoRadialDistortion &obj):
k_(obj.k_), ocx_(obj.ocx_), ocy_(obj.ocy_), ncx_(obj.ncx_), ncy_(obj.ncy_), lookup_(obj.lookup_), max_(obj.max_), resolution_(obj.resolution_) {}
void SetParameters(std::vector<double>& k, double max = 2000, int resolution = 10000);
std::vector<double> GetParameters() { return k_; };
void ComputeNewXY(double xo, double yo, double& xn, double& yn) const;
void ComputeOldXY(double xn, double yn, double& xo, double& yo) const;
// for vil_warp, which doesn't work
void forward_map(double x1, double y1, double* x2, double* y2) const;
void inverse_map(double x2, double y2, double* x1, double* y1) const;
protected:
std::vector<double> k_;
double ocx_;
double ocy_;
double ncx_;
double ncy_;
void SetupLookupTable(double max, int resolution);
std::map<double, double> lookup_;
double max_;
int resolution_;
};
#include <cmath>
using namespace std;
void vstUndoRadialDistortion::SetParameters(vector<double>& k, double max, int resolution)
{
k_ = k;
max_ = max;
resolution_ = resolution;
SetupLookupTable(max_, resolution_);
}
void vstUndoRadialDistortion::forward_map(double x1, double y1, double* x2, double* y2) const
{
ComputeNewXY(x1-ocx_, y1-ocy_, *x2, *y2);
*x2 += ncx_;
*y2 += ncy_;
}
void vstUndoRadialDistortion::inverse_map(double x2, double y2, double* x1, double* y1) const
{
ComputeOldXY(x2-ncx_, y2-ncy_, *x1, *y1);
*x1 += ocx_;
*y1 += ocy_;
}
//----------------------------------------------------------------------------
// Relative w.r.t. center !
void vstUndoRadialDistortion::ComputeNewXY(double xo, double yo, double& xn, double& yn) const
{
double r = (xo*xo) + (yo*yo); // r is r squared
double f = 1.0;
for (int i = 0; i < k_.size(); i++) f += (k_[i] * pow(r, i+1));
xn = f*xo;
yn = f*yo;
}
void vstUndoRadialDistortion::ComputeOldXY(double xn, double yn, double& xo, double& yo) const
{
double rn = sqrt((xn*xn) + (yn*yn)); //// r is r squared
map<double, double>::const_iterator next = lookup_.upper_bound(rn);
map<double, double>::const_iterator prev = next; prev--;
// The compiler will optimise this.
double a = (*prev).first;
double b = (*next).first;
double c = rn;
double d = (*prev).second;
double e = (*next).second;
double f = ((e-d)/(b-a))*(c-a) + d;
xo = f*xn;
yo = f*yn;
}
void vstUndoRadialDistortion::SetupLookupTable(double max, int resolution)
{
lookup_.clear();
double incr = max/(double)resolution;
double value = 0.0;
double old=-1.0;
while (value < max)
{
double f = 1.0;
for (int i = 0; i < k_.size(); i++)
f += (k_[i] * pow(value*value, i+1));
if ((f*value)>old)
{
//cout << "R = " << value << " ,Rd = " << f*value << " ,f = " << f << endl;
lookup_[f*value] = 1.0/f;
old=f*value;
value += incr;
}
else {
//vstDebug(100)<<"vstUndoRadialDistortion::SetupLookupTable:warning RADIAL DISTORTION FOLDING BACK at "<<value<<endl;
break;
}
}
}

View File

@ -0,0 +1,192 @@
#include <Qt>
#include <QPixmap>
//#include <QtGui>
//#include <QtXml/QDomDocument>
//#include <QtXml/QDomElement>
//#include <QtXml/QDomNode>
//
//
//// temporaneamente prendo la versione corrente dalla cartella test
//#include<vcg/complex/trimesh/update/bounding.h>
//#include <wrap/io_trimesh/io_mask.h>
//#include <vcg/complex/trimesh/create/platonic.h>
//#include <vcg/complex/trimesh/update/bounding.h>
//#include <vcg/complex/trimesh/update/normal.h>
//#include <vcg/math/matrix33.h>
//#include<vcg/complex/trimesh/append.h>
//
//#include <QMessageBox>
//#include <QFileDialog>
//#include "epoch.h"
//#include "radial_distortion.h"
//#include "epoch_camera.h"
#include <vector>
#include "scalar_image.h"
#include <bzlib.h>
using namespace std;
/*
Images with bit-depths greater than 8bpp and up to 31bpp are supported by the JJ2000 codec if they are stored in PGX files
(one file per component). PGX is a custom monochrome file format invented specifically to simplify the use of JPEG 2000
with images of different bit-depths in the range of 1 to 31 bits per pixel.
The file consists of a one line text header followed by the (raw) data.
Header: "PG"+ ws +<endianess>+ ws +[sign]+ws + <bit-depth>+" "+<width>+" "+<height>+[compressed_size]'\n'
where:
* ws (white-spaces) is any combination of characters ' ' and '\t'.
* endianess equals "LM" or "ML"(resp. little-endian or big-endian)
* sign equals "+" or "-" (resp. unsigned or signed). If omited, values are supposed to be unsigned.
* bit-depth that can be any number between 1 and 31. This number must take into account the eventual sign bit.
* width and height are the image dimensions (in pixels).
Data: The image binary values appear one after the other (in raster order) immediately after the last header character ('\n')
and are byte-aligned (they are packed into 1,2 or 4 bytes per sample, depending upon the bit-depth value).
*/
template <>
bool ScalarImage<float>::Open(const char *filename)
{
FILE *fp=fopen(filename,"rb");
if(!fp) return false;
char buf[256];
fgets(buf,255,fp);
qDebug("Header of %s is '%s'",filename,buf);
float ll,lh;
int depth;
char mode;
int compressed_size=0;
sscanf(buf,"PG LM %i %i %i %c %f %f %i",&depth,&w,&h,&mode,&ll,&lh,&compressed_size);
qDebug("image should be of %i x %i %i depth and with range in %f -- %f in mode %c",w,h,depth,ll,lh,mode);
if(depth!=16)
{
qDebug("Wrong depth of image 16 bit expected");
return false;
}
if (mode != 'l' && mode != 'L')
{
qDebug("Wrong mode, expected l or L");
return false;
}
if (mode == 'l')
{
vector<unsigned short> bb(w*h);
fread(&*bb.begin(),w*h,sizeof(short),fp);
v.resize(w*h);
for(int i =0; i<w*h;++i)
v[i]=ll+(lh-ll)*(float(bb[i])/65536.0f);
}
else
{
char* compressed_buffer = new char[compressed_size];
fread(compressed_buffer,compressed_size,1,fp);
// decompress!
size_t size = w*h*sizeof(short);
unsigned char * uncompressed_buffer = new unsigned char[size];
unsigned int mysize = size;
BZ2_bzBuffToBuffDecompress((char*)uncompressed_buffer, &mysize, compressed_buffer, compressed_size, 0, 0);
if (mysize != size)
{
qDebug("This is very wrong. The uncompressed size is not the expected size");
return false;
}
int imagesize = w*h;
unsigned char *correct_buffer = new unsigned char[size];
for (int i=0; i<imagesize; ++i)
{
for (int j=0; j<sizeof(short); ++j)
{
correct_buffer[sizeof(short)*i +j] = uncompressed_buffer[imagesize*j + i];
}
}
v.resize(w*h);
for (int i=0; i<imagesize; ++i)
{
float a = *((unsigned short*)&correct_buffer[i*sizeof(short)]);
v[i] = ll+(lh-ll)*(a/65536.0f);
}
delete [] uncompressed_buffer;
delete [] compressed_buffer;
delete [] correct_buffer;
}
fclose(fp);
return true;
}
template <>
bool ScalarImage<unsigned char>::Open(const char *filename)
{
FILE *fp=fopen(filename,"rb");
if(!fp) return false;
char buf[256];
fgets(buf,255,fp);
qDebug("Header of %s is '%s'",filename,buf);
int depth;
char mode = ' ';
int compressed_size=0;
int nrscan = sscanf(buf,"PG LM %i %i %i %c %i",&depth,&w,&h,&mode,&compressed_size);
if (nrscan == 3)
qDebug("image should be of %i x %i %i depth ",w,h,depth);
else
qDebug("compressed image of %i x %i %i depth ",w,h,depth);
if(depth!=8)
{
qDebug("Wrong depth of image: 8 bit expected");
return false;
}
if (mode != 'C')
{
v.resize(w*h);
fread(&*v.begin(),w*h,sizeof(unsigned char),fp);
}
else
{
char* compressed_buffer = new char[compressed_size];
fread(compressed_buffer,compressed_size,1,fp);
// decompress!
unsigned int mysize = w*h;
v.resize(w*h);
BZ2_bzBuffToBuffDecompress((char*)&*v.begin(), &mysize, compressed_buffer, compressed_size, 0, 0);
if (mysize != w*h)
{
qDebug("This is very wrong. The uncompressed size is not the expected size");
return false;
}
}
fclose(fp);
return true;
}
template <>
ScalarImage<unsigned char>::ScalarImage(QImage img)
{
resize(img.width(),img.height());
for(int y=0;y<h;++y)
for(int x=0;x<w;++x)
Val(x,y)=qGray(img.pixel(x,y));
}
template <class ScalarType>
bool ScalarImage<ScalarType>::Subsample(const int factor, ScalarImage<ScalarType> &fli)
{
resize(fli.w/factor,fli.h/factor);
for(int i=0;i<h;++i)
for(int j=0;j<w;++j)
Val(i,j)=fli.Val(i*factor,j*factor);
return true;
};

View File

@ -0,0 +1,127 @@
#ifndef VCG_SCALARIMAGE_H
#define VCG_SCALARIMAGE_H
#include <algorithm>
#include <assert.h>
#include <vcg/space/color4.h>
/*
Very simple class to store a bitmap of floating point values.
*/
template <class ScalarType>
class ScalarImage
{
public:
std::vector<ScalarType> v;
int w,h;
void resize(int _w, int _h) { w=_w; h=_h; v.resize(w*h);}
bool Open(const char *filename);
ScalarType &Val(int x,int y) {
assert(x>=0 && x<w);
assert(y>=0 && y<h);
return v[y*w+x];
};
ScalarType Val(int x,int y) const {
assert(x>=0 && x<w);
assert(y>=0 && y<h);
return v[y*w+x];
};
void operator = ( const ScalarImage & img )
{
w=img.w;
h=img.h;
v=img.v;
}
ScalarType MinVal() { return *std::min_element(v.begin(),v.end()); }
ScalarType MaxVal() { return *std::max_element(v.begin(),v.end()); }
inline QImage convertToQImage()
{
QImage img(w,h,QImage::Format_RGB32);
float maxV = MaxVal();
float minV = MinVal();
float scale = 1.0f / (maxV-minV);
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++)
{
float value = (Val(x, y) - minV) * scale;
value *= 255.0f;
img.setPixel(x,y,qRgb(value,value,value));
}
return img;
}
void Erode(ScalarImage &Eroded, const int wsize=1) const
{
Eroded.resize(w,h);
// erosion filter (3 x 3)
int minimum;
for (int y = wsize; y < h-wsize; y++)
for (int x = wsize; x < w-wsize; x++)
{
minimum = Val(x, y);
for (int yy = y - wsize; yy <= y + wsize; yy++)
for (int xx = x - wsize; xx <= x + wsize; xx++)
if (Val(xx, yy) < minimum)
minimum = Val(xx, yy);
Eroded.Val(x, y) = minimum;
}
}
void Dilate(ScalarImage &Dilated, int wsize=1)
{
Dilated.resize(w,h);
// dilation filter (3 x 3)
int maximum;
for (int y = wsize; y < h-wsize; y++)
for (int x = wsize; x < w-wsize; x++)
{
maximum = Val(x, y);
for (int yy = y - wsize; yy <= y + wsize; yy++)
for (int xx = x - wsize; xx <= x + wsize; xx++)
if (Val(xx, yy) > maximum)
maximum = Val(xx, yy);
Dilated.Val(x, y) = maximum;
}
}
ScalarImage(QImage img);
ScalarImage(){};
bool Subsample(const int factor, ScalarImage<ScalarType> &fli);
static QPixmap colorizedScaledToHeight(const int desiredH, ScalarImage<ScalarType> &fli, float colormax=10)
{
assert(fli.h>desiredH);
int factor = fli.h / desiredH;
int newW=fli.w/factor -1;
int newH=fli.h/factor -1;
QImage newImage(newW,newH,QImage::Format_RGB32);
for(int i=0;i<newImage.height();++i)
for(int j=0;j<newImage.width();++j)
{
float sum=0;
for(int si=0;si<factor;++si)
for(int sj=0;sj<factor;++sj)
sum+= fli.Val(j*factor+sj,i*factor+si);
sum/=factor*factor;
sum=std::min(sum,colormax);
vcg::Color4b avgcolor; avgcolor.ColorRamp(0,colormax,sum);
newImage.setPixel(j,i,qRgb(avgcolor.V(0),avgcolor.V(1),avgcolor.V(2)));
}
return QPixmap::fromImage(newImage);
};
};
typedef ScalarImage<float> FloatImage;
typedef ScalarImage<unsigned char> CharImage;
#endif

View File

@ -0,0 +1,678 @@
<ui version="4.0" >
<class>v3dImportDialog</class>
<widget class="QDialog" name="v3dImportDialog" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>792</width>
<height>606</height>
</rect>
</property>
<property name="sizePolicy" >
<sizepolicy vsizetype="Preferred" hsizetype="Expanding" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle" >
<string>V3D Import Settings</string>
</property>
<layout class="QHBoxLayout" >
<item>
<layout class="QVBoxLayout" >
<item>
<widget class="QLabel" name="infoLabel" >
<property name="text" >
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="previewLabel" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text" >
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" >
<item>
<widget class="QLabel" name="label" >
<property name="text" >
<string>SubSample</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="imgSizeLabel" >
<property name="text" >
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="subsampleSpinBox" >
<property name="toolTip" >
<string>&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;/head>&lt;body style=" white-space: pre-wrap; font-style:normal; text-decoration:none;">&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Set the subsample factor:&lt;/p>&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> 1 the image is not resized&lt;/p>&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> 2 image is halved &lt;span style=" font-style:italic;">(one point every 4)&lt;/span>&lt;/p>&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> 3 image is reduced to one third &lt;span style=" font-style:italic;">(one point every 9)&lt;/span>&lt;/p>&lt;/body>&lt;/html></string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" >
<item>
<widget class="QLabel" name="label_2" >
<property name="text" >
<string>Minimum Count</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<size>
<width>91</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QSpinBox" name="minCountSpinBox" >
<property name="toolTip" >
<string>&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;/head>&lt;body style=" white-space: pre-wrap; font-style:normal; text-decoration:none;">&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Set the minimum number of match for a sample to be accepted&lt;/p>&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">3 means that only samples that had been found a correspondence with other 2 images or more are considered&lt;/p>&lt;/body>&lt;/html></string>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" >
<property name="spacing" >
<number>0</number>
</property>
<item>
<widget class="QSlider" name="minCountSlider" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Fixed" hsizetype="Expanding" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximum" >
<number>10</number>
</property>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="rangeLabel" >
<property name="text" >
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" >
<item>
<widget class="QLabel" name="label_3" >
<property name="text" >
<string>Minimum Angle</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDoubleSpinBox" name="qualitySpinBox" >
<property name="decimals" >
<number>1</number>
</property>
<property name="maximum" >
<double>90.000000000000000</double>
</property>
<property name="singleStep" >
<double>5.000000000000000</double>
</property>
<property name="value" >
<double>75.000000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" >
<item>
<widget class="QLabel" name="label_5" >
<property name="text" >
<string>Feature Aware Smoothing</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDoubleSpinBox" name="smoothSpinBox" >
<property name="decimals" >
<number>0</number>
</property>
<property name="maximum" >
<double>20.000000000000000</double>
</property>
<property name="value" >
<double>3.000000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" >
<item>
<widget class="QCheckBox" name="removeSmallCCCheckBox" >
<property name="layoutDirection" >
<enum>Qt::RightToLeft</enum>
</property>
<property name="text" >
<string>Remove pieces less than</string>
</property>
<property name="checked" >
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="maxCCDiagSpinBox" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Fixed" hsizetype="Preferred" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip" >
<string>When enabled, all the floating pieces smaller than the indicated percentage are deleted. Unit is the diagonal of the bounding box of the object</string>
</property>
<property name="decimals" >
<number>0</number>
</property>
<property name="maximum" >
<double>25.000000000000000</double>
</property>
<property name="singleStep" >
<double>5.000000000000000</double>
</property>
<property name="value" >
<double>5.000000000000000</double>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="holeCheckBox" >
<property name="enabled" >
<bool>true</bool>
</property>
<property name="layoutDirection" >
<enum>Qt::RightToLeft</enum>
</property>
<property name="text" >
<string>Close Holes less than</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="holeSpinBox" >
<property name="singleStep" >
<number>5</number>
</property>
<property name="value" >
<number>10</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" >
<item row="0" column="5" >
<widget class="QSlider" name="dilationSizeSlider" >
<property name="minimum" >
<number>1</number>
</property>
<property name="maximum" >
<number>6</number>
</property>
<property name="singleStep" >
<number>1</number>
</property>
<property name="pageStep" >
<number>5</number>
</property>
<property name="value" >
<number>2</number>
</property>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="1" column="3" >
<widget class="QSpinBox" name="erosionNumPassSpinBox" >
<property name="minimum" >
<number>1</number>
</property>
<property name="maximum" >
<number>10</number>
</property>
<property name="value" >
<number>3</number>
</property>
</widget>
</item>
<item row="0" column="4" >
<widget class="QLabel" name="lblDilationSize" >
<property name="text" >
<string>Size:</string>
</property>
</widget>
</item>
<item row="1" column="6" >
<widget class="QLabel" name="lblErosionSizeValue" >
<property name="text" >
<string>5 x 5</string>
</property>
</widget>
</item>
<item row="0" column="3" >
<widget class="QSpinBox" name="dilationNumPassSpinBox" >
<property name="minimum" >
<number>1</number>
</property>
<property name="maximum" >
<number>5</number>
</property>
</widget>
</item>
<item row="0" column="2" >
<widget class="QLabel" name="lblDilationSteps" >
<property name="text" >
<string>Num. passes:</string>
</property>
</widget>
</item>
<item row="1" column="5" >
<widget class="QSlider" name="erosionSizeSlider" >
<property name="minimum" >
<number>1</number>
</property>
<property name="maximum" >
<number>6</number>
</property>
<property name="singleStep" >
<number>1</number>
</property>
<property name="pageStep" >
<number>5</number>
</property>
<property name="value" >
<number>2</number>
</property>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="0" column="6" >
<widget class="QLabel" name="lblDilationSizeValue" >
<property name="text" >
<string>5 x 5</string>
</property>
</widget>
</item>
<item row="1" column="2" >
<widget class="QLabel" name="lblErosionSteps" >
<property name="text" >
<string>Num. passes:</string>
</property>
</widget>
</item>
<item row="0" column="0" >
<widget class="QLabel" name="label_8" >
<property name="text" >
<string>Depth Filter</string>
</property>
</widget>
</item>
<item row="1" column="4" >
<widget class="QLabel" name="lblErosionSize" >
<property name="text" >
<string>Size:</string>
</property>
</widget>
</item>
<item row="0" column="1" >
<widget class="QCheckBox" name="dilationCheckBox" >
<property name="text" >
<string>Dilation</string>
</property>
<property name="checked" >
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1" >
<widget class="QCheckBox" name="erosionCheckBox" >
<property name="text" >
<string>Erosion</string>
</property>
<property name="checked" >
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" >
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="fastMergeCheckBox" >
<property name="toolTip" >
<string>&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;/head>&lt;body style=" white-space: pre-wrap; font-style:normal; text-decoration:none;">&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Perform a fast, fixed resolution merging of all the range maps. If unchecked all the rangemaps are simply put in the same space without merging them.&lt;/p>&lt;/body>&lt;/html></string>
</property>
<property name="layoutDirection" >
<enum>Qt::RightToLeft</enum>
</property>
<property name="text" >
<string>Fast merge</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_6" >
<property name="text" >
<string>Resolution: Min</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="mergeResolutionSpinBox" >
<property name="minimum" >
<number>1</number>
</property>
<property name="maximum" >
<number>5</number>
</property>
<property name="value" >
<number>3</number>
</property>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_7" >
<property name="text" >
<string>Max</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" >
<item>
<widget class="QPushButton" name="selectButton" >
<property name="text" >
<string>Select</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QSpinBox" name="subsampleSequenceSpinBox" >
<property name="value" >
<number>3</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" >
<item>
<widget class="QLabel" name="label_10" >
<property name="text" >
<string>Scaling Factor</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLineEdit" name="scaleLineEdit" >
<property name="inputMask" >
<string/>
</property>
<property name="text" >
<string>1.0</string>
</property>
<property name="alignment" >
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" >
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<size>
<width>131</width>
<height>31</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="okButton" >
<property name="text" >
<string>OK</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="plyButton" >
<property name="toolTip" >
<string>Process all selected range maps and save them as separated ply</string>
</property>
<property name="text" >
<string>Export as PLY</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cancelButton" >
<property name="text" >
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QTableWidget" name="imageTableWidget" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Expanding" hsizetype="MinimumExpanding" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="columnCount" >
<number>3</number>
</property>
<column/>
<column/>
<column/>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>okButton</sender>
<signal>clicked()</signal>
<receiver>v3dImportDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel" >
<x>554</x>
<y>682</y>
</hint>
<hint type="destinationlabel" >
<x>342</x>
<y>405</y>
</hint>
</hints>
</connection>
<connection>
<sender>cancelButton</sender>
<signal>clicked()</signal>
<receiver>v3dImportDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel" >
<x>637</x>
<y>682</y>
</hint>
<hint type="destinationlabel" >
<x>425</x>
<y>433</y>
</hint>
</hints>
</connection>
<connection>
<sender>removeSmallCCCheckBox</sender>
<signal>toggled(bool)</signal>
<receiver>maxCCDiagSpinBox</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel" >
<x>348</x>
<y>584</y>
</hint>
<hint type="destinationlabel" >
<x>637</x>
<y>584</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,246 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
#include <Qt>
#include <QtGui>
#include <QDialog>
#include "v3dImportDialog.h"
#include "maskImageWidget.h"
using namespace vcg;
// small helper function for generating a color ramp pixmap to be used in the import dialog.
QPixmap generateColorRamp()
{
const int width=100;
const int height=15;
Color4b ramp;
QImage newImage(width,height,QImage::Format_RGB32);
for(int x=0;x<width;++x)
for(int y=0;y<height;++y)
{
ramp.ColorRamp(0,width,x);
newImage.setPixel(x,y,qRgb(ramp.V(0),ramp.V(1),ramp.V(2)));
}
return QPixmap::fromImage(newImage);
}
/*
Main function that populate the dialog, loading all the images and eventually creating the thumbs.
called directly by the open before invoking this dialog.
*/
void v3dImportDialog::setEpochReconstruction(EpochReconstruction *_er,CallBackPos *cb)
{
// if the epoch reconstruction has not changed do nothing
if(erCreated == _er->created)
{
er=_er;
return;
}
er=_er;
erCreated=er->created;
infoLabel->setText(er->name + " - " + er->author + " - " + er->created);
imageTableWidget->clear();
imageTableWidget->setRowCount(er->modelList.size());
imageTableWidget->setColumnCount(4);
//imageTableWidget->setColumnWidth (1,64);
imageTableWidget->setSelectionBehavior (QAbstractItemView::SelectRows);
imageTableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
imageTableWidget->setMinimumWidth(64*8);
rangeLabel->setPixmap(generateColorRamp());
rangeLabel->setMaximumHeight(10);
rangeLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
rangeLabel->setScaledContents(true);
minCountSlider->setMaximumHeight(12);
int i;
for(i=0; i<er->modelList.size() ;++i)
{
cb(i*100/er->modelList.size(),"Reading images");
QString ThumbImgName=EpochModel::ThumbName(er->modelList[i].textureName);
QString ThumbCntName=EpochModel::ThumbName(er->modelList[i].countName);
imageTableWidget->setRowHeight (i,64);
QTableWidgetItem *emptyHeaderItem = new QTableWidgetItem(i*2);
QTableWidgetItem *texNameHeaderItem = new QTableWidgetItem(er->modelList[i].textureName,i);
imageTableWidget->setItem(i, 0, texNameHeaderItem);
imageTableWidget->setItem(i, 1, emptyHeaderItem);
QLabel *imageLabel = new QLabel(imageTableWidget);
if(!QFile::exists(ThumbImgName))
{
QPixmap(er->modelList[i].textureName).scaledToHeight(64).save(ThumbImgName,"jpg");
if(!QFile::exists(ThumbImgName))
QMessageBox::warning(this,"Error in Thumb creation",
QString("Unable to create '%1' from '%2'").arg(ThumbImgName),er->modelList[i].textureName);
}
imageLabel->setPixmap(QPixmap(EpochModel::ThumbName(er->modelList[i].textureName)));
imageTableWidget->setCellWidget(i,1,imageLabel);
if(QFile::exists(er->modelList[i].maskName))
{
QTableWidgetItem *emptyHeaderItem = new QTableWidgetItem(i*4);
imageTableWidget->setItem(i, 2, emptyHeaderItem);
QLabel *maskLabel = new QLabel(imageTableWidget);
maskLabel->setPixmap(QPixmap(er->modelList[i].maskName).scaledToHeight(64));
imageTableWidget->setCellWidget(i,2,maskLabel);
}
else
{
QTableWidgetItem *maskHeaderItem = new QTableWidgetItem(QString("double click for\nediting the mask"),i*3);
imageTableWidget->setItem(i, 2, maskHeaderItem);
}
if(!QFile::exists(ThumbCntName))
{
CharImage chi;
bool ret=chi.Open(er->modelList[i].countName.toAscii());
if(!ret) QMessageBox::warning(this,"Error in Thumb creation",QString("Unable to create '%1' from '%2'").arg(ThumbCntName,er->modelList[i].textureName));
CharImage::colorizedScaledToHeight(64,chi).save(ThumbCntName,"jpg");
if(!QFile::exists(ThumbCntName))
QMessageBox::warning(this,"Error in Thumb creation",QString("Unable to create '%1' from '%2'").arg(ThumbCntName,er->modelList[i].textureName));
}
QLabel *countLabel = new QLabel(imageTableWidget);
countLabel->setPixmap(QPixmap(ThumbCntName));
QPixmap tmp(ThumbCntName);
if(tmp.isNull())
QMessageBox::warning(this,"Error in Thumb creation",QString("Null Pixmap '%1'").arg(ThumbCntName));
imageTableWidget->setCellWidget(i,3,countLabel);
}
cb(100,"Completed Image Reading.");
show(); // necessary to make the size of the image preview correct.
imageTableWidget->setItemSelected(imageTableWidget->item(0,0),true);
imageTableWidget->setItemSelected(imageTableWidget->item(0,1),true);
imageTableWidget->setItemSelected(imageTableWidget->item(0,2),true);
//on_imageTableWidget_itemSelectionChanged();
}
void v3dImportDialog::on_imageTableWidget_itemSelectionChanged()
{
if(imageTableWidget->selectedItems().size()==3)
{
int row= imageTableWidget->row(imageTableWidget->selectedItems().first());
QPixmap tmp(er->modelList[row].textureName);
imgSize=tmp.size();
previewLabel->setPixmap(tmp.scaled(previewLabel->size(),Qt::KeepAspectRatio) );
on_subsampleSpinBox_valueChanged(subsampleSpinBox->value());
}
}
void v3dImportDialog::on_plyButton_clicked()
{
exportToPLY = true;
accept();
}
void v3dImportDialog::on_selectButton_clicked()
{
int itemNum=imageTableWidget->rowCount();
int modVal=subsampleSequenceSpinBox->value();
if(modVal==0) return;
for(int i=0;i<itemNum;i+=modVal)
imageTableWidget->setRangeSelected(QTableWidgetSelectionRange(i,0,i,2),true);
}
void v3dImportDialog::on_imageTableWidget_itemClicked(QTableWidgetItem * item )
{
int row= imageTableWidget->row(item);
previewLabel->setPixmap(
QPixmap(er->modelList[row].textureName).scaled(previewLabel->size(),Qt::KeepAspectRatio) );
}
void v3dImportDialog::on_imageTableWidget_itemDoubleClicked(QTableWidgetItem * item )
{
int row= imageTableWidget->row(item);
int col= imageTableWidget->column(item);
if(col!=2) return;
qDebug("DoubleClicked on image %s",qPrintable(er->modelList[row].textureName));
QImage ttt(er->modelList[row].textureName);
qDebug("'%s' %i x %i",qPrintable(er->modelList[row].textureName),ttt.width(),ttt.height());
//ui::maskImageWidget masker(QImage(er->modelList[row].textureName));
ui::maskImageWidget masker(ttt);
if (QFile::exists(er->modelList[row].maskName))
masker.loadMask(er->modelList[row].maskName);
QImage mymask;
if (masker.exec() == QDialog::Accepted) mymask = masker.getMask();
if (!mymask.isNull())
{
mymask.save(er->modelList[row].maskName,"png");
QLabel *imageLabel = new QLabel(imageTableWidget);
imageLabel->setPixmap(QPixmap(er->modelList[row].maskName).scaledToHeight(64));
imageTableWidget->itemAt(row,2)->setText("");
imageTableWidget->setCellWidget(row,2,imageLabel);
}
}
void v3dImportDialog::on_minCountSpinBox_valueChanged(int val)
{
if(minCountSlider->value()!=val) minCountSlider->setValue(val);
}
void v3dImportDialog::on_minCountSlider_valueChanged(int val)
{
if(minCountSpinBox->value()!=val) minCountSpinBox->setValue(val);
}
void v3dImportDialog::on_subsampleSpinBox_valueChanged(int)
{
int ss=subsampleSpinBox->value();
if(ss==0)
{
subsampleSpinBox->setValue(1);
return;
}
imgSizeLabel->setText(QString("(%1 x %2) -> (%3 x %4)").arg(imgSize.width()).arg(imgSize.height()).arg(imgSize.width()/ss).arg(imgSize.height()/ss) );
}
void v3dImportDialog::on_mergeResolutionSpinBox_valueChanged(int)
{
fastMergeCheckBox->setChecked(true); // if someone touch the slider check the fast merging box
}
void v3dImportDialog::dilationSizeChanged(int size)
{
int winsize = size * 2 + 1;
QString str = QString("%1 x %2").arg(winsize).arg(winsize);
lblDilationSizeValue->setText(str);
}
void v3dImportDialog::erosionSizeChanged(int size)
{
int winsize = size * 2 + 1;
QString str = QString("%1 x %2").arg(winsize).arg(winsize);
lblErosionSizeValue->setText(str);
}

View File

@ -0,0 +1,53 @@
#ifndef V3DIMPORTER_DIALOG_H
#define V3DIMPORTER_DIALOG_H
// for options on decimator
#include <QDialog>
#include "ui_v3dImportDialog.h"
#include "epoch_reconstruction.h"
class v3dImportDialog : public QDialog, public Ui::v3dImportDialog {
Q_OBJECT
public:
v3dImportDialog() : QDialog()
{
setupUi( this );
subsampleSpinBox->setValue(2);
minCountSpinBox->setValue(3);
// connections
connect(dilationSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(dilationSizeChanged(int)));
connect(erosionSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(erosionSizeChanged(int)));
er=0;
exportToPLY=false;
}
public:
void setEpochReconstruction(EpochReconstruction *_er,vcg::CallBackPos *cb);
bool exportToPLY; /// when true all the selected range maps are exported as separated ply
public slots:
void on_selectButton_clicked();
void on_imageTableWidget_itemClicked(QTableWidgetItem * item );
void on_imageTableWidget_itemSelectionChanged();
void on_imageTableWidget_itemDoubleClicked(QTableWidgetItem * item );
void on_plyButton_clicked();
private:
EpochReconstruction *er;
QString erCreated;
QSize imgSize;
private slots:
void on_mergeResolutionSpinBox_valueChanged(int);
void on_subsampleSpinBox_valueChanged(int);
void on_minCountSlider_valueChanged(int);
void on_minCountSpinBox_valueChanged(int);
void dilationSizeChanged(int);
void erosionSizeChanged(int);
};
#endif