mirror of
https://github.com/lucaspalomodevelop/meshlab.git
synced 2026-03-19 19:14:42 +00:00
first working version of the project-to-texture filter
This commit is contained in:
parent
2990c8a34f
commit
0b98dceb5a
@ -34,15 +34,31 @@
|
||||
|
||||
#include "render_helper.cpp"
|
||||
|
||||
#include "pushpull.h"
|
||||
#include "rastering.h"
|
||||
#include <vcg/complex/algorithms/update/texture.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace vcg;
|
||||
|
||||
// utility---------------------------------
|
||||
|
||||
#define CheckError(x,y); if ((x)) {this->errorMessage = (y); return false;}
|
||||
|
||||
static QString extractFilenameWOExt(MeshModel* mm)
|
||||
{
|
||||
QFileInfo fi(mm->fullName());
|
||||
return fi.baseName();
|
||||
}
|
||||
//-----------------------------------------
|
||||
|
||||
// Constructor
|
||||
FilterColorProjectionPlugin::FilterColorProjectionPlugin()
|
||||
{
|
||||
typeList
|
||||
<< FP_SINGLEIMAGEPROJ
|
||||
<< FP_MULTIIMAGETRIVIALPROJ;
|
||||
<< FP_MULTIIMAGETRIVIALPROJ
|
||||
<< FP_MULTIIMAGETRIVIALPROJTEXTURE;
|
||||
|
||||
foreach(FilterIDType tt , types())
|
||||
actionList << new QAction(filterName(tt), this);
|
||||
@ -54,6 +70,7 @@ QString FilterColorProjectionPlugin::filterName(FilterIDType filterId) const
|
||||
switch(filterId) {
|
||||
case FP_SINGLEIMAGEPROJ : return QString("Project current raster color to current mesh");
|
||||
case FP_MULTIIMAGETRIVIALPROJ : return QString("Project active rasters color to current mesh");
|
||||
case FP_MULTIIMAGETRIVIALPROJTEXTURE : return QString("Project active rasters color to current mesh, filling the texture");
|
||||
default : assert(0);
|
||||
}
|
||||
}
|
||||
@ -64,6 +81,7 @@ QString FilterColorProjectionPlugin::filterName(FilterIDType filterId) const
|
||||
switch(filterId) {
|
||||
case FP_SINGLEIMAGEPROJ : return QString("Color information from the current raster is perspective-projected on the current mesh");
|
||||
case FP_MULTIIMAGETRIVIALPROJ : return QString("Color information from all the active rasters is perspective-projected on the current mesh using basic weighting");
|
||||
case FP_MULTIIMAGETRIVIALPROJTEXTURE : return QString("Color information from all the active rasters is perspective-projected on the current mesh, filling the texture, using basic weighting");
|
||||
default : assert(0);
|
||||
}
|
||||
}
|
||||
@ -73,6 +91,7 @@ int FilterColorProjectionPlugin::getRequirements(QAction *action){
|
||||
switch(ID(action)){
|
||||
case FP_SINGLEIMAGEPROJ: return MeshModel::MM_VERTCOLOR;
|
||||
case FP_MULTIIMAGETRIVIALPROJ: return MeshModel::MM_VERTCOLOR;
|
||||
case FP_MULTIIMAGETRIVIALPROJTEXTURE : return 0;
|
||||
default: assert(0); return 0;
|
||||
}
|
||||
return 0;
|
||||
@ -132,6 +151,45 @@ void FilterColorProjectionPlugin::initParameterSet(QAction *action, MeshDocument
|
||||
}
|
||||
break;
|
||||
|
||||
case FP_MULTIIMAGETRIVIALPROJTEXTURE :
|
||||
{
|
||||
QString fileName = extractFilenameWOExt(md.mm());
|
||||
fileName = fileName.append("_color.png");
|
||||
parlst.addParam(new RichString("textName",
|
||||
fileName,
|
||||
"Texture file",
|
||||
"The texture file to be created"));
|
||||
parlst.addParam(new RichInt ("texsize",
|
||||
1024,
|
||||
"pixel size of texture image",
|
||||
"pixel size of texture image, the image will be a square tsize X tsize, most applications do require that tsize is a power of 2"));
|
||||
parlst.addParam(new RichFloat ("deptheta",
|
||||
0.5,
|
||||
"depth threshold",
|
||||
"threshold value for depth buffer projection (shadow buffer)"));
|
||||
parlst.addParam(new RichBool ("onselection",
|
||||
false,
|
||||
"Only on selecton",
|
||||
"If true, projection is only done for selected vertices"));
|
||||
parlst.addParam(new RichBool ("useangle",
|
||||
true,
|
||||
"use angle weight",
|
||||
"If true, color contribution is weighted by pixel view angle"));
|
||||
parlst.addParam(new RichBool ("usedistance",
|
||||
true,
|
||||
"use distance weight",
|
||||
"If true, color contribution is weighted by pixel view distance"));
|
||||
parlst.addParam(new RichBool ("useborders",
|
||||
true,
|
||||
"use image borders weight",
|
||||
"If true, color contribution is weighted by pixel distance from image boundaries"));
|
||||
parlst.addParam(new RichBool ("usesilhouettes",
|
||||
true,
|
||||
"use depth discontinuities weight",
|
||||
"If true, color contribution is weighted by pixel distance from depth discontinuities (external and internal silhouettes)"));
|
||||
}
|
||||
break;
|
||||
|
||||
default: break; // do not add any parameter for the other filters
|
||||
}
|
||||
}
|
||||
@ -300,7 +358,7 @@ bool FilterColorProjectionPlugin::applyFilter(QAction *filter, MeshDocument &md,
|
||||
allcammaximagesize = imgdiag;
|
||||
}
|
||||
|
||||
//-- cycle all cmaeras
|
||||
//-- cycle all cameras
|
||||
cam_ind = 0;
|
||||
foreach(RasterModel *raster, md.rasterList)
|
||||
{
|
||||
@ -470,6 +528,289 @@ bool FilterColorProjectionPlugin::applyFilter(QAction *filter, MeshDocument &md,
|
||||
} break;
|
||||
|
||||
|
||||
case FP_MULTIIMAGETRIVIALPROJTEXTURE :
|
||||
{
|
||||
bool onselection = par.getBool("onselection");
|
||||
int texsize = par.getInt("texsize");
|
||||
float eta = par.getFloat("deptheta");
|
||||
bool useangle = par.getBool("useangle");
|
||||
bool usedistance = par.getBool("usedistance");
|
||||
bool useborders = par.getBool("useborders");
|
||||
bool usesilhouettes = par.getBool("usesilhouettes");
|
||||
QString textName = par.getString("textName");
|
||||
|
||||
int textW = texsize;
|
||||
int textH = texsize;
|
||||
|
||||
Point2f pp; // projected point
|
||||
float depth=0; // depth of point (distance from camera)
|
||||
float pdepth=0; // depth value of projected point (from depth map)
|
||||
double pweight; // pixel weight
|
||||
MeshModel *model;
|
||||
bool do_project;
|
||||
int cam_ind;
|
||||
int texcount = 0; // current texel index
|
||||
|
||||
// min max depth for depth weight normalization
|
||||
float allcammaxdepth;
|
||||
float allcammindepth;
|
||||
|
||||
// max image size for border weight normalization
|
||||
float allcammaximagesize;
|
||||
|
||||
// get the working model
|
||||
model = md.mm();
|
||||
|
||||
// the mesh has to be correctly transformed before mapping
|
||||
tri::UpdatePosition<CMeshO>::Matrix(model->cm,model->cm.Tr,true);
|
||||
tri::UpdateBounding<CMeshO>::Box(model->cm);
|
||||
|
||||
// texture file name
|
||||
QString filePath(model->fullName());
|
||||
filePath = filePath.left(std::max<int>(filePath.lastIndexOf('\\'),filePath.lastIndexOf('/'))+1);
|
||||
// Check textName and eventually add .png ext
|
||||
CheckError(textName.length() == 0, "Texture file not specified");
|
||||
CheckError(std::max<int>(textName.lastIndexOf("\\"),textName.lastIndexOf("/")) != -1, "Path in Texture file not allowed");
|
||||
if (!textName.endsWith(".png", Qt::CaseInsensitive))
|
||||
textName.append(".png");
|
||||
filePath.append(textName);
|
||||
|
||||
// Image creation
|
||||
CheckError(textW <= 0, "Texture Width has an incorrect value");
|
||||
CheckError(textH <= 0, "Texture Height has an incorrect value");
|
||||
QImage img(QSize(textW,textH), QImage::Format_ARGB32);
|
||||
img.fill(qRgba(0,0,0,0)); // transparent black
|
||||
|
||||
// Compute (texture-space) border edges
|
||||
model->updateDataMask(MeshModel::MM_FACEFACETOPO);
|
||||
//tri::UpdateTopology<CMeshO>::FaceFace(model->cm);
|
||||
tri::UpdateTopology<CMeshO>::FaceFaceFromTexCoord(model->cm);
|
||||
tri::UpdateFlags<CMeshO>::FaceBorderFromFF(model->cm);
|
||||
|
||||
// create a list of to-be-filled texels and accumulators
|
||||
// storing texel 2d coords, texel mesh-space point, texel mesh normal
|
||||
|
||||
vector<TexelDesc> texels;
|
||||
texels.clear();
|
||||
texels.reserve(textW*textH); // just to avoid the 2x reallocate rule...
|
||||
|
||||
vector<TexelAccum> accums;
|
||||
accums.clear();
|
||||
accums.reserve(textW*textH); // just to avoid the 2x reallocate rule...
|
||||
|
||||
// Rasterizing triangles in the list of voxels
|
||||
TexFillerSampler tfs(img);
|
||||
tfs.texelspointer = &texels;
|
||||
tfs.accumpointer = &accums;
|
||||
tfs.InitCallback(cb, model->cm.fn, 0, 80);
|
||||
tri::SurfaceSampling<CMeshO,TexFillerSampler>::Texture(model->cm,tfs,textW,textH,true);
|
||||
|
||||
// Revert alpha values for border edge pixels to 255
|
||||
cb(81, "Cleaning up texture ...");
|
||||
for (int y=0; y<textH; ++y)
|
||||
for (int x=0; x<textW; ++x)
|
||||
{
|
||||
QRgb px = img.pixel(x,y);
|
||||
if (qAlpha(px) < 255 && qAlpha(px) > 0)
|
||||
img.setPixel(x,y, px | 0xff000000);
|
||||
}
|
||||
|
||||
// calculate accuratenear/far for all cameras
|
||||
std::vector<float> my_near;
|
||||
std::vector<float> my_far;
|
||||
calculateNearFarAccurate(md, &my_near, &my_far);
|
||||
|
||||
allcammaxdepth = -1000000;
|
||||
allcammindepth = 1000000;
|
||||
allcammaximagesize = -1000000;
|
||||
for(cam_ind = 0; cam_ind < md.rasterList.size(); cam_ind++)
|
||||
{
|
||||
if(my_far[cam_ind] > allcammaxdepth)
|
||||
allcammaxdepth = my_far[cam_ind];
|
||||
if(my_near[cam_ind] < allcammindepth)
|
||||
allcammindepth = my_near[cam_ind];
|
||||
|
||||
float imgdiag = sqrt(double(md.rasterList[cam_ind]->shot.Intrinsics.ViewportPx[0] * md.rasterList[cam_ind]->shot.Intrinsics.ViewportPx[1]));
|
||||
if (imgdiag > allcammaximagesize)
|
||||
allcammaximagesize = imgdiag;
|
||||
}
|
||||
|
||||
//-- cycle all cameras
|
||||
cam_ind = 0;
|
||||
foreach(RasterModel *raster, md.rasterList)
|
||||
{
|
||||
do_project = true;
|
||||
|
||||
// no drawing if camera not valid
|
||||
if(!raster->shot.IsValid())
|
||||
do_project = false;
|
||||
|
||||
// no drawing if raster is not active
|
||||
//if(!raster->shot.IsValid())
|
||||
// do_project = false;
|
||||
|
||||
if(do_project)
|
||||
{
|
||||
// delete & reinit rendermanager
|
||||
if(rendermanager != NULL)
|
||||
delete rendermanager;
|
||||
rendermanager = new RenderHelper();
|
||||
if( rendermanager->initializeGL(cb) != 0 )
|
||||
return false;
|
||||
Log("init GL");
|
||||
if( rendermanager->initializeMeshBuffers(model, cb) != 0 )
|
||||
return false;
|
||||
Log("init Buffers");
|
||||
|
||||
// render normal & depth
|
||||
rendermanager->renderScene(raster->shot, model, RenderHelper::NORMAL, my_near[cam_ind]*0.99, my_far[cam_ind]*1.01);
|
||||
|
||||
// If should be used silhouette weighting, it is needed to compute depth discontinuities
|
||||
// and per-pixel distance from detected borders on the entire image here
|
||||
// the weight is then applied later, per-vertex, when needed
|
||||
floatbuffer *silhouette_buff=NULL;
|
||||
float maxsildist = rendermanager->depth->sx + rendermanager->depth->sy;
|
||||
if(usesilhouettes)
|
||||
{
|
||||
silhouette_buff = new floatbuffer();
|
||||
silhouette_buff->init(rendermanager->depth->sx, rendermanager->depth->sy);
|
||||
|
||||
silhouette_buff->applysobel(rendermanager->depth);
|
||||
//sprintf(dumpFileName,"Abord%i.bmp",cam_ind);
|
||||
//silhouette_buff->dumpbmp(dumpFileName);
|
||||
|
||||
silhouette_buff->initborder(rendermanager->depth);
|
||||
//sprintf(dumpFileName,"Bbord%i.bmp",cam_ind);
|
||||
//silhouette_buff->dumpbmp(dumpFileName);
|
||||
|
||||
maxsildist = silhouette_buff->distancefield();
|
||||
//sprintf(dumpFileName,"Cbord%i.bmp",cam_ind);
|
||||
//silhouette_buff->dumpbmp(dumpFileName);
|
||||
}
|
||||
|
||||
for(texcount=0; texcount < texels.size(); texcount++)
|
||||
{
|
||||
pp = raster->shot.Project(texels[texcount].meshpoint);
|
||||
|
||||
//if inside image
|
||||
if(pp[0]>0 && pp[1]>0 && pp[0]<raster->shot.Intrinsics.ViewportPx[0] && pp[1]<raster->shot.Intrinsics.ViewportPx[1])
|
||||
{
|
||||
|
||||
depth = raster->shot.Depth(texels[texcount].meshpoint);
|
||||
pdepth = rendermanager->depth->getval(int(pp[0]), int(pp[1])); // rendermanager->depth[(int(pp[1]) * raster->shot.Intrinsics.ViewportPx[0]) + int(pp[0])];
|
||||
|
||||
if(depth <= (pdepth + eta))
|
||||
{
|
||||
// determine color
|
||||
QRgb pcolor = raster->currentPlane->image.pixel(pp[0],raster->shot.Intrinsics.ViewportPx[1] - pp[1]);
|
||||
// determine weight
|
||||
pweight = 1.0;
|
||||
|
||||
if(useangle)
|
||||
{
|
||||
Point3f pixnorm;
|
||||
Point3f viewaxis;
|
||||
|
||||
pixnorm = texels[texcount].meshnormal;
|
||||
pixnorm.Normalize();
|
||||
|
||||
viewaxis = raster->shot.GetViewPoint() - texels[texcount].meshpoint;
|
||||
viewaxis.Normalize();
|
||||
|
||||
float ang = abs(pixnorm * viewaxis);
|
||||
ang = min(1.0f, ang);
|
||||
|
||||
pweight *= ang;
|
||||
}
|
||||
|
||||
if(usedistance)
|
||||
{
|
||||
float distw = depth;
|
||||
distw = 1.0 - (distw - (allcammindepth*0.99)) / ((allcammaxdepth*1.01) - (allcammindepth*0.99));
|
||||
|
||||
pweight *= distw;
|
||||
pweight *= distw;
|
||||
}
|
||||
|
||||
if(useborders)
|
||||
{
|
||||
double xdist = 1.0 - (abs(pp[0] - (raster->shot.Intrinsics.ViewportPx[0] / 2.0)) / (raster->shot.Intrinsics.ViewportPx[0] / 2.0));
|
||||
double ydist = 1.0 - (abs(pp[1] - (raster->shot.Intrinsics.ViewportPx[1] / 2.0)) / (raster->shot.Intrinsics.ViewportPx[1] / 2.0));
|
||||
double borderw = min (xdist , ydist);
|
||||
|
||||
pweight *= borderw;
|
||||
}
|
||||
|
||||
if(usesilhouettes)
|
||||
{
|
||||
// here the silhouette weight is applied, but it is calculated before, on a per-image basis
|
||||
float silw = 1.0;
|
||||
silw = silhouette_buff->getval(int(pp[0]), int(pp[1])) / maxsildist;
|
||||
pweight *= silw;
|
||||
}
|
||||
|
||||
accums[texcount].weights += pweight;
|
||||
accums[texcount].acc_red += (qRed(pcolor) * pweight / 255.0);
|
||||
accums[texcount].acc_grn += (qGreen(pcolor) * pweight / 255.0);
|
||||
accums[texcount].acc_blu += (qBlue(pcolor) * pweight / 255.0);
|
||||
}
|
||||
}
|
||||
|
||||
} // end foreach texel
|
||||
cam_ind ++;
|
||||
|
||||
if(usesilhouettes)
|
||||
{
|
||||
delete silhouette_buff;
|
||||
}
|
||||
|
||||
} // end if(do_project)
|
||||
|
||||
} // end foreach camera
|
||||
|
||||
// for each texel.... divide accumulated values by weight and write to texture
|
||||
for(texcount=0; texcount < texels.size(); texcount++)
|
||||
{
|
||||
float texel_red = 1.0;
|
||||
float texel_green = 1.0;
|
||||
float texel_blue = 1.0;
|
||||
|
||||
if(accums[texcount].weights > 0.0)
|
||||
{
|
||||
texel_red = accums[texcount].acc_red / accums[texcount].weights;
|
||||
texel_green = accums[texcount].acc_grn / accums[texcount].weights;
|
||||
texel_blue = accums[texcount].acc_blu / accums[texcount].weights;
|
||||
}
|
||||
|
||||
img.setPixel(texels[texcount].texcoord.X(), img.height() - 1 - texels[texcount].texcoord.Y(), qRgba(texel_red*255.0, texel_green*255.0, texel_blue*255.0, 255));
|
||||
}
|
||||
|
||||
// PullPush
|
||||
cb(85, "Filling texture holes...");
|
||||
PullPush(img, qRgba(0,0,0,0));
|
||||
|
||||
|
||||
// Undo topology changes
|
||||
tri::UpdateTopology<CMeshO>::FaceFace(model->cm);
|
||||
tri::UpdateFlags<CMeshO>::FaceBorderFromFF(model->cm);
|
||||
|
||||
// Save texture
|
||||
cb(90, "Saving texture ...");
|
||||
CheckError(!img.save(filePath), "Texture file cannot be saved");
|
||||
Log( "Texture \"%s\" Created", filePath.toStdString().c_str());
|
||||
assert(QFile(filePath).exists());
|
||||
|
||||
// Assign texture
|
||||
model->cm.textures.clear();
|
||||
model->cm.textures.push_back(textName.toStdString());
|
||||
|
||||
// the mesh has to return to its original position
|
||||
tri::UpdatePosition<CMeshO>::Matrix(model->cm,Inverse(model->cm.Tr),true);
|
||||
tri::UpdateBounding<CMeshO>::Box(model->cm);
|
||||
|
||||
|
||||
} break;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -485,6 +826,9 @@ FilterColorProjectionPlugin::FilterClass FilterColorProjectionPlugin::getClass(Q
|
||||
case FP_MULTIIMAGETRIVIALPROJ:
|
||||
return FilterClass(Camera + VertexColoring);
|
||||
break;
|
||||
case FP_MULTIIMAGETRIVIALPROJTEXTURE:
|
||||
return FilterClass(Camera + Texture);
|
||||
break;
|
||||
default : assert(0);
|
||||
return MeshFilterInterface::Generic;
|
||||
}
|
||||
@ -498,6 +842,9 @@ int FilterColorProjectionPlugin::postCondition( QAction* a ) const{
|
||||
case FP_MULTIIMAGETRIVIALPROJ:
|
||||
return MeshModel::MM_VERTCOLOR;
|
||||
break;
|
||||
case FP_MULTIIMAGETRIVIALPROJTEXTURE:
|
||||
return MeshModel::MM_UNKNOWN;
|
||||
break;
|
||||
default: assert(0);
|
||||
return MeshModel::MM_NONE;
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ class FilterColorProjectionPlugin : public QObject, public MeshFilterInterface
|
||||
Q_INTERFACES(MeshFilterInterface)
|
||||
|
||||
public:
|
||||
enum { FP_SINGLEIMAGEPROJ, FP_MULTIIMAGETRIVIALPROJ };
|
||||
enum { FP_SINGLEIMAGEPROJ, FP_MULTIIMAGETRIVIALPROJ, FP_MULTIIMAGETRIVIALPROJTEXTURE };
|
||||
|
||||
FilterColorProjectionPlugin();
|
||||
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
include (../../shared.pri)
|
||||
|
||||
SOURCES = filter_color_projection.cpp\
|
||||
render_helper.cpp\
|
||||
floatbuffer.cpp\
|
||||
|
||||
|
||||
HEADERS = filter_color_projection.h\
|
||||
render_helper.h\
|
||||
floatbuffer.h\
|
||||
pushpull.h \
|
||||
rastering.h \
|
||||
|
||||
TARGET = filter_color_projection
|
||||
|
||||
@ -19,21 +19,21 @@
|
||||
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
|
||||
* for more details. *
|
||||
* *
|
||||
****************************************************************************/
|
||||
#ifndef FLOATBUFFER_H
|
||||
#define FLOATBUFFER_H
|
||||
|
||||
#include<vcg\math\shot.h>
|
||||
#include<vcg\math\histogram.h>
|
||||
|
||||
#include<QString>
|
||||
#include<QImage>
|
||||
****************************************************************************/
|
||||
#ifndef FLOATBUFFER_H
|
||||
#define FLOATBUFFER_H
|
||||
|
||||
#include<vcg\math\shot.h>
|
||||
#include<vcg\math\histogram.h>
|
||||
|
||||
#include<QString>
|
||||
#include<QImage>
|
||||
#include<queue>
|
||||
|
||||
using namespace std;
|
||||
using namespace vcg;
|
||||
|
||||
//---------------------------------- BMP headers for cross platform compilation
|
||||
|
||||
using namespace std;
|
||||
using namespace vcg;
|
||||
|
||||
//---------------------------------- BMP headers for cross platform compilation
|
||||
|
||||
/* Note: the magic number has been removed from the bmpfile_header structure
|
||||
since it causes alignment problems
|
||||
@ -71,56 +71,56 @@ typedef struct {
|
||||
uint32_t ncolors;
|
||||
uint32_t nimpcolors;
|
||||
} BITMAPINFOHEADER_X;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class colorp
|
||||
{
|
||||
public:
|
||||
|
||||
float r;
|
||||
float g;
|
||||
float b;
|
||||
|
||||
float w;
|
||||
int im;
|
||||
};
|
||||
|
||||
class floatbuffer
|
||||
{
|
||||
public:
|
||||
|
||||
float* data;
|
||||
|
||||
int sx,sy;
|
||||
|
||||
int loaded; // -1 not created
|
||||
// 1 loaded
|
||||
// 0 unloaded
|
||||
|
||||
QString filename; // when unloaded, offcore filename
|
||||
|
||||
floatbuffer(void);
|
||||
floatbuffer(floatbuffer *from);
|
||||
~floatbuffer(void);
|
||||
|
||||
int init(int sizex, int sizey);
|
||||
int destroy();
|
||||
|
||||
float getval(int xx, int yy);
|
||||
int setval(int xx, int yy, float val);
|
||||
|
||||
int fillwith(float val);
|
||||
|
||||
int applysobel(floatbuffer *from);
|
||||
int initborder(floatbuffer* zerofrom);
|
||||
int distancefield();
|
||||
|
||||
int dump(QString filename);
|
||||
int dumpbmp(QString filename, bool asitis = false);
|
||||
int dumpbmp2(QString filename);
|
||||
int dumppfm(QString filename);
|
||||
};
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class colorp
|
||||
{
|
||||
public:
|
||||
|
||||
float r;
|
||||
float g;
|
||||
float b;
|
||||
|
||||
float w;
|
||||
int im;
|
||||
};
|
||||
|
||||
class floatbuffer
|
||||
{
|
||||
public:
|
||||
|
||||
float* data;
|
||||
|
||||
int sx,sy;
|
||||
|
||||
int loaded; // -1 not created
|
||||
// 1 loaded
|
||||
// 0 unloaded
|
||||
|
||||
QString filename; // when unloaded, offcore filename
|
||||
|
||||
floatbuffer(void);
|
||||
floatbuffer(floatbuffer *from);
|
||||
~floatbuffer(void);
|
||||
|
||||
int init(int sizex, int sizey);
|
||||
int destroy();
|
||||
|
||||
float getval(int xx, int yy);
|
||||
int setval(int xx, int yy, float val);
|
||||
|
||||
int fillwith(float val);
|
||||
|
||||
int applysobel(floatbuffer *from);
|
||||
int initborder(floatbuffer* zerofrom);
|
||||
int distancefield();
|
||||
|
||||
int dump(QString filename);
|
||||
int dumpbmp(QString filename, bool asitis = false);
|
||||
int dumpbmp2(QString filename);
|
||||
int dumppfm(QString filename);
|
||||
};
|
||||
|
||||
|
||||
#endif // FLOATBUFFER_H
|
||||
149
src/meshlabplugins/filter_color_projection/pushpull.h
Normal file
149
src/meshlabplugins/filter_color_projection/pushpull.h
Normal file
@ -0,0 +1,149 @@
|
||||
/****************************************************************************
|
||||
* 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. *
|
||||
* *
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef _PUSHPULL_H
|
||||
#define _PUSHPULL_H
|
||||
|
||||
typedef unsigned char byte;
|
||||
|
||||
#include <QtGui>
|
||||
|
||||
namespace vcg
|
||||
{
|
||||
/* pull push filling algorithm */
|
||||
|
||||
int mean4w(int p1,byte w1,int p2,byte w2,int p3,byte w3,int p4,byte w4)
|
||||
{
|
||||
int result =(p1*int(w1) + p2*int(w2) +p3*int(w3) + p4*int(w4) )
|
||||
/ ( int(w1)+int(w2)+int(w3)+int(w4) ) ;
|
||||
return result;
|
||||
}
|
||||
|
||||
QRgb mean4Pixelw(QRgb p1,byte w1,QRgb p2,byte w2,QRgb p3,byte w3,QRgb p4,byte w4)
|
||||
{
|
||||
int r= mean4w(qRed(p1),w1,qRed(p2),w2,qRed(p3),w3,qRed(p4),w4);
|
||||
int g= mean4w(qGreen(p1),w1,qGreen(p2),w2,qGreen(p3),w3,qGreen(p4),w4);
|
||||
int b= mean4w(qBlue(p1),w1,qBlue(p2),w2,qBlue(p3),w3,qBlue(p4),w4);
|
||||
int a= mean4w(qAlpha(p1),w1,qAlpha(p2),w2,qAlpha(p3),w3,qAlpha(p4),w4);
|
||||
|
||||
return qRgba(r,g,b,a);
|
||||
|
||||
}
|
||||
|
||||
// Genera una mipmap pesata
|
||||
void PullPushMip( QImage & p, QImage & mip, QRgb bkcolor )
|
||||
{
|
||||
assert(p.width()/2==mip.width());
|
||||
assert(p.height()/2==mip.height());
|
||||
byte w1,w2,w3,w4;
|
||||
int x,y;
|
||||
for(y=0;y<mip.height();++y)
|
||||
for(x=0;x<mip.width();++x)
|
||||
{
|
||||
if(p.pixel(x*2 ,y*2 )==bkcolor) w1=0; else w1=255;
|
||||
if(p.pixel(x*2+1,y*2 )==bkcolor) w2=0; else w2=255;
|
||||
if(p.pixel(x*2 ,y*2+1)==bkcolor) w3=0; else w3=255;
|
||||
if(p.pixel(x*2+1,y*2+1)==bkcolor) w4=0; else w4=255;
|
||||
if(w1+w2+w3+w4>0 )
|
||||
mip.setPixel(x, y, mean4Pixelw(
|
||||
p.pixel(x*2 ,y*2 ),w1,
|
||||
p.pixel(x*2+1,y*2 ),w2,
|
||||
p.pixel(x*2 ,y*2+1),w3,
|
||||
p.pixel(x*2+1,y*2+1),w4 ));
|
||||
}
|
||||
}
|
||||
|
||||
// interpola a partire da una mipmap
|
||||
void PullPushFill( QImage & p, QImage & mip, QRgb bkg )
|
||||
{
|
||||
assert(p.width()/2==mip.width());
|
||||
assert(p.height()/2==mip.height());
|
||||
// byte w1,w2,w3,w4;
|
||||
int x,y;
|
||||
for(y=0;y<mip.height();++y)
|
||||
for(x=0;x<mip.width();++x)
|
||||
{
|
||||
if(p.pixel(x*2 ,y*2)==bkg)
|
||||
p.setPixel(x*2 ,y*2 ,mean4Pixelw( mip.pixel(x ,y ) , byte(144),
|
||||
(x>0 ? mip.pixel(x-1,y ) : bkg), (x>0 ? byte( 48) : 0),
|
||||
(y>0 ? mip.pixel(x ,y-1) : bkg), (y>0 ? byte( 48) : 0),
|
||||
((x>0 && y>0 )? mip.pixel(x-1,y-1) : bkg), ((x>0 && y>0 )? byte( 16) : 0)));
|
||||
if(p.pixel(x*2+1,y*2 )==bkg)
|
||||
p.setPixel(x*2+1,y*2 ,mean4Pixelw(mip.pixel(x ,y ) ,byte(144),
|
||||
(x<mip.width()-1 ? mip.pixel(x+1,y) : bkg), (x<mip.width()-1 ? byte( 48) : 0),
|
||||
(y>0 ? mip.pixel(x ,y-1) : bkg), (y>0 ? byte( 48) : 0),
|
||||
((x<mip.width()-1 && y>0) ? mip.pixel(x+1,y-1) : bkg), ((x<mip.width()-1 && y>0) ? byte( 16) : 0)));
|
||||
if(p.pixel(x*2 ,y*2+1)==bkg)
|
||||
p.setPixel(x*2 ,y*2+1, mean4Pixelw( mip.pixel(x ,y ), byte(144),
|
||||
(x>0 ? mip.pixel(x-1,y ) : bkg), (x>0 ? byte( 48) : 0),
|
||||
(y<mip.height()-1 ? mip.pixel(x ,y+1) : bkg), (y<mip.height()-1 ? byte( 48) : 0),
|
||||
((x>0 && y<mip.height()-1) ? mip.pixel(x-1,y+1) : bkg), ((x>0 && y<mip.height()-1 )? byte( 16) : 0)));
|
||||
if(p.pixel(x*2+1,y*2+1)==bkg)
|
||||
p.setPixel(x*2+1,y*2+1, mean4Pixelw(mip.pixel(x ,y ), byte(144),
|
||||
(x<mip.width()-1 ? mip.pixel(x+1,y ) : bkg), (x<mip.width()-1 ? byte( 48) : 0),
|
||||
(y<mip.height()-1 ? mip.pixel(x ,y+1) : bkg), ( y<mip.height()-1 ? byte( 48) : 0),
|
||||
((x<mip.width()-1 && y<mip.height()-1) ? mip.pixel(x+1,y+1) : bkg), ((x<mip.width()-1 && y<mip.height()-1) ? byte( 16) : 0)));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PullPush( QImage & p, QRgb bkcolor )
|
||||
{
|
||||
int i=0;
|
||||
std::vector<QImage> mip(16);
|
||||
int div=2;
|
||||
int miplev=0;
|
||||
|
||||
// pull phase create the mipmap
|
||||
while(1){
|
||||
mip[miplev]= QImage(p.width()/div,p.height()/div,p.format());
|
||||
mip[miplev].fill(bkcolor);
|
||||
div*=2;
|
||||
if(miplev>0) PullPushMip(mip[miplev-1],mip[miplev],bkcolor);
|
||||
else PullPushMip(p,mip[miplev],bkcolor);
|
||||
if(mip[miplev].width()<=4 || mip[miplev].height()<=4) break;
|
||||
++miplev;
|
||||
}
|
||||
miplev++;
|
||||
#ifdef _PUSHPULL_DEBUG
|
||||
for(int k=0;k<miplev;k++) {
|
||||
char buf[100];sprintf(buf,"mip%02i.png",k);
|
||||
mip[k].Save(buf);
|
||||
}
|
||||
#endif
|
||||
// push phase: refill
|
||||
for(i=miplev-1;i>=0;--i){
|
||||
if(i>0) PullPushFill(mip[i-1],mip[i],bkcolor);
|
||||
else PullPushFill(p,mip[i],bkcolor);
|
||||
}
|
||||
|
||||
#ifdef _PUSHPULL_DEBUG
|
||||
for(k=0;k<miplev;k++) {
|
||||
char buf[100];sprintf(buf,"mipfill%02i.png",k);
|
||||
mip[k].Save(buf);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} // End namespace
|
||||
#endif
|
||||
212
src/meshlabplugins/filter_color_projection/rastering.h
Normal file
212
src/meshlabplugins/filter_color_projection/rastering.h
Normal file
@ -0,0 +1,212 @@
|
||||
/****************************************************************************
|
||||
* 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. *
|
||||
* *
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef _RASTERING_H
|
||||
#define _RASTERING_H
|
||||
|
||||
#include <QtGui>
|
||||
#include <common/interfaces.h>
|
||||
#include <vcg/complex/algorithms/point_sampling.h>
|
||||
#include <vcg/space/triangle2.h>
|
||||
|
||||
|
||||
// texel descriptor---------------------------------
|
||||
|
||||
typedef struct{
|
||||
|
||||
Point2i texcoord;
|
||||
|
||||
Point3f meshpoint;
|
||||
Point3f meshnormal;
|
||||
|
||||
} TexelDesc;
|
||||
|
||||
typedef struct{
|
||||
|
||||
float weights;
|
||||
float acc_red;
|
||||
float acc_grn;
|
||||
float acc_blu;
|
||||
|
||||
} TexelAccum;
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
|
||||
//----------- TEXTURE FILLING SAMPLER ------------------------------
|
||||
/*
|
||||
class TexFillSampler
|
||||
{
|
||||
public:
|
||||
TexFillSampler(CMeshO* _m){m=_m; uvSpaceFlag = false; qualitySampling=false; tex=0;};
|
||||
CMeshO *m;
|
||||
QImage* tex;
|
||||
int texSamplingWidth;
|
||||
int texSamplingHeight;
|
||||
bool uvSpaceFlag;
|
||||
bool qualitySampling;
|
||||
texwork *thiswork;
|
||||
|
||||
void AddVert(const MyMesh::VertexType &p)
|
||||
{
|
||||
}
|
||||
|
||||
void AddFace(const MyMesh::FaceType &f, MyMesh::CoordType p)
|
||||
{
|
||||
}
|
||||
|
||||
void AddTextureSample(const MyMesh::FaceType &f, const MyMesh::CoordType &p, const Point2i &tp, float edgeDist=0)
|
||||
{
|
||||
double rr,gg,bb;
|
||||
double count;
|
||||
QColor col;
|
||||
float weight;
|
||||
Point2f ppoint;
|
||||
Point3f pray;
|
||||
float dpt,ref;
|
||||
|
||||
Point3f meshpoint = f.P(0)*p[0] + f.P(1)*p[1] +f.P(2)*p[2];
|
||||
Point3f meshnorm = f.V(0)->N()*p[0] + f.V(1)->N()*p[1] +f.V(2)->N()*p[2];
|
||||
|
||||
rr = gg = bb = 0;
|
||||
count = 0;
|
||||
for(int camit=0; camit<thiswork->camnum; camit++)
|
||||
{
|
||||
ppoint = thiswork->immagini[camit].imshot->Project(meshpoint);
|
||||
// pray is the vector from the point-to-be-colored to the camera center
|
||||
pray = (thiswork->immagini[camit].imshot->Extrinsics.Tra() - meshpoint).Normalize();
|
||||
|
||||
|
||||
if((ppoint[0] > 0) && (ppoint[0] < thiswork->immagini[camit].imshot->Intrinsics.ViewportPx.X()) &&
|
||||
(ppoint[1] > 0) && (ppoint[1] < thiswork->immagini[camit].imshot->Intrinsics.ViewportPx.Y()))
|
||||
if((meshnorm.dot(-thiswork->immagini[camit].imshot->Axis(2))) <= 0.0)
|
||||
if((pray.dot(-thiswork->immagini[camit].imshot->Axis(2))) <= 0.0)
|
||||
{
|
||||
|
||||
dpt = (thiswork->immagini[camit].imshot->Depth(meshpoint) - thiswork->depth_eta);
|
||||
ref = thiswork->immagini[camit].camdepth.getval(ppoint[0],ppoint[1]);
|
||||
|
||||
if(dpt < ref)
|
||||
{
|
||||
weight = thiswork->immagini[camit].camweight.getval(ppoint[0],ppoint[1]);
|
||||
ppoint[1] = thiswork->immagini[camit].imshot->Intrinsics.ViewportPx.Y() - ppoint[1];
|
||||
col = thiswork->immagini[camit].impicture->pixel(ppoint[0],ppoint[1]);
|
||||
|
||||
if((col.red() + col.green() + col.blue()) > 0)
|
||||
{
|
||||
rr += col.red()*weight; gg += col.green()*weight; bb += col.blue()*weight;
|
||||
count += weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(count > 0)
|
||||
{
|
||||
rr = (double)rr/count;
|
||||
gg = (double)gg/count;
|
||||
bb = (double)bb/count;
|
||||
|
||||
// updating color mean
|
||||
if(thiswork->usemean)
|
||||
{
|
||||
thiswork->meanrr = (thiswork->meanrr * 10.0 + rr) / 11.0;
|
||||
thiswork->meangg = (thiswork->meangg * 10.0 + gg) / 11.0;
|
||||
thiswork->meanbb = (thiswork->meanbb * 10.0 + bb) / 11.0;
|
||||
}
|
||||
}
|
||||
else // default color
|
||||
{
|
||||
rr = thiswork->meanrr;
|
||||
gg = thiswork->meangg;
|
||||
bb = thiswork->meanbb;
|
||||
}
|
||||
|
||||
//--------------------------
|
||||
|
||||
tex->setPixel(tp[0], texSamplingHeight - tp[1], qRgb(rr,gg,bb));
|
||||
|
||||
|
||||
|
||||
}
|
||||
}; // end class TexFillSampler
|
||||
//------------------------------------------------------------------
|
||||
|
||||
*/
|
||||
|
||||
|
||||
class TexFillerSampler
|
||||
{
|
||||
public:
|
||||
|
||||
QImage &trgImg;
|
||||
|
||||
vector<TexelDesc> *texelspointer; // list of active texels to be filled
|
||||
vector<TexelAccum> *accumpointer; // list of color + weight accumulators
|
||||
|
||||
// Callback stuff
|
||||
vcg::CallBackPos *cb;
|
||||
const CMeshO::FaceType *currFace;
|
||||
int faceNo, faceCnt, start, offset;
|
||||
|
||||
|
||||
TexFillerSampler(QImage &_img) : trgImg(_img) {}
|
||||
|
||||
void InitCallback(vcg::CallBackPos *_cb, int _faceNo, int _start=0, int _offset=100)
|
||||
{
|
||||
assert(_faceNo > 0);
|
||||
assert(_start>=0);
|
||||
assert(_offset>=0 && _offset <= 100-_start);
|
||||
cb = _cb;
|
||||
faceNo = _faceNo;
|
||||
faceCnt = 0;
|
||||
start = _start;
|
||||
offset = _offset;
|
||||
currFace = NULL;
|
||||
}
|
||||
|
||||
// expects points outside face (affecting face color) with edge distance > 0
|
||||
void AddTextureSample(const CMeshO::FaceType &f, const CMeshO::CoordType &p, const vcg::Point2i &tp, float edgeDist= 0.0)
|
||||
{
|
||||
Point3f meshpoint = f.P(0)*p[0] + f.P(1)*p[1] +f.P(2)*p[2];
|
||||
Point3f meshnorm = (f.V(0)->N()*p[0] + f.V(1)->N()*p[1] + f.V(2)->N()*p[2]).Normalize();
|
||||
|
||||
TexelDesc newtexel;
|
||||
newtexel.texcoord = tp;
|
||||
newtexel.meshpoint = meshpoint;
|
||||
newtexel.meshnormal = meshnorm;
|
||||
|
||||
TexelAccum newaccum;
|
||||
newaccum.weights = 0.0;
|
||||
newaccum.acc_red = 0.0;
|
||||
newaccum.acc_grn = 0.0;
|
||||
newaccum.acc_blu = 0.0;
|
||||
|
||||
texelspointer->push_back(newtexel);
|
||||
accumpointer->push_back(newaccum);
|
||||
}
|
||||
}; // end class TexFillerSampler
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
Loading…
x
Reference in New Issue
Block a user