void Arc3DModel::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);
            }
}
/// Apply the hand drawn mask image
bool Arc3DModel::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 Arc3DModel::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;
                }
        }
}
/* 
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::setArc3DReconstruction(Arc3DReconstruction *_er)
{
  // if the epoch reconstruction has not changed do nothing
  if(erCreated == _er->created) 
  {
    er=_er;
    return;
  }
  
  er=_er;
  erCreated=er->created;
  ui.infoLabel->setText(er->name + " - " + er->author + " - " + er->created);
  
  ui.imageTableWidget->clear();
  ui.imageTableWidget->setRowCount(er->modelList.size());
  ui.imageTableWidget->setColumnCount(4);
  //imageTableWidget->setColumnWidth (1,64);
  ui.imageTableWidget->setSelectionBehavior (QAbstractItemView::SelectRows);
  ui.imageTableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
  ui.imageTableWidget->setMinimumWidth(64*8);
  
  ui.rangeLabel->setPixmap(generateColorRamp());
  ui.rangeLabel->setMaximumHeight(10);
	ui.rangeLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
	ui.rangeLabel->setScaledContents(true);
	
  ui.minCountSlider->setMaximumHeight(12);
    
  int i;
  for(i=0; i<er->modelList.size() ;++i)
  {
        
    QString ThumbImgName=Arc3DModel::ThumbName(er->modelList[i].textureName);
    QString ThumbCntName=Arc3DModel::ThumbName(er->modelList[i].countName);

    ui.imageTableWidget->setRowHeight (i,64);
    QTableWidgetItem *emptyHeaderItem = new QTableWidgetItem(i*2);
    QTableWidgetItem *texNameHeaderItem = new QTableWidgetItem(er->modelList[i].textureName,i);
    ui.imageTableWidget->setItem(i, 0, texNameHeaderItem);
    ui.imageTableWidget->setItem(i, 1, emptyHeaderItem);
    QLabel *imageLabel = new QLabel(ui.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(Arc3DModel::ThumbName(er->modelList[i].textureName)));
    ui.imageTableWidget->setCellWidget(i,1,imageLabel);
    if(QFile::exists(er->modelList[i].maskName))
    {
      QTableWidgetItem *emptyHeaderItem = new QTableWidgetItem(i*4);
      ui.imageTableWidget->setItem(i, 2, emptyHeaderItem);
      QLabel *maskLabel = new QLabel(ui.imageTableWidget);
      maskLabel->setPixmap(QPixmap(er->modelList[i].maskName).scaledToHeight(64));
      ui.imageTableWidget->setCellWidget(i,2,maskLabel);     
    }
    else
    {
      QTableWidgetItem *maskHeaderItem = new QTableWidgetItem(QString("double click for\nediting the mask"),i*3);
      ui.imageTableWidget->setItem(i, 2, maskHeaderItem);
    }

    if(!QFile::exists(ThumbCntName))
      {
        CharImage chi;
        bool ret=chi.Open(er->modelList[i].countName.toUtf8().data());
        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(ui.imageTableWidget);
    countLabel->setPixmap(QPixmap(ThumbCntName));
    QPixmap tmp(ThumbCntName);
    if(tmp.isNull())
       QMessageBox::warning(this,"Error in Thumb creation",QString("Null Pixmap '%1'").arg(ThumbCntName));
    ui.imageTableWidget->setCellWidget(i,3,countLabel);
  }  

    
show(); // necessary to make the size of the image preview correct.
ui.imageTableWidget->setItemSelected(ui.imageTableWidget->item(0,0),true);    
ui.imageTableWidget->setItemSelected(ui.imageTableWidget->item(0,1),true);    
ui.imageTableWidget->setItemSelected(ui.imageTableWidget->item(0,2),true);    


}
Point3m Arc3DModel::TraCorrection(CMeshO &m, int subsampleFactor, int minCount, int smoothSteps)
{
    FloatImage depthImgf;
    CharImage countImgc;
    depthImgf.Open(depthName.toUtf8().data());
    countImgc.Open(countName.toUtf8().data());

    QImage TextureImg;
    TextureImg.load(textureName);

    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");

    float depthThr = ComputeDepthJumpThr(depthSubf,0.8f);
    for(int ii=0;ii<smoothSteps;++ii)
        Laplacian2(depthSubf,countSubf,minCount,FeatureMask,depthThr);

    vcg::tri::Grid<CMeshO>(m,depthSubf.w,depthSubf.h,depthImgf.w,depthImgf.h,&*depthSubf.v.begin());

    // 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).
    ComputeDepthJumpThr(depthSubf,0.95f);

    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.toUtf8().data());

        CMeshO::VertexIterator vi;
        Matrix33d Rinv= Inverse(cam.R);
        Point3m correction(0.0,0.0,0.0);
        int numSamp=0;

        for(vi=m.vert.begin();vi!=m.vert.end();++vi)if(!(*vi).IsD())
        {
            Point3m in=(*vi).P();
            Point3d out;
            correction+=cam.DepthTo3DPoint(in[0], in[1], in[2], out);
            numSamp++;

        }
        if (numSamp!=0)
            correction/=(double)numSamp;

        return correction;

}
bool Arc3DModel::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;
    clock();
    depthImgf.Open(depthName.toUtf8().data());
    countImgc.Open(countName.toUtf8().data());

    QImage TextureImg;
    TextureImg.load(textureName);
    clock();

    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");

    clock();

    float depthThr = ComputeDepthJumpThr(depthSubf,0.8f);
    for(int ii=0;ii<smoothSteps;++ii)
        Laplacian2(depthSubf,countSubf,minCount,FeatureMask,depthThr);

    clock();

    vcg::tri::Grid<CMeshO>(m,depthSubf.w,depthSubf.h,depthImgf.w,depthImgf.h,&*depthSubf.v.begin());

    clock();


    // 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.toUtf8().data());

        CMeshO::VertexIterator vi;
        Matrix33d Rinv= Inverse(cam.R);

        for(vi=m.vert.begin();vi!=m.vert.end();++vi)if(!(*vi).IsD())
        {
            Point3m 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]));
            vcg::Color4b tmpcol(qRed(c),qGreen(c),qBlue(c),0);
            (*vi).C().Import(tmpcol);
            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;
        }

        clock();

        CMeshO::FaceIterator fi;
        Point3m CameraPos = Point3m::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
            {
                Point3m n=vcg::TriangleNormal(*fi);
                n.Normalize();
                Point3m dir=CameraPos-vcg::Barycenter(*fi);
                dir.Normalize();
                if(dir.dot(n) < minAngleCos)
                {
                    (*fi).SetD();
                    --m.fn;
                }
            }
        }

        tri::Clean<CMeshO>::RemoveUnreferencedVertex(m);
        clock();

        Matrix44m scaleMat;
        scaleMat.SetScale(scalingFactor,scalingFactor,scalingFactor);
        vcg::tri::UpdatePosition<CMeshO>::Matrix(m, scaleMat);

        return true;
}
void Arc3DModel::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");
}