From e75c4bd820dd57b2bfc8dff1019ebbdba4a3515c Mon Sep 17 00:00:00 2001 From: Paolo Cignoni cignoni Date: Wed, 6 Apr 2011 20:55:37 +0000 Subject: [PATCH] Added Watermarking filter --- .../filter_watermark/ERF.h | 107 +++++++ .../filter_watermark/filter_watermark.cpp | 286 ++++++++++++++++++ .../filter_watermark/filter_watermark.h | 57 ++++ .../filter_watermark/filter_watermark.pro | 9 + .../filter_watermark/utilsWatermark.cpp | 142 +++++++++ .../filter_watermark/utilsWatermark.h | 51 ++++ 6 files changed, 652 insertions(+) create mode 100644 src/plugins_experimental/filter_watermark/ERF.h create mode 100644 src/plugins_experimental/filter_watermark/filter_watermark.cpp create mode 100644 src/plugins_experimental/filter_watermark/filter_watermark.h create mode 100644 src/plugins_experimental/filter_watermark/filter_watermark.pro create mode 100644 src/plugins_experimental/filter_watermark/utilsWatermark.cpp create mode 100644 src/plugins_experimental/filter_watermark/utilsWatermark.h diff --git a/src/plugins_experimental/filter_watermark/ERF.h b/src/plugins_experimental/filter_watermark/ERF.h new file mode 100644 index 000000000..01ace3396 --- /dev/null +++ b/src/plugins_experimental/filter_watermark/ERF.h @@ -0,0 +1,107 @@ +#include +#include +#include + +template +inline T SQR(const T a) {return a*a;} + + +struct Erf { + static const int ncof=28; + static const double cof[28]; + + + inline double erf(double x) { + if (x >=0.) return 1.0 - erfccheb(x); + else return erfccheb(-x) - 1.0; + } + + inline double erfc(double x) { + if (x >= 0.) return erfccheb(x); + else return 2.0 - erfccheb(-x); + } + + double erfccheb(double z){ + int j; + double t,ty,tmp,d=0.,dd=0.; + if (z < 0.) throw("erfccheb requires nonnegative argument"); + t = 2./(2.+z); + ty = 4.*t - 2.; + for (j=ncof-1;j>0;j--) { + tmp = d; + d = ty*d - dd + cof[j]; + dd = tmp; + } + return t*exp(-z*z + 0.5*(cof[0] + ty*d) - dd); + } + + double inverfc(double p) { + double x,err,t,pp; + if (p >= 2.0) return -100.; + if (p <= 0.0) return 100.; + pp = (p < 1.0)? p : 2. - p; + t = sqrt(-2.*log(pp/2.)); + x = -0.70711*((2.30753+t*0.27061)/(1.+t*(0.99229+t*0.04481)) - t); + for (int j=0;j<2;j++) { + err = erfc(x) - pp; + x += err/(1.12837916709551257*exp(-SQR(x))-x*err); + } + return (p < 1.0? x : -x); + } + + inline double inverf(double p) {return inverfc(1.-p);} + +}; + +const double Erf::cof[28] = {-1.3026537197817094, 6.4196979235649026e-1, + 1.9476473204185836e-2,-9.561514786808631e-3,-9.46595344482036e-4, + 3.66839497852761e-4,4.2523324806907e-5,-2.0278578112534e-5, + -1.624290004647e-6,1.303655835580e-6,1.5626441722e-8,-8.5238095915e-8, + 6.529054439e-9,5.059343495e-9,-9.91364156e-10,-2.27365122e-10, + 9.6467911e-11, 2.394038e-12,-6.886027e-12,8.94487e-13, 3.13092e-13, + -1.12708e-13,3.81e-16,7.106e-15,-1.523e-15,-9.4e-17,1.21e-16,-2.8e-17}; +double erfcc(const double x) +{ + double t,z=fabs(x),ans; + t=2./(2.+z); + ans=t*exp(-z*z-1.26551223+t*(1.00002368+t*(0.37409196+t*(0.09678418+ + t*(-0.18628806+t*(0.27886807+t*(-1.13520398+t*(1.48851587+ + t*(-0.82215223+t*0.17087277))))))))); + return (x >= 0.0 ? ans : 2.0-ans); +} +struct Normaldist : Erf { + double mu, sig; + Normaldist(double mmu = 0., double ssig = 1.) : mu(mmu), sig(ssig) { + if (sig <= 0.) throw("bad sig in Normaldist"); + } + double p(double x) { + return (0.398942280401432678/sig)*exp(-0.5*SQR((x-mu)/sig)); + } + double cdf(double x) { + return 0.5*erfc(-0.707106781186547524*(x-mu)/sig); + } + double invcdf(double p) { + if (p <= 0. || p >= 1.) throw("bad p in Normaldist"); + return -1.41421356237309505*sig*inverfc(2.*p)+mu; + } +}; +struct Lognormaldist : Erf { + double mu, sig; + Lognormaldist(double mmu = 0., double ssig = 1.) : mu(mmu), sig(ssig) { + if (sig <= 0.) throw("bad sig in Lognormaldist"); + } + double p(double x) { + if (x < 0.) throw("bad x in Lognormaldist"); + if (x == 0.) return 0.; + return (0.398942280401432678/(sig*x))*exp(-0.5*SQR((log(x)-mu)/sig)); + } + double cdf(double x) { + if (x < 0.) throw("bad x in Lognormaldist"); + if (x == 0.) return 0.; + return 0.5*erfc(-0.707106781186547524*(log(x)-mu)/sig); + } + double invcdf(double p) { + if (p <= 0. || p >= 1.) throw("bad p in Lognormaldist"); + return exp(-1.41421356237309505*sig*inverfc(2.*p)+mu); + } +}; diff --git a/src/plugins_experimental/filter_watermark/filter_watermark.cpp b/src/plugins_experimental/filter_watermark/filter_watermark.cpp new file mode 100644 index 000000000..6b0b89c3e --- /dev/null +++ b/src/plugins_experimental/filter_watermark/filter_watermark.cpp @@ -0,0 +1,286 @@ +/**************************************************************************** +* 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. * +* * +****************************************************************************/ + +#include "filter_watermark.h" +#include "utilsWatermark.h" +#include + + +using namespace vcg; +using namespace std; + + +// Constructor usually performs only two simple tasks of filling the two lists +// - typeList: with all the possible id of the filtering actions +// - actionList with the corresponding actions. If you want to add icons to your filtering actions you can do here by construction the QActions accordingly + +WatermarkPlugin::WatermarkPlugin() +{ + typeList << FP_EMBED_WATERMARK << FP_DECODE_WATERMARK; + + foreach(FilterIDType tt , types()) + actionList << new QAction(filterName(tt), this); +} + +// ST() must return the very short string describing each filtering action +// (this string is used also to define the menu entry) +QString WatermarkPlugin::filterName(FilterIDType filterId) const +{ + switch(filterId) { + case FP_EMBED_WATERMARK : return QString("Watermark embedding on 3D points"); + case FP_DECODE_WATERMARK : return QString("Watermark decoding on 3D points"); + default : assert(0); + } + return QString(); +} + +// Info() must return the longer string describing each filtering action +// (this string is used in the About plugin dialog) + QString WatermarkPlugin::filterInfo(FilterIDType filterId) const +{ + switch(filterId) { + case FP_EMBED_WATERMARK : return QString("Embed a watermark on the model with a given strength depending on a string code. The string code can be a mix of letters, numbers and special characters. A missing alarm probability is guaranteed according to a prefixed false alarm probability.
See:
F. Uccheddu, M. Corsini and M. Barni
Wavelet-Based Blind Watermarking of 3D Models
ACM Multimedia and Security Workshop, 2004, pp. 143-154.

"); + case FP_DECODE_WATERMARK : return QString("Decode a watermark potentially embedded on the model and depending on a string code. The string code is a mix of letters, numbers and special characters."); + default : assert(0); + } + return QString("Unknown Filter"); +} + +// The FilterClass describes in which generic class of filters it fits. +// This choice affect the submenu in which each filter will be placed +// More than a single class can be choosen. +WatermarkPlugin::FilterClass WatermarkPlugin::getClass(QAction *a) +{ + switch(ID(a)) + { + case FP_EMBED_WATERMARK : return MeshFilterInterface::Smoothing; + case FP_DECODE_WATERMARK : return MeshFilterInterface::Smoothing; + default : assert(0); + } + return MeshFilterInterface::Generic; +} + +// This function define the needed parameters for each filter. Return true if the filter has some parameters +// it is called every time, so you can set the default value of parameters according to the mesh +// For each parameter you need to define, +// - the name of the parameter, +// - the string shown in the dialog +// - the default value +// - a possibly long string describing the meaning of that parameter (shown as a popup help in the dialog) +void WatermarkPlugin::initParameterSet(QAction *action,MeshModel &m, RichParameterSet & parlst) +{ + switch(ID(action)) { + case FP_EMBED_WATERMARK : + case FP_DECODE_WATERMARK : + parlst.addParam(new RichFloat("Power_gamma",(float)0.001,"Power value","Power value for the watermark; trade-off between visual quality and robustness. The default is 0.001")); + parlst.addParam(new RichString("String_code","STRING_CODE", "Embedding code","The code to embed into the model. It can be an alphanumeric string")); + break; + default : assert(0); + } +} + + + + +// The Real Core Function doing the actual mesh processing. +// Move Vertex of a random quantity +bool WatermarkPlugin::applyFilter(QAction * filter, MeshDocument &md, RichParameterSet & par, vcg::CallBackPos *cb) +{ + + MeshModel &m=*md.mm(); + const float max_gamma = par.getFloat("Power_gamma"); + const QString string_code = par.getString("String_code"); + + utilsWatermark utilw; + double cellTablesize = 1.0; + int nsTheta = 360.0/cellTablesize; + int nsPhi = 180.0/cellTablesize; + + unsigned int seed = utilw.ComputeSeed(string_code); + + vector > watermarkTable = utilw.CreateWatermarkTable(seed); + + switch(ID(filter)) + { + case FP_EMBED_WATERMARK: + { + int indexThetaRef,indexPhiRef; + + double DELTAR; + + //for(unsigned int i = 0; i< m.cm.vert.size(); i++) + for(CMeshO::VertexIterator vi=m.cm.vert.begin();vi!=m.cm.vert.end();++vi) + { + double sphR,sphTheta,sphPhi; + + double dx = (vi)->P()[0]; + double dy = (vi)->P()[1]; + double dz = (vi)->P()[2]; + + // Passo 1 : Trasformazione in coordinate sferiche della wavelet + utilw.cartesian2sph( vi->P()[0], vi->P()[1],vi->P()[2], sphR, sphTheta, sphPhi ); + + // Puņ accadere, basta pensare al vertice (-1.0,0.0,0.0) + if ( (sphTheta+180.0) >= 360.0) + sphTheta -= 360.0; + + double dd = PHIEXCLUSION_DEGREE; + if ((sphR > 0.000000000001)&&(sphPhi < 180.0-PHIEXCLUSION_DEGREE)&&(sphPhi > PHIEXCLUSION_DEGREE)) + { + // Passo 2 : Mappatura sul Tabella + + nsPhi = 180.0/cellTablesize; + nsTheta = 360.0/cellTablesize; + + indexThetaRef = (int)utilw.round( (sphTheta+180.0) ); + indexPhiRef = (int)utilw.round( (179.0/180.0)*sphPhi ); + + if (indexThetaRef == nsTheta) + indexThetaRef = 0; + + DELTAR = watermarkTable[indexPhiRef][indexThetaRef]; + + //watermark on vertex module + sphR = sphR + DELTAR*max_gamma; + + if (sphR < 0.00000000001) + sphR = 0.00000000001; + + utilw.sph2cartesian( sphR,sphTheta,sphPhi, (vi)->P()[0], (vi)->P()[1], (vi)->P()[2] ); + double dx = (vi)->P()[0]; + double dy = (vi)->P()[1]; + double dz = (vi)->P()[2]; + + int dd=0; + } + + } + vcg::tri::UpdateBounding::Box(m.cm); + + + } break; + case FP_DECODE_WATERMARK: + { + double Pf = 0.01; + double Pm; + + double sphR,sphTheta,sphPhi; + + int indexThetaRef,indexPhiRef; + + double DELTAR; + + vector corr; + vector sphRi; + + int n=0; + + for(CMeshO::VertexIterator vi=m.cm.vert.begin();vi!=m.cm.vert.end();++vi) + { + + // Passo 1 : Trasformazione in coordinate sferiche della wavelet + utilw.cartesian2sph( vi->P()[0], vi->P()[1],vi->P()[2], sphR, sphTheta, sphPhi ); + + // Puņ accadere, basta pensare al vertice (-1.0,0.0,0.0) + if ( (sphTheta+180.0) >= 360.0) + sphTheta -= 360.0; + + if ((sphR > 0.000000000001)&&(sphPhi < 180.0-PHIEXCLUSION_DEGREE)&&(sphPhi > PHIEXCLUSION_DEGREE)) + { + nsPhi = 180.0/cellTablesize; + nsTheta = 360.0/cellTablesize; + + indexThetaRef = (int)utilw.round( (sphTheta+180.0) ); + indexPhiRef = (int)utilw.round( (179.0/180.0)*sphPhi ); + + if (indexThetaRef == nsTheta) + indexThetaRef = 0; + + DELTAR = watermarkTable[indexPhiRef][indexThetaRef]; + + sphRi.push_back( sphR ); + corr.push_back(sphR*DELTAR); + n++; + + } + } + + double mean_corr = 0; + double var_corr = 0; + + // mean + for(int i = 0; i < n; i++) + mean_corr += corr[i]; + mean_corr /= (double)n; + + + // variance + for(int i = 0; i < n; i++) + var_corr+= pow(corr[i] - mean_corr, 2); + var_corr /= (double)n; + + + //VALORI TEORICI + double muRhoH0,muRhoH1; + double varRhoH0, varRhoH1; + double var_sphR = 0.0; + for (int i=0; i::Box(m.cm); + } break; + default : assert(0); + } + + +} + + + + +QString WatermarkPlugin::filterScriptFunctionName( FilterIDType filterID ) +{ + switch(filterID) { + case FP_EMBED_WATERMARK : return QString("embedWatermarking"); + case FP_DECODE_WATERMARK : return QString("decodeWatermarking"); + default : assert(0); + } + return QString(); +} + +Q_EXPORT_PLUGIN(WatermarkPlugin) diff --git a/src/plugins_experimental/filter_watermark/filter_watermark.h b/src/plugins_experimental/filter_watermark/filter_watermark.h new file mode 100644 index 000000000..e2f80885c --- /dev/null +++ b/src/plugins_experimental/filter_watermark/filter_watermark.h @@ -0,0 +1,57 @@ +/**************************************************************************** +* 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 WATERMARKPLUGIN_H +#define WATERMARKPLUGIN_H + +#include + +#include +class QScriptEngine; + +class WatermarkPlugin : public QObject, public MeshFilterInterface +{ + Q_OBJECT + Q_INTERFACES(MeshFilterInterface) + +public: + enum { FP_EMBED_WATERMARK, FP_DECODE_WATERMARK } ; + + WatermarkPlugin(); + ~WatermarkPlugin(){}; + + virtual QString pluginName(void) const { return "WatermarkPlugin"; } + + virtual QString filterName(FilterIDType filter) const; + virtual QString filterInfo(FilterIDType filter) const; + virtual void initParameterSet(QAction *,MeshModel &/*m*/, RichParameterSet & /*parent*/); + virtual bool applyFilter(QAction *filter, MeshDocument &md, RichParameterSet & /*parent*/, vcg::CallBackPos * cb) ; + virtual int postCondition( QAction* ) const {return MeshModel::MM_VERTCOORD | MeshModel::MM_FACENORMAL | MeshModel::MM_VERTNORMAL;}; + virtual FilterClass getClass(QAction *a); + virtual QString filterScriptFunctionName(FilterIDType filterID); + + +}; + + +#endif diff --git a/src/plugins_experimental/filter_watermark/filter_watermark.pro b/src/plugins_experimental/filter_watermark/filter_watermark.pro new file mode 100644 index 000000000..0e829f49b --- /dev/null +++ b/src/plugins_experimental/filter_watermark/filter_watermark.pro @@ -0,0 +1,9 @@ +include (../../shared.pri) + +HEADERS += filter_watermark.h \ + utilsWatermark.h + +SOURCES += filter_watermark.cpp \ + utilsWatermark.cpp + +TARGET = filter_watermark diff --git a/src/plugins_experimental/filter_watermark/utilsWatermark.cpp b/src/plugins_experimental/filter_watermark/utilsWatermark.cpp new file mode 100644 index 000000000..087c633f9 --- /dev/null +++ b/src/plugins_experimental/filter_watermark/utilsWatermark.cpp @@ -0,0 +1,142 @@ +/**************************************************************************** +* 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. * +* * +****************************************************************************/ + +#include "utilsWatermark.h" +#include "qstring.h" +#include "filter_watermark.h" +#include "ERF.h" + +using namespace std; + +// Seed generator for the psedo random sequence in the watermark table +// The seed is obtained by multiplying the ascii codes of each char of the chosen string +unsigned int utilsWatermark::ComputeSeed( QString string_code ) +{ + unsigned int code = (unsigned int)(string_code[0].digitValue()); + + for (int i=1; i > utilsWatermark::CreateWatermarkTable(unsigned int seed) +{ + + + double cellTablesize = 1.0; + static vcg::math::MarsenneTwisterRNG rnd; + rnd.initialize(seed); + double p = 0; // I valori generati di 'p' vanno da -RANGE_MARCHIO a RANGE_MARCHIO + + int nsTheta = 360.0/cellTablesize; + int nsPhi = 180.0/cellTablesize; + + vector > watermarkTable; + watermarkTable.resize(nsPhi); + for (int k = 0; k < nsPhi; ++k) + watermarkTable[k].resize(nsTheta); + + + int i,j; + for (i=0; i Radianti + phi *= DEG_TO_RAD; + theta *= DEG_TO_RAD; + + x = float(R*cos(theta)*sin(phi)); + z = float(R*sin(theta)*sin(phi)); + y = float(R*cos(phi)); + +} + +// Cartesian to Spheric Coordinates System conversion +void utilsWatermark::cartesian2sph(double x, double y, double z, double &R, double &theta, double &phi) +{ + R = sqrt( x*x + y*y + z*z ); + theta = atan2( z,x ); + phi = acos( y / R ); + + // Conversione Radianti --> Gradi + phi *= RAD_TO_DEG; + theta *= RAD_TO_DEG; +} + +// rounding +double utilsWatermark::round(double x) +{ + double app = floor(x); + + if (x - app >= 0.5) + return (app + 1.0); + else + return app; +} + +// evaluate threshold +double utilsWatermark::thresholdRo(double muRhoH0, double varRhoH0, + double muRhoH1, double varRhoH1, + double Pf, double &Pm) +{ + + Erf erf; + double Y; + double Z; + + // Calcolo di T(ro) + ///////////////////////////////////////////////// + + + Y = erf.inverfc( 2.0*Pf ); + + double Trho = sqrt(2.0*varRhoH0)*Y + muRhoH0; + + // Calcolo di Pm + /////////////////////////////////////////////////////// + + //Z = gsl_sf_erfc( sqrt( ((muRhoH1 - Trho)*(muRhoH1 - Trho))/(2.0*varRhoH1)) ); + //Z = erfc( sqrt( ((muRhoH1 - Trho)*(muRhoH1 - Trho))/(2.0*varRhoH1)) ); + Z = erf.erfc( sqrt( ((muRhoH1 - Trho)*(muRhoH1 - Trho))/(2.0*varRhoH1)) ); + + Pm = 0.5*Z; + + return Trho; +} diff --git a/src/plugins_experimental/filter_watermark/utilsWatermark.h b/src/plugins_experimental/filter_watermark/utilsWatermark.h new file mode 100644 index 000000000..d5988c205 --- /dev/null +++ b/src/plugins_experimental/filter_watermark/utilsWatermark.h @@ -0,0 +1,51 @@ +/**************************************************************************** +* 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 __UTILS_WATERMARKPLUGIN_H +#define __UTILS_WATERMARKPLUGIN_H + +#define RANGE_MARCHIO 1.0 // I valori della TabellaMarchio vanno da -RANGE_MARCHIO a +RANGE_MARCHIO (distribuiti UNIFORMEMENTE) +#define RAD_TO_DEG 57.295779513082320876798154814105 // Radianti*RAD_TO_DEG = Gradi +#define DEG_TO_RAD 0.017453292519943295769236907684886 // Gradi*DEG_TO_RAD = Radianti +#define PHIEXCLUSION_DEGREE 8.0 // Esclude dalla marchiature le coordinate polari con phi troppo vicine a 180.0 o a 0.0 + +#include "qstring.h" +#include "math.h" +#include + +class utilsWatermark +{ +public: + utilsWatermark(void){} + ~utilsWatermark(void){} + static unsigned int ComputeSeed(QString string_code); + static std::vector< std::vector > CreateWatermarkTable(unsigned int seed); + static void sph2cartesian(double R, double theta, double phi, float &x, float &y, float &z); + static void cartesian2sph(double x, double y, double z, double &R, double &theta, double &phi); + static double round(double x); + static double thresholdRo(double muRhoH0, double varRhoH0, + double muRhoH1, double varRhoH1, + double Pf, double &Pm); +}; + +#endif