first working version of the project-to-texture filter

This commit is contained in:
Marco Callieri mcallieri 2011-06-24 12:13:42 +00:00
parent 2990c8a34f
commit 0b98dceb5a
6 changed files with 780 additions and 71 deletions

View File

@ -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;
}

View File

@ -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();

View File

@ -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

View File

@ -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

View 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

View 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