Image processing: luminance weighted - c++

I would like to weigh values of luminance.
Example:
I have a vector of luminance values:
vector <int> lum {50,100,150,200,250);
I have a vector of coefficients:
vector <float> coef {5.1 , 2.55 , 1.7 , 1.275, 1.02 }
I create an empty image:
Mat1 m(15):
So, my first value of luminance is 50 (lum[0]=50) and I want it to be applied on the 5.1 (coef[0]=5.1) first pixel of my matrix. To do that, I need to weight the 6th pixel with the first and the second value of luminance. In my case, the luminance of my 6th pixel will be 95 because (0.1*50)+ (0.9*100)=95
At the moment, for the second coefficient (coef[1]=2.55), I have used 0.9 on 2.55 for the previous calcul. It remains 1,65 on this coefficient so the 7th pixel will have 100 of luminance and the 8th will have (0.65*100)+ (0.35*150) = 117,5.
And so on...
Actually I have this:
//Blibliothèque Opencv
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui/highgui.hpp"
// cpp include
#include <iostream>
#include <cmath>
#include <math.h>
#include <string.h>
#include <vector>
#include <opencv2/opencv.hpp>
#define MPI 3.14159265358979323846264338327950288419716939937510
#define RAD2DEG (180./MPI)
using namespace cv;
using namespace std;
vector <int> lum{ 50, 100, 150, 200, 250 };
vector <float> coef (5,0);
vector <int> newv(15, 0);
float pixelRef = 255, angle = 0, coeff = 0;
int main()
{
for (int n = 0; n < lum.size(); ++n)
{
//get angle
angle = ((acos(lum[n] / pixelRef)));
cout << "angle :" << angle*RAD2DEG << endl;
// get coefficient
coef [n] = (1 / (cos(angle)));
cout << "coeff :" << coef [n] << endl;
// try to weighted my pixels
newv[n] = (coef*lum[n]) + ((1 - coeff)*lum[n + 1]);
}
return 0;
}

I modified the last element of coef to 3.02f to show that this code handles well also the last element. The result sequence is:
50, 50, 50, 50, 50, 95, 100, 117.5, 150, 182.5, 218.75, 250, 250,
The code could be probably re-written better, but I'll leave this to you:
#include <opencv2/opencv.hpp>
#include <vector>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
vector <int> lum{ 50, 100, 150, 200, 250 };
vector <float> coef{ 5.1f, 2.55f, 1.7f, 1.275f, 3.02f };
vector<float> v;
int idx_lum = 0;
int idx_coef = 0;
while (true)
{
int c = int(coef[idx_coef]);
for (int i = 0; i < c; ++i)
{
v.push_back(float(lum[idx_lum]));
}
float alpha = fmod(coef[idx_coef], 1.f);
float beta = 1.f - alpha;
v.push_back(alpha * lum[idx_lum] + beta * lum[idx_lum + 1]);
idx_lum++;
idx_coef++;
coef[idx_coef] = coef[idx_coef] - beta;
if (idx_lum >= lum.size() - 1 || idx_coef >= coef.size() - 1)
{
int cc = int(coef[idx_coef]);
for (int i = 0; i < cc; ++i)
{
v.push_back(float(lum[idx_lum]));
}
// Only if the last remainder is needed
//float alpha = fmod(coef[idx_coef], 1.f);
//v.push_back(alpha * lum[idx_lum]);
break;
}
}
// Print out the values
copy(v.begin(), v.end(), ostream_iterator<float>(cout, ", "));
// Get a cv::Mat from the std::vector
Mat1f m = Mat1f(v).t();
return 0;
}

Related

Why is my function not printing the area of a pentagon?

I'm trying to create a function that will return the area of a pentagon, but it just prints 0 to the screen.
#include <iostream>
#include <cmath>
double area_of_pentagon(int side){
double area = 1/4 * sqrt(5*(5+2*sqrt(5)))*side*side;
return area;
}
int main(){
std::cout << area_of_pentagon(6);
}
My code is supposed to output 61.94 if the side length is 6, but it just returns 0.
#include <iostream>
#include <cmath>
double area_of_pentagon(int side){
//double area = 1/4 * sqrt(5*(5+2*sqrt(5)))*side*side; // 1/4 is 0 which is causing problem
//Use below 2 options.
double area = (sqrt(5*(5+2*sqrt(5)))*side*side)/4;
//double area = (0.25) * sqrt(5*(5+2*sqrt(5)))*side*side; //or use this
return area;
}
int main(){
std::cout << area_of_pentagon(6);
}

A really weired issue in (c++) code

I have defined a variable in my code:
double R0;
When I set the variable less than 0.9, the code doesn’t run without no error! I have also written cout<<2; exactly after main(){ but the program doesn’t even show that! I am very confused :( what is the problem? When I change R0 to 0.9 or bigger than it, the code runs. This is the most minimal example I could provide:
#include <iostream>
#include <math.h>
#include <vector>
#include <functional>
#include <string>
#include <sstream>
using namespace std;
vector<double> getBoxCoords(int boxID, double L, double R0, int nbox)
{
vector<double> coords(4);
int yID = ceil((double)(boxID+1)/nbox);
int xID = fmod((boxID+1-(yID-1)*nbox), nbox);
if(xID==0)
xID = nbox;
coords[0] = (xID-1) * R0; // minX
coords[1] = (yID-1) * R0; // minY
coords[2] = min(xID * R0, L); // maxX
coords[3] = min(yID * R0, L); // maxY
return coords;
}
double PBC(double pos, double L)
{
if(fabs(pos) > L / 2.0)
return L-fabs(pos);
else
return fabs(pos);
}
int main()
{
std::cout << 2;
int N=100;
double rho=4.0, v0=0.03, eta=0.2, R0=0.03;
double L = pow(N/rho,0.5);
int nbox = (int)ceil(L/R0);
vector<vector<int>> box_neighbors;
vector<int> indices;
for(int i = 0; i < nbox * nbox; i++) //NUMBER OF BOX
{
vector<double> ci = getBoxCoords(i, L, R0, nbox);
indices.clear();
for(int j = 0; j < nbox * nbox; j++)
{
vector<double> cj = getBoxCoords(j, L, R0, nbox);
bool xflag=false,
yflag=false;
if (PBC(ci[0]-cj[0],L)<R0 || PBC(ci[0]-cj[2],L)<R0 || PBC(ci[2]-cj[0],L)<R0 || PBC(ci[2]-cj[2],L)<R0)
xflag=true;
if (PBC(ci[1]-cj[1],L)<R0 || PBC(ci[1]-cj[3],L)<R0 || PBC(ci[3]-cj[1],L)<R0 || PBC(ci[3]-cj[3],L)<R0)
yflag=true;
if(xflag && yflag)
indices.push_back(j);
}
box_neighbors.push_back(indices);
}
return 0;
}
How can I remove the problem? Could anyone provide a runnable answer?
First thing first, the debug std::cout << 2; is now shown because you do not end the stream, proper way of doing it is
std::cout << 2 << std::endl;
then you will be able to see the debugging message.
Secondly, Your program runs, but takes too much time to finish, when R0 is small. For the given value, that is, 0.03, both layers of the loop will execute nbox * nbox times which is 27889, thus, 777796321 in total.

can't get same results Matlab generates

Take a look at the following transfer function:
With Matlab Simulink:
The result is
In State-space representation, the system can be modeled as follows:
In Matlab, we can model the system in the state-space representation:
which yields the following plot:
which is exactly the result generated by using transfer function. I'm trying to generate same results with odeint but failed. This is the code
#include <iostream>
#include <Eigen/Dense>
#include <boost/numeric/odeint.hpp>
#include <iomanip>
#include <fstream>
using namespace std;
using namespace boost::numeric::odeint;
typedef std::vector< double > state_type;
void equations(const state_type &y, state_type &dy, double x)
{
Eigen::MatrixXd A(3, 3), B(3,1);
/*
x = y[0]
dx = y[1] = dy[0]
ddx = y[2] = dy[1]
dddx = dy[2]
*/
const double r(1);
A << 0, 1, 0,
0, 0, 1,
-24, -26, -9;
B << 0, 0, 1;
//#####################( ODE Equations )################################
Eigen::MatrixXd X(3, 1), dX(3,1);
X << y[0], y[1], y[2];
dX = A*X + B*r;
dy[0] = dX(0,0);
dy[1] = dX(1,0);
dy[2] = dX(2,0);
}
int main(int argc, char **argv)
{
const double dt = 0.01;
runge_kutta_dopri5 < state_type > stepper;
state_type y(3);
// initial values
y[0] = 0.0; // x1
y[1] = 0.0; // x2
y[2] = 0.0; // x3
ofstream data("file.txt");
for (double t(0.0); t <= 5.0; t += dt){
data << t << " " << 2*y[0] << " " << 7*y[1] << " " << 1*y[2] << std::endl;
stepper.do_step(equations, y, t, dt);
}
return 0;
}
And this is the result for all state vector
None of the preceding variables match the results generated by Matlab. Any suggestions to fix this code?
Look at the expression you have for y. When you multiply a 1x3 matrix with a 3x1 matrix, the result should be a 1x1 matrix, where the value of the single element is the dot product of the two matrices. What you're currently doing is element-wise multiplication when you write to data instead of calculating the dot product.

Increase number of columns filled in as row number increases

I am writing a program to fill in a matrix (I use the dlib library but not relevant to the question. In the following my goal is for rows 1-19 (row indices 0-18) for one additional column to fill in with my formula. For example row 1 has the first column filled in, row 2 has the first two columns filled in. The first column for every row is preset to my initial value as required. What can I do to the nested for loops indicated by comment my lmm() function to get my desired output?
#include <random>
#include <iostream>
#include <sstream>
#include <string>
#include <fstream>
#include <vector>
#include <cmath>
#include <limits>
#include <cstdlib>
#include <chrono>
#include <iterator>
#include <algorithm>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/mean.hpp>
#include <boost/accumulators/statistics/kurtosis.hpp>
#include <boost/accumulators/statistics/variance.hpp>
#include <boost/accumulators/statistics/skewness.hpp>
#include <dlib/optimization.h>
#include <dlib/matrix.h>
#include "mleFunctor.h"
#include "mleDerFunctor.h"
using namespace boost::accumulators;
using namespace dlib;
//Generate Gaussian via Box-Muller from Mark Joshi's C++ Design Patterns and Derivatives Pricing
double GetOneGaussianByBoxMuller()
{
double result;
double x;
double y;
double sizeSquared;
do
{
x = 2.0*std::rand() / static_cast<double>(RAND_MAX) - 1;
y = 2.0*std::rand() / static_cast<double>(RAND_MAX) - 1;
sizeSquared = x*x + y*y;
} while (sizeSquared >= 1.0);
result = x*sqrt(-2 * log(sizeSquared) / sizeSquared);
return result;
}
double libSum(matrix<double,20,20> v, matrix<double, 20, 20> lib, int r,int c , double d, int index,std::vector<double> W)
{
double sum = 0.0;
for (auto k = index + 1; k < lib.nr()-1; ++k)
{
sum += ((d*v(k,c-1)*lib(k,c-1))/(1+d*lib(k,c-1)))*v(k,c-1) * lib(r, c-1)*(W[c] - W[c-1]);
}
return sum;
}
void lmm()
{
double dt = .25;
std::vector<double> W(20);
std::vector<double> T;
matrix<double, 20, 20> L;
W[0] = 0;
for (auto c = 1; c < W.size(); ++c)
{
W[c] = W[c - 1] + sqrt(dt)*GetOneGaussianByBoxMuller();
}
for (auto i = 0; i < 20; ++i)
{
T.push_back(i*.25);
}
set_all_elements(L, 0);
set_colm(L, 0) = .003641; //3M Libor Rate on November 16,2015
matrix<double,20,20> vol;
set_all_elements(vol,.15);
//Loop that should fill in one more column each (ie 0 indexed row has one column filled in,
//row index 1 should have 2 columns filled in etc
for (auto c = 1; c < L.nc(); ++c)
{
for (auto r = 1; r < c; ++r)
{
L(r, c) = L(r, c-1) + libSum(vol, L,r,c, .25, c,W) + vol(r,c-1) * L(r, c-1 )*(W[c] - W[c-1]);
}
}
std::ofstream outfile("LMMFlatVol.csv");
outfile << L << std :: endl;
}
int main()
{
lmm();
return 0;
}
As of right now my output is just the preset first columns with the rest of the matrix zeroes as I initialized.
Your primary problem seems to be your loop condition. Note that the inner for loop should start at the first active row. For example, in the third column (c==2) the first time you calculate a value is row three (r==2). After that all rows until the bottom of the matrix have a calculated value.
Your previous logic did not even calculate any value for the second column (c==1) because r was set to 1 and (r < c) evaluated to false!
for (auto c = 1; c < L.nc(); ++c)
{
for (auto r = c; r < L.nc(); ++r)
{
L(r, c) = L(r, c-1) + libSum(vol, L,r,c, .25, c,W) + vol(r,c-1) * L(r, c-1 )*(W[c] - W[c-1]);
}
}

Having a matrix MxN of integers how to group them into polygons with boost geometry?

We have a matrix of given integers (any from 1 to INT_MAX) like
1 2 3
1 3 3
1 3 3
100 2 1
We want to create polygons with same colors for each unique int in matrix so our polygons would have coords/groupings like shown here.
and we could generate image like this:
Which *(because of vectirisation that was performed would scale to such size like):
(sorry for crappy drawings)
Is it possible and how to do such thing with boost geometry?
Update:
So #sehe sad: I'd simply let Boost Geometry do most of the work. so I created this pixel by pixel class aeria grower using purely Boost.Geometry, compiles, runs but I need it to run on clustered data.. and I have 1000 by 1800 files of uchars (each unique uchar == data belongs to that claster). Problem with this code: on 18th line it gets SO WARY SLOW that each point creation starts to take more than one second=(
code:
//Boost
#include <boost/assign.hpp>
#include <boost/foreach.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/geometry/geometry.hpp>
#include <boost/geometry/geometries/geometries.hpp>
#include <boost/geometry/multi/geometries/multi_polygon.hpp>
#include <boost/geometry/geometries/adapted/boost_tuple.hpp>
//and this is why we use Boost Geometry from Boost trunk
//#include <boost/geometry/extensions/io/svg/svg_mapper.hpp>
BOOST_GEOMETRY_REGISTER_BOOST_TUPLE_CS(cs::cartesian)
void make_point(int x, int y, boost::geometry::model::polygon<boost::geometry::model::d2::point_xy<double> > & ring)
{
using namespace boost::assign;
boost::geometry::append( ring, boost::geometry::model::d2::point_xy<double>(x-1, y-1));
boost::geometry::append( ring, boost::geometry::model::d2::point_xy<double>(x, y-1));
boost::geometry::append( ring, boost::geometry::model::d2::point_xy<double>(x, y));
boost::geometry::append( ring, boost::geometry::model::d2::point_xy<double>(x-1, y));
boost::geometry::append( ring, boost::geometry::model::d2::point_xy<double>(x-1, y-1));
boost::geometry::correct(ring);
}
void create_point(int x, int y, boost::geometry::model::multi_polygon< boost::geometry::model::polygon<boost::geometry::model::d2::point_xy<double> > > & mp)
{
boost::geometry::model::multi_polygon< boost::geometry::model::polygon<boost::geometry::model::d2::point_xy<double> > > temp;
boost::geometry::model::polygon<boost::geometry::model::d2::point_xy<double> > ring;
make_point(x, y, ring);
boost::geometry::union_(mp, ring, temp);
boost::geometry::correct(temp);
mp=temp;
}
int main()
{
using namespace boost::assign;
boost::geometry::model::multi_polygon< boost::geometry::model::polygon < boost::geometry::model::d2::point_xy<double> > > pol, simpl;
//read image
std::ifstream in("1.mask", std::ios_base::in | std::ios_base::binary);
int sx, sy;
in.read(reinterpret_cast<char*>(&sy), sizeof(int));
in.read(reinterpret_cast<char*>(&sx), sizeof(int));
std::vector< std::vector<unsigned char> > image(sy);
for(int i =1; i <= sy; i++)
{
std::vector<unsigned char> row(sx);
in.read(reinterpret_cast<char*>(&row[0]), sx);
image[i-1] = row;
}
//
std::map<unsigned char, boost::geometry::model::multi_polygon < boost::geometry::model::polygon < boost::geometry::model::d2::point_xy<double> > > > layered_image;
for(int y=1; y <= sy; y++)
{
for(int x=1; x <= sx; x++)
{
if (image[y-1][x-1] != 1)
{
create_point(x, y, layered_image[image[y-1][x-1]]);
std::cout << x << " : " << y << std::endl;
}
}
}
}
So as you can see my code suks.. so I decided to create a renderer for #sehe code:
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <set>
//Boost
#include <boost/assign.hpp>
#include <boost/array.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/geometry/geometry.hpp>
#include <boost/geometry/geometries/geometries.hpp>
#include <boost/geometry/multi/geometries/multi_polygon.hpp>
#include <boost/geometry/geometries/adapted/boost_tuple.hpp>
#include <boost/random/uniform_int.hpp>
#include <boost/random/mersenne_twister.hpp>
//and this is why we use Boost Geometry from Boost trunk
#include <boost/geometry/extensions/io/svg/svg_mapper.hpp>
BOOST_GEOMETRY_REGISTER_BOOST_TUPLE_CS(cs::cartesian)
namespace mxdetail
{
typedef size_t cell_id; // row * COLS + col
template <typename T> struct area
{
T value;
typedef std::vector<cell_id> cells_t;
cells_t cells;
};
template <typename T, size_t Rows, size_t Cols>
std::vector<area<T> > getareas(const boost::array<boost::array<T, Cols>, Rows>& matrix)
{
typedef boost::array<boost::array<T, Cols>, Rows> mtx;
std::vector<area<T> > areas;
struct visitor_t
{
const mtx& matrix;
std::set<cell_id> visited;
visitor_t(const mtx& mtx) : matrix(mtx) { }
area<T> start(const int row, const int col)
{
area<T> result;
visit(row, col, result);
return result;
}
void visit(const int row, const int col, area<T>& current)
{
const cell_id id = row*Cols+col;
if (visited.end() != visited.find(id))
return;
bool matches = current.cells.empty() || (matrix[row][col] == current.value);
if (matches)
{
visited.insert(id);
current.value = matrix[row][col];
current.cells.push_back(id);
// process neighbours
for (int nrow=std::max(0, row-1); nrow < std::min((int) Rows, row+2); nrow++)
for (int ncol=std::max(0, col-1); ncol < std::min((int) Cols, col+2); ncol++)
/* if (ncol!=col || nrow!=row) */
visit(nrow, ncol, current);
}
}
} visitor(matrix);
for (int r=0; r < (int) Rows; r++)
for (int c=0; c < (int) Cols; c++)
{
mxdetail::area<int> area = visitor.start(r,c);
if (!area.cells.empty()) // happens when startpoint already visited
areas.push_back(area);
}
return areas;
}
}
typedef boost::array<int, 4> row;
template <typename T, size_t N>
boost::array<T, N> make_array(const T (&a)[N])
{
boost::array<T, N> result;
std::copy(a, a+N, result.begin());
return result;
}
void make_point(int x, int y, boost::geometry::model::polygon<boost::geometry::model::d2::point_xy<double> > & ring)
{
using namespace boost::assign;
boost::geometry::append( ring, boost::geometry::model::d2::point_xy<double>(x-1, y-1));
boost::geometry::append( ring, boost::geometry::model::d2::point_xy<double>(x, y-1));
boost::geometry::append( ring, boost::geometry::model::d2::point_xy<double>(x, y));
boost::geometry::append( ring, boost::geometry::model::d2::point_xy<double>(x-1, y));
boost::geometry::append( ring, boost::geometry::model::d2::point_xy<double>(x-1, y-1));
boost::geometry::correct(ring);
}
void create_point(int x, int y, boost::geometry::model::multi_polygon< boost::geometry::model::polygon<boost::geometry::model::d2::point_xy<double> > > & mp)
{
boost::geometry::model::multi_polygon< boost::geometry::model::polygon<boost::geometry::model::d2::point_xy<double> > > temp;
boost::geometry::model::polygon<boost::geometry::model::d2::point_xy<double> > ring;
make_point(x, y, ring);
boost::geometry::union_(mp, ring, temp);
boost::geometry::correct(temp);
mp=temp;
}
boost::random::mt19937 rng;
boost::random::uniform_int_distribution<> color(10,255);
std::string fill_rule()
{
int red, green, blue;
red = color(rng);
green = color(rng);
blue = color(rng);
std::ostringstream rule;
rule << "fill-rule:nonzero;fill-opacity:0.5;fill:rgb("
<< red << "," << green << "," << blue
<< ");stroke:rgb("
<< (red - 5) << "," << (green - 5) << "," << (blue -5)
<< ");stroke-width:2";
return rule.str();
}
int main()
{
int sx = 4;
int sy = 5;
int row0[] = { 1 , 2, 3, 3, };
int row1[] = { 1 , 3, 3, 3,};
int row2[] = { 1 , 3, 3, 3, };
int row3[] = { 2 , 2, 1, 2, };
int row4[] = { 100, 2, 2, 2, };
boost::array<row, 5> matrix;
matrix[0] = make_array(row0);
matrix[1] = make_array(row1);
matrix[2] = make_array(row2);
matrix[3] = make_array(row3);
matrix[4] = make_array(row4);
typedef std::vector<mxdetail::area<int> > areas_t;
typedef areas_t::value_type::cells_t cells_t;
areas_t areas = mxdetail::getareas(matrix);
using namespace boost::assign;
typedef boost::geometry::model::polygon
<
boost::geometry::model::d2::point_xy<double>
> polygon;
typedef boost::geometry::model::multi_polygon<polygon> mp;
typedef boost::geometry::point_type<mp>::type point_type;
std::string filename = "draw.svg";
std::ofstream svg(filename.c_str());
boost::geometry::svg_mapper<point_type> mapper(svg, 400, 400);
for (areas_t::const_iterator it=areas.begin(); it!=areas.end(); ++it)
{
mp pol;
std::cout << "area of " << it->value << ": ";
for (cells_t::const_iterator pit=it->cells.begin(); pit!=it->cells.end(); ++pit)
{
int row = *pit / 3, col = *pit % 3;
std::cout << "(" << row << "," << col << "), ";
create_point( (row+1), (col+1), pol);
}
std::cout << std::endl;
mapper.add(pol);
mapper.map(pol, fill_rule());
}
std::cout << "areas detected: " << areas.size() << std::endl;
std::cin.get();
}
this code is compilable but it sucks (seems I did not get how to work with arrays after all...):
In short, if I got the question right, I'd simply let Boost Geometry do most of the work.
For a sample matrix of NxM, create NxM 'flyweight' rectangle polygons to correspond to each matrix cell visually.
Now, using an iterative deepening algorithm, find all groups:
* for each _unvisited_ cell in matrix
* start a new group
* [visit:]
- mark _visited_
- for each neighbour with equal value:
- add to curent group and
- recurse [visit:]
Note that the result of this algorithm could be distinct groups with the same values (representing disjunct polygons). E.g. the value 2 from the sample in the OP would result in two groups.
Now for each group, simply call Boost Geometry's Union_ algorithm to find the consolidated polygon to represent that group.
Sample implementation
Update Here is a non-optimized implementation in C++11:
Edit See here for C++03 version (using Boost)
The sample data used in the test corresponds to the matrix from the question.
#include <iostream>
#include <array>
#include <vector>
#include <set>
namespace mxdetail
{
typedef size_t cell_id; // row * COLS + col
template <typename T> struct area
{
T value;
std::vector<cell_id> cells;
};
template <typename T, size_t Rows, size_t Cols>
std::vector<area<T> > getareas(const std::array<std::array<T, Cols>, Rows>& matrix)
{
typedef std::array<std::array<T, Cols>, Rows> mtx;
std::vector<area<T> > areas;
struct visitor_t
{
const mtx& matrix;
std::set<cell_id> visited;
visitor_t(const mtx& mtx) : matrix(mtx) { }
area<T> start(const int row, const int col)
{
area<T> result;
visit(row, col, result);
return result;
}
void visit(const int row, const int col, area<T>& current)
{
const cell_id id = row*Cols+col;
if (visited.end() != visited.find(id))
return;
bool matches = current.cells.empty() || (matrix[row][col] == current.value);
if (matches)
{
visited.insert(id);
current.value = matrix[row][col];
current.cells.push_back(id);
// process neighbours
for (int nrow=std::max(0, row-1); nrow < std::min((int) Rows, row+2); nrow++)
for (int ncol=std::max(0, col-1); ncol < std::min((int) Cols, col+2); ncol++)
/* if (ncol!=col || nrow!=row) */
visit(nrow, ncol, current);
}
}
} visitor(matrix);
for (int r=0; r < Rows; r++)
for (int c=0; c < Cols; c++)
{
auto area = visitor.start(r,c);
if (!area.cells.empty()) // happens when startpoint already visited
areas.push_back(area);
}
return areas;
}
}
int main()
{
typedef std::array<int, 3> row;
std::array<row, 4> matrix = {
row { 1 , 2, 3, },
row { 1 , 3, 3, },
row { 1 , 3, 3, },
row { 100, 2, 1, },
};
auto areas = mxdetail::getareas(matrix);
std::cout << "areas detected: " << areas.size() << std::endl;
for (const auto& area : areas)
{
std::cout << "area of " << area.value << ": ";
for (auto pt : area.cells)
{
int row = pt / 3, col = pt % 3;
std::cout << "(" << row << "," << col << "), ";
}
std::cout << std::endl;
}
}
Compiled with gcc-4.6 -std=c++0x the output is:
areas detected: 6
area of 1: (0,0), (1,0), (2,0),
area of 2: (0,1),
area of 3: (0,2), (1,1), (1,2), (2,1), (2,2),
area of 100: (3,0),
area of 2: (3,1),
area of 1: (3,2),
When number of points is big (say, more than 1000x1000), the solution above would gobble a lot of memory. And this is exactly what happened to the topic-starter.
Below I show more scalable approach.
I would separate two problems here: one is to find the areas, another is to convert them into polygons.
The first problem is actually equivalent to finding the connected components of the grid graph where neighbors has edges if and only if they have equal "colors" attached to it. One can use a grid graph from boost-graph.
#include <boost/graph/grid_graph.hpp>
// Define dimension lengths, a MxN in this case
boost::array<int, 2> lengths = { { M, N } };
// Create a MxN two-dimensional, unwrapped grid graph
grid_graph<2> graph(lengths);
Next, we should convert a given matrix M into an edge filter: grid edges are present iff the "color" of the neighbors are the same.
template <class Matrix>
struct GridEdgeFilter
{
typedef grid_graph<2> grid;
GridEdgeFilter(const Matrix & m, const grid&g):_m(&m),_g(&g){}
/// \return true iff edge is present in the graph
bool operator()(grid::edge_descriptor e) const
{
grid::vertex_descriptor src = source(e,*_g), tgt = target(e,*_g);
//src[0] is x-coord of src, etc. The value (*m)[x,y] is the color of the point (x,y).
//Edge is preserved iff matrix values are equal
return (*_m)[src[0],src[1]] == (*_m)[tgt[0],tgt[1]];
}
const Matrix * _m;
const grid* _g;
};
Finally, we define a boost::filtered_graph of grid and EdgeFilter and call Boost.Graph algorithm for connected components.
Each connected component represents a set of points of a single color i.e. exactly the area we want to transform into a polygon.
Here we have another issue. Boost.Geometry only allows to merge polygons one by one. Hence it becomes very slow when number of polygons is big.
The better way is to use Boost.Polygon, namely its Property Merge functionality. One starts with empty property_merge object, and goes on by inserting rectangles of given color (you can set color as a property). Then one calls the method merge and gets a polygon_set as the output.