/* ---- This file is part of SECONDO. Copyright (C) 2004, University in Hagen, Department of Computer Science, Database Systems for New Applications. SECONDO 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. SECONDO 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 for more details. You should have received a copy of the GNU General Public License along with SECONDO; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ---- //paragraph [1] title: [{\Large \bf ] [}] April 2017 Michael Loris [1] Standalone program to calculate the Earth Mover's distance */ #include #include #include #include #include #include #include "../JPEGImage.h" #include double transport2(std::vector ist1, std::vector ist2); struct Flow { double fromWeight; double distance; double toWeight; }; double euclidDist(FeatureSignatureTuple ist1, FeatureSignatureTuple ist2) { double tmpRes = 0.0; tmpRes += pow(ist1.weight - ist2.weight, 2); tmpRes += pow(ist1.centroid.x - ist2.centroid.x, 2); tmpRes += pow(ist1.centroid.y - ist2.centroid.y, 2); tmpRes += pow(ist1.centroid.colorValue1 - ist2.centroid.colorValue1, 2); tmpRes += pow(ist1.centroid.colorValue2 - ist2.centroid.colorValue2, 2); tmpRes += pow(ist1.centroid.colorValue3 - ist2.centroid.colorValue3, 2); tmpRes += pow(ist1.centroid.coarseness - ist2.centroid.coarseness, 2); tmpRes += pow(ist1.centroid.contrast - ist2.centroid.contrast, 2); return sqrt(tmpRes); } int main(int argc, char** argv) { if (argc < 2) { std::cout << "please provide two signature files" << std::endl; return 1; } std::vector ist1; std::vector ist2; std::cout << argv[1] << " " << argv[2] << std::endl; double w; int x, y; double r,g,b; double coa, con; std::ifstream infile(argv[1]); std::string std; int counter = 0; while (infile >> w >> x >> y >> r >> g >> b >> coa >> con) { ist1.push_back({w, x, y, r, g, b, coa, con}); } counter = 0; std::ifstream infile2(argv[2]); while (infile2 >> w >> x >> y >> r >> g >> b >> coa >> con) { ist2.push_back({w, x, y, r, g, b, coa, con}); } double res = transport2(ist1, ist2); std::cout << res << std::endl; return 0; } double transport2(std::vector fst1, std::vector fst2) { // set the larger vector to p to avoid artifical nodes std::vector p; std::vector q; if (fst1.size() > fst2.size()) { p = fst1; q = fst2; } else { p = fst2; q = fst1; } long** distMat = new long*[p.size()]; for (unsigned int i = 0; i < p.size(); i++) distMat[i] = new long[q.size()]; // fill distance matrix for (unsigned int i = 0; i < p.size(); i++) // rows { for (unsigned int j = 0; j < q.size(); j++) // columns { // std::cout << "i:" << i << std::endl; distMat[i][j] = euclidDist(p.at(i), q.at(j)); // std::cout << distMat[i][j] << "|"; } // std::cout << std::endl; } // start in top left corner // north-west corner method // supply and demand are equal -> no costs, move one down // supply < demand -> move one down, // supply > demand -> move one right, // adjust supply (demand - supply) // we need to fullfill the demand // the demand consists of the sum of all // "dirt piles/sinks" of a signature // also, every "line of dirt piles" //(x,y,color1,color2,color3, coarseness, contrast) // is multiplied by it's weight // let's get the complete demand first: long sumDemand = 0; long sumSupply = 0; // to deal with situations when demand != supply // I'll avoid rounding errors by multiplying // the weights with an arbitrarily chosen big number // this is not ideal, a proper scaling // should be put in place for later versions for (auto sigTuple : p) { //todo remove magic number sumSupply = static_cast(round(sigTuple.weight * 1000000)); } for (auto sigTuple : q) { sumDemand = static_cast(round(sigTuple.weight * 1000000)); } // let's convert all weights to long // to avoid trouble in double country // for this, the FeatureSignatureTuple type // can't be used anymore // so, I'll go with simple vectors std::vectorpVec; for (auto fst : p) { long tmpWeight = static_cast(round(fst.weight * 1000000)); // std::cout << "pVec push:" << tmpWeight << std::endl; pVec.push_back(tmpWeight); } std::vectorqVec; for (auto fst : q) { long tmpWeight = static_cast(round(fst.weight * 1000000)); // std::cout << "qVec push:" << tmpWeight << std::endl; qVec.push_back(tmpWeight); } // if demand is higher, add a zero cost dummy to supply // if it's the other way around, add a zero cost dummy // to demand if (sumDemand > sumSupply) { std::cout << "demand is higher" << std::endl; long tmpDemand = sumDemand - sumSupply; pVec.push_back(tmpDemand); } else if (sumSupply > sumDemand) { long tmpSupply = sumSupply - sumDemand; qVec.push_back(tmpSupply); } std::cout << "len supply:" << p.size() << std::endl; std::cout << "len demand:" << q.size() << std::endl; //double cost = 0.0; unsigned int i = 0; unsigned int j = 0; double sumDists = 0.0; double sumFlows = 0.0; while (true) { //std::cout << "i:" << i << std::endl; //std::cout << "j:" << j << std::endl; std::cout << "pvecsize:" << pVec.size() << std::endl; std::cout << "qecsize:" << qVec.size() << std::endl; if (i >= qVec.size()) { std::cout << "breaking i" << std::endl; break; } if (j >= pVec.size()) { std::cout << "breaking j" << std::endl; break; } //std::cout << "pVec:" << pVec.at(j) << " qVec:" //<< qVec.at(i) << std::endl; if (pVec.at(j) > qVec.at(i)) // supply higher { std::cout << "supply higher" << std::endl; pVec.at(j) -= qVec.at(i); // decrease supply std::cout << "pVec:" << pVec.at(j) << std::endl; std::cout << "i:" << i << " j:" << j << std::endl; sumFlows += qVec.at(i); // increase float sum sumDemand -= qVec.at(i); // global counter sumDists += qVec.at(i) * distMat[i][j]; i++; // move to next requester } else if (pVec.at(j) < qVec.at(i)) // demand higher { std::cout << "demand higher" << std::endl; qVec.at(i) -= pVec.at(j); // reduce supply std::cout << "qVec:" << qVec.at(i) << std::endl; std::cout << "i:" << i << " j:" << j << std::endl; sumFlows += pVec.at(j); sumDemand -= pVec.at(j); sumDists += pVec.at(j) * distMat[i][j]; std::cout << "pVec:" << pVec.at(j) << " distMat:" << distMat[i][j] << std::endl; // std::cout << "prod:" //<< pVec.at(j) * distMat[i][j] << std::endl; j++; // move to next supplier } else // supply and demand are equal { std::cout << "else"<< std::endl; pVec.at(j) -= qVec.at(i); qVec.at(i) -= pVec.at(j); sumFlows += qVec.at(i); sumDemand -= qVec.at(i); sumDists += pVec.at(j) * distMat[i][j]; std::cout << "pVec:" << pVec.at(j) << " distMat:" << distMat[i][j] << std::endl; std::cout << "i:" << i << " j:" << j << std::endl; // std::cout << "prod:" //<< pVec.at(j) * distMat[i][j] << std::endl; j++; i++; } std::cout << "end i:" << i << "end j:" << j << std::endl; } std::cout << "end sumDemand:" << sumDemand << std::endl; std::cout << "end sumSuppply:" << sumSupply << std::endl; std::cout << "end sumFlow:" << sumFlows << std::endl; std::cout << "end sumDists:" << sumDists << std::endl; return (sumFlows * sumDists) / sumFlows; /* while (i < q.size() && j < p.size()) { if (j >= q.size()) break; if (j >= p.size()) break; if (p.at(j).weight > q.at(i).weight) // supply higher { // std::cout << " fog 1a 1" << std::endl; p.at(j).weight -= q.at(i).weight; // std::cout << " fog 1a 2" << std::endl; cost += (distMat[i][j] * q.at(i).weight); //std::cout << " fog 1a 3" << std::endl; sumDists += distMat[i][j]; //std::cout << " fog 1a 4" << std::endl; q.at(i).weight = 0; i++; } else if (p.at(j).weight < q.at(i).weight) // demand higher { //std::cout << "fog 1b 1" << std::endl; q.at(i).weight -= p.at(j).weight; //std::cout << " fog 1b 2, j:" << j << std::endl; cost += (distMat[i][j] * q.at(j).weight); // i? //std::cout << " fog 1b 3" << std::endl; sumDists += distMat[i][j]; //std::cout << " fog 1b 4" << std::endl; p.at(j).weight = 0; //std::cout << " fog 1b 5" << std::endl; j++; } else { //std::cout << "fog 1c" << std::endl; distMat[i][j] *= q.at(i).weight; cost += distMat[i][j] * q.at(j).weight; p.at(j).weight = 0; q.at(i).weight = 0; j++; } } */ //if (cost == 0.0) // return 0.0; //else // return (sumDists * cost) / cost; }