How to store a vector field with VTK? C++, VTKWriter - c++

Let's say, I have a vector field u, with components ux, uy and uz, defined at (unstructured) positions in space rx, ry and rz.
All I want, is to store this vector field with the VTK format, i.e. with the class "vtkwriter" from libvtk to enable visualization with Paraview.
I think I got the code for incorporating the positions right, but somehow I can't figure out, how to incorporate the data:
#include <vtkPoints.h>
#include <vtkPolyDataWriter.h>
#include <vtkSmartPointer.h>
void write_file (double* rx, double* ry, double* rz,
double* ux, double* uy, double* uz,
int n, const char* filename)
{
vtkSmartPointer<vtkPoints> points =
vtkSmartPointer<vtkPoints>::New ();
points->SetNumberOfPoints(n);
for (int i = 0; i < n; ++i) {
points->SetPoint(i, rx[i], ry[i], rz[i]);
}
// how to incorporate the vector field u?
vtkSmartPointer<vtkPolyDataWriter> writer =
vtkSmartPointer<vtkPolyDataWriter>::New ();
writer->setFileName (filename);
// how to tell the writer, what to write?
writer->Write ();
}
The first question is: is the general way correct, i.e. the coordinate's treatment with vtkPoints?
When searching the internet, I find many results, how the final file should look like.
I could probably generate that format by hand, but that isn't really what I want to do.
On the other hand, I'm somehow not able to understand VTK's documentation. Whenever I look up the documentation of a class, it refers to the documentation of some other classes, and these other classes' documentations refer back to the first one.
The same holds for the examples.
So far, I haven't found one, that explains how to handle vector valued data, that is defined at arbitrary positions, and the other examples are so complicated, that I'm completely stuck here.
I think, the solution somehow uses vtkPolyData, but I can't figure out, how to insert data.
I think, it needs a vtkDoubleArray, but I haven't found so far, how to make if vector valued.
Thanks in advance.

Ok, I got it done after enough trial and error.
The coordinates, where the vector field is defined should be vtkPoints and the data of interest should be a vtkDoubleArray.
The incorporation into the final vtkPolyData object is done via vtkPolyData::GetPointData()->SetVectors(...).
Finally, the cell type needs to be be set as vtkVertex:
#include <vtkCellArray.h>
#include <vtkDoubleArray.h>
#include <vtkPointData.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkPolyDataWriter.h>
#include <vtkSmartPointer.h>
#include <vtkVertex.h>
void VTKWriter::write_file(double* rx, double *ry, double *rz,
double* ux, double *uy, double *uz,
int n, const char* filename)
{
vtkSmartPointer<vtkPoints> points =
vtkSmartPointer<vtkPoints>::New();
points->SetNumberOfPoints(n);
vtkSmartPointer<vtkCellArray> vertices =
vtkSmartPointer<vtkCellArray>::New();
vertices->SetNumberOfCells(n);
for (int i = 0; i < n; ++i) {
points->SetPoint(i, rx[i], ry[i], rz[i]);
vtkSmartPointer<vtkVertex> vertex =
vtkSmartPointer<vtkVertex>::New();
vertex->GetPointIds()->SetId(0, i);
vertices->InsertNextCell(vertex);
}
vtkSmartPointer<vtkDoubleArray> u =
vtkSmartPointer<vtkDoubleArray>::New();
u->SetName("u");
u->SetNumberOfComponents(3);
u->SetNumberOfTuples(n);
for (int i = 0; i < n; ++i) {
u->SetTuple3(i, ux[i], uy[i], uz[i]);
}
vtkSmartPointer<vtkPolyData> polydata =
vtkSmartPointer<vtkPolyData>::New();
polydata->SetPoints(points);
polydata->SetVerts(vertices);
polydata->GetPointData()->SetVectors(u);
vtkSmartPointer<vtkPolyDataWriter> writer =
vtkSmartPointer<vtkPolyDataWriter>::New();
writer->SetFileName(filename);
writer->SetInputData(polydata);
writer->Write ();
}
The reason, why I didn't got this at first was, because the interaction between points, cells, vertices, pointdata and polydata isn't easy to grasp when one is new to VTK, the tutorials do not really cover this at all, and VTK's Doxygen documentation is also somehow useless at this point.

Related

C++ Avoiding Triple Pointers

I am trying to create an array of X pointers referencing matrices of dimensions Y by 16. Is there any way to accomplish this in C++ without the use of triple pointers?
Edit: Adding some context for the problem.
There are a number of geometries on the screen, each with a transform that has been flattened to a 1x16 array. Each snapshot represents the transforms for each of number of components. So the matrix dimensions are 16 by num_components by num_snapshots , where the latter two dimensions are known at run-time. In the end, we have many geometries with motion applied.
I'm creating a function that takes a triple pointer argument, though I cannot use triple pointers in my situation. What other ways can I pass this data (possibly via multiple arguments)? Worst case, I thought about flattening this entire 3D matrix to an array, though it seems like a sloppy thing to do. Any better suggestions?
What I have now:
function(..., double ***snapshot_transforms, ...)
What I want to accomplish:
function (..., <1+ non-triple pointer parameters>, ...)
Below isn't the function I'm creating that takes the triple pointer, but shows what the data is all about.
static double ***snapshot_transforms_function (int num_snapshots, int num_geometries)
{
double component_transform[16];
double ***snapshot_transforms = new double**[num_snapshots];
for (int i = 0; i < num_snapshots; i++)
{
snapshot_transforms[i] = new double*[num_geometries];
for (int j = 0; j < num_geometries; j++)
{
snapshot_transforms[i][j] = new double[16];
// 4x4 transform put into a 1x16 array with dummy values for each component for each snapshot
for (int k = 0; k < 16; k++)
snapshot_transforms[i][j][k] = k;
}
}
return snapshot_transforms;
}
Edit2: I cannot create new classes, nor use C++ features like std, as the exposed function prototype in the header file is getting put into a wrapper (that doesn't know how to interpret triple pointers) for translation to other languages.
Edit3: After everyone's input in the comments, I think going with a flattened array is probably the best solution. I was hoping there would be some way to split this triple pointer and organize this complex data across multiple data pieces neatly using simple data types including single pointers. Though I don't think there is a pretty way of doing this given my caveats here. I appreciate everyone's help =)
It is easier, better, and less error prone to use an std::vector. You are using C++ and not C after all. I replaced all of the C-style array pointers with vectors. The typedef doublecube makes it so that you don't have to type vector<vector<vector<double>>> over and over again. Other than that the code basically stays the same as what you had.
If you don't actually need dummy values I would remove that innermost k loop completely. reserve will reserve the memory space that you need for the real data.
#include <vector>
using std::vector; // so we can just call it "vector"
typedef vector<vector<vector<double>>> doublecube;
static doublecube snapshot_transforms_function (int num_snapshots, int num_geometries)
{
// I deleted component_transform. It was never used
doublecube snapshot_transforms;
snapshot_transforms.reserve(num_snapshots);
for (int i = 0; i < num_snapshots; i++)
{
snapshot_transforms.at(i).reserve(num_geometries);
for (int j = 0; j < num_geometries; j++)
{
snapshot_transforms.at(i).at(j).reserve(16);
// 4x4 transform put into a 1x16 array with dummy values for each component for each snapshot
for (int k = 0; k < 16; k++)
snapshot_transforms.at(i).at(j).at(k) = k;
}
}
return snapshot_transforms;
}
Adding a little bit of object-orientation usually makes the code easier to manage -- for example, here's some code that creates an array of 100 Matrix objects with varying numbers of rows per Matrix. (You could vary the number of columns in each Matrix too if you wanted to, but I left them at 16):
#include <vector>
#include <memory> // for shared_ptr (not strictly necessary, but used in main() to avoid unnecessarily copying of Matrix objects)
/** Represents a (numRows x numCols) 2D matrix of doubles */
class Matrix
{
public:
// constructor
Matrix(int numRows = 0, int numCols = 0)
: _numRows(numRows)
, _numCols(numCols)
{
_values.resize(_numRows*_numCols);
std::fill(_values.begin(), _values.end(), 0.0f);
}
// copy constructor
Matrix(const Matrix & rhs)
: _numRows(rhs._numRows)
, _numCols(rhs._numCols)
{
_values.resize(_numRows*_numCols);
std::fill(_values.begin(), _values.end(), 0.0f);
}
/** Returns the value at (row/col) */
double get(int row, int col) const {return _values[(row*_numCols)+col];}
/** Sets the value at (row/col) to the specified value */
double set(int row, int col, double val) {return _values[(row*_numCols)+col] = val;}
/** Assignment operator */
Matrix & operator = (const Matrix & rhs)
{
_numRows = rhs._numRows;
_numCols = rhs._numCols;
_values = rhs._values;
return *this;
}
private:
int _numRows;
int _numCols;
std::vector<double> _values;
};
int main(int, char **)
{
const int numCols = 16;
std::vector< std::shared_ptr<Matrix> > matrixList;
for (int i=0; i<100; i++) matrixList.push_back(std::make_shared<Matrix>(i, numCols));
return 0;
}

How to access matrix data in opencv by another mat with locations (indexing)

Suppose I have a Mat of indices (locations) called B, We can say that this Mat has dimensions of 1 x 100 and We suppose to have another Mat, called A, full of data of the same dimensions of B.
Now, I would access to the data of A with B. Usually I would create a for loop and I would take for each elements of B, the right elements of A. For the most fussy of the site, this is the code that I would write:
for(int i=0; i < B.cols; i++){
int index = B.at<int>(0, i);
std::cout<<A.at<int>(0, index)<<std:endl;
}
Ok, now that I showed you what I could do, I ask you if there is a way to access the matrix A, always using the B indices, in a more intelligent and fast way. As someone could do in python thanks to the numpy.take() function.
This operation is called remapping. In OpenCV, you can use function cv::remap for this purpose.
Below I present the very basic example of how remap algorithm works; please note that I don't handle border conditions in this example, but cv::remap does - it allows you to use mirroring, clamping, etc. to specify what happens if the indices exceed the dimensions of the image. I also don't show how interpolation is done; check the cv::remap documentation that I've linked to above.
If you are going to use remapping you will probably have to convert indices to floating point; you will also have to introduce another array of indices that should be trivial (all equal to 0) if your image is one-dimensional. If this starts to represent a problem because of performance, I'd suggest you implement the 1-D remap equivalent yourself. But benchmark first before optimizing, of course.
For all the details, check the documentation, which covers everything you need to know to use te algorithm.
cv::Mat<float> remap_example(cv::Mat<float> image,
cv::Mat<float> positions_x,
cv::Mat<float> positions_y)
{
// sizes of positions arrays must be the same
int size_x = positions_x.cols;
int size_y = positions_x.rows;
auto out = cv::Mat<float>(size_y, size_x);
for(int y = 0; y < size_y; ++y)
for(int x = 0; x < size_x; ++x)
{
float ps_x = positions_x(x, y);
float ps_y = positions_y(x, y);
// use interpolation to determine intensity at image(ps_x, ps_y),
// at this point also handle border conditions
// float interpolated = bilinear_interpolation(image, ps_x, ps_y);
out(x, y) = interpolated;
}
return out;
}
One fast way is to use pointer for both A (data) and B (indexes).
const int* pA = A.ptr<int>(0);
const int* pIndexB = B.ptr<int>(0);
int sum = 0;
for(int i = 0; i < Bi.cols; ++i)
{
sum += pA[*pIndexB++];
}
Note: Be carefull with pixel type, in this case (as you write in your code) is int!
Note2: Using cout for each point access put the optimization useless!
Note3: In this article Satya compare four methods for pixel access and fastest seems "foreach": https://www.learnopencv.com/parallel-pixel-access-in-opencv-using-foreach/

Trying to make a live data grapher with CImg library (C++)

I'm new to CImg. Not sure if there's already a live data plotter in the library but I thought I'd go ahead and make one myself. If what I'm looking for already exists in the library please point me to the function. otherwise, here is my super inefficient code that I'm hoping you can help me with~
#include <iostream>
#include "CImg.h"
#include <ctime>
#include <cmath>
using namespace cimg_library;
int main()
{
CImg<unsigned char> plot(400, 320, 1, 3, 0);
CImgDisplay graph(plot, "f(x)");
clock();
const unsigned char red[] = {255, 0, 0};
float* G = new float[plot.width()]; //define an array holding the values that are to be displayed on the graph
while (1){
G[0] = ((plot.height()/4) * sin(clock() / 1000.0)) + plot.height()/2; // new f(t) value
for (int i = 1; i <= plot.width() - 1; i++){
G[plot.width() - i] = G[plot.width() - i - 1]; //basically shift all the array values to current address+1
plot.draw_point(plot.width() - 3*i, G[i-1], red, 1).display(graph);
}
plot.fill(0);
}
return 0;
}
problems
the grapher traverses right to left soo slowly.. and I'm not sure how to make a smooth curve hence I went with points.. how do you make a smooth curve?
There is already something for you in the library, method CImg<T>::draw_graph(), as (brielfy) explained here :
http://cimg.eu/reference/structcimg__library_1_1CImg.html#a2e629aadedc4518001f00333f25bfec8
There are few examples provided with the library that use this method, see files examples/tutorial.cpp and examples/plotter1d.cpp.

Warped scene with two sets of Geodes

I have a few objects that I want to combine into a scene graph.
Street inherits from Geode and has a Geometry child drawable made up of a GL_LINE_STRIP.
Pointer inherits from PositionAttitudeTransform and contains a Geode which contains two Geometry polygons.
When I add a bunch of Streets to a Group, it looks just fine. When I add only the Pointer to a Group, it also looks fine. But if I somehow have them both in the scene, the second one is screwed up. See the two pictures.
In the above picture, the street network is as desired, and in the picture below, the pointer is as desired.
I'd appreciate any help! If you need to see the code, let me know and I'll update my question.
Update 1: Since nothing has happened so far, here is the minimal amount of code necessary to produce the phenomenon. I have put two pointers next to each other with no problem, so I'm starting to suspect that I made the streets wrong... next update will be some street generation code.
Update 2: The code now contains the street drawing code.
Update 3: The code now contains the pointer drawing code as well, and the street drawing
code has been simplified.
// My libraries:
#include <asl/util/color.h>
using namespace asl;
#include <straph/point.h>
#include <straph/straph.h>
using namespace straph;
// Standard and OSG libraries:
#include <utility>
#include <boost/tuple/tuple.hpp> // tie
using namespace std;
#include <osg/ref_ptr>
#include <osg/Array>
#include <osg/Geometry>
#include <osg/Geode>
#include <osg/Group>
#include <osg/LineWidth>
using namespace osg;
#include <osgUtil/Tessellator>
#include <osgViewer/Viewer>
using namespace osgViewer;
/*
* Just FYI: A Polyline looks like this:
*
* typedef std::vector<Point> Polyline;
*
* And a Point basically is a simple struct:
*
* struct Point {
* double x;
* double y;
* };
*/
inline osg::Vec3d toVec3d(const straph::Point& p, double elevation=0.0)
{
return osg::Vec3d(p.x, p.y, elevation);
}
Geometry* createStreet(const straph::Polyline& path)
{
ref_ptr<Vec3dArray> array (new Vec3dArray(path.size()));
for (unsigned i = 0; i < path.size(); ++i) {
(*array)[i] = toVec3d(path[i]);
}
Geometry* geom = new Geometry;
geom->setVertexArray(array.get());
geom->addPrimitiveSet(new osg::DrawArrays(GL_LINE_STRIP, 0, array->size()));
return geom;
}
Geode* load_streets()
{
unique_ptr<Straph> graph = read_shapefile("mexico/roads", 6);
Geode* root = new Geode();
boost::graph_traits<straph::Straph>::edge_iterator ei, ee;
for (boost::tie(ei, ee) = edges(*graph); ei != ee; ++ei) {
const straph::Segment& s = (*graph)[*ei];
root->addDrawable(createStreet(s.polyline));
}
return root;
}
Geode* createPointer(double width, const Color& body_color, const Color& border_color)
{
float f0 = 0.0f;
float f3 = 3.0f;
float f1 = 1.0f * width;
float f2 = 2.0f * width;
// Create vertex array
ref_ptr<Vec3Array> vertices (new Vec3Array(4));
(*vertices)[0].set( f0 , f0 , f0 );
(*vertices)[1].set( -f1/f3, -f1/f3 , f0 );
(*vertices)[2].set( f0 , f2/f3 , f0 );
(*vertices)[3].set( f1/f3, -f1/f3 , f0 );
// Build the geometry object
ref_ptr<Geometry> polygon (new Geometry);
polygon->setVertexArray( vertices.get() );
polygon->addPrimitiveSet( new DrawArrays(GL_POLYGON, 0, 4) );
// Set the colors
ref_ptr<Vec4Array> body_colors (new Vec4Array(1));
(*body_colors)[0] = body_color.get();
polygon->setColorArray( body_colors.get() );
polygon->setColorBinding( Geometry::BIND_OVERALL );
// Start the tesselation work
osgUtil::Tessellator tess;
tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY );
tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD );
tess.retessellatePolygons( *polygon );
// Create the border-lines
ref_ptr<Geometry> border (new Geometry);
border->setVertexArray( vertices.get() );
border->addPrimitiveSet(new DrawArrays(GL_LINE_LOOP, 0, 4));
border->getOrCreateStateSet()->setAttribute(new LineWidth(2.0f));
ref_ptr<Vec4Array> border_colors (new Vec4Array(1));
(*border_colors)[0] = border_color.get();
border->setColorArray( border_colors.get() );
border->setColorBinding( Geometry::BIND_OVERALL );
// Create Geode object
ref_ptr<Geode> geode (new Geode);
geode->addDrawable( polygon.get() );
geode->addDrawable( border.get() );
return geode.release();
}
int main(int, char**)
{
Group* root = new Group();
Geode* str = load_streets();
root->addChild(str);
Geode* p = createPointer(6.0, TangoColor::Scarlet3, TangoColor::Black);
root->addChild(p);
Viewer viewer;
viewer.setSceneData(root);
viewer.getCamera()->setClearColor(Color(TangoColor::White).get());
viewer.run();
}
In the functions createStreet I use a Vec3dArray for the vertex array, whereas in the createPointer function, I use a Vec3Array. In the library I guess it expects all nodes
to be composed of floats or doubles, but not both. Changing these two functions solves the problem:
inline osg::Vec3 toVec3(const straph::Point& p, float elevation=0.0)
{
return osg::Vec3(float(p.x), float(p.y), elevation);
}
Geometry* createStreet(const straph::Polyline& path)
{
ref_ptr<Vec3Array> array (new Vec3Array(path.size()));
for (unsigned i = 0; i < path.size(); ++i) {
(*array)[i] = toVec3(path[i]);
}
Geometry* geom = new Geometry;
geom->setVertexArray(array.get());
geom->addPrimitiveSet(new osg::DrawArrays(GL_LINE_STRIP, 0, array->size()));
return geom;
}
Here a comment by Robert Osfield:
I can only provide a guess, and that would be that the Intel OpenGL doesn't handle double vertex data correctly, so you are stumbling across a driver bug.
In general OpenGL hardware is based around floating point maths so the drivers normally convert any double data you pass it into floats before passing it to the GPU. Even if the driver does this correctly this conversion process slows performance down so it's best to keep osg::Geometry vertex/texcoord/normal etc. data all in float arrays such as Vec3Array.
You can retain precision by translating your data to a local origin prior to conversion to float then place a MatrixTransform above your data to place it in the correct 3D position. The OSG by default uses double for all internal matrices that that when it accumulates the modelvew matrix during the cull traversal double precision is maintain for as long as possible before passing the final modelview matrix to OpenGL. Using this technique the OSG can handle whole earth data without any jitter/precision problems.

Mean shift implementation in C++

Can anyone recommend a lightweight mean shift clustering implementation in C++? I am already using OpenCV, however their mean shift implementation is for tracking, not clustering. I have seen EDISON, however, this is for image segmentation and not clustering.
I could implement it myself, however would rather not invest the time, and not take the risk of bugs.
Thanks
This is old, but I am working with mean shift right now so I thought it best to answer.
I think I understand the distinction you are making here, but when you say you are looking for mode detection this is vague in the technical sense as from the point of view of the algorithm as the algorithm inherently is for searching for "modes", which are the local minima or maxima depending on how you frame the optimization problem (Gradient descent or ascent).
This source, which was found on the EDISON site, claims to be a c++ implementation of the mean shift clustering algorithm, but as discussed above, clustering is the main implementation of the mode seeking behavior that all other uses of mean shift is based on, especially segmentation, so you can certainly use the EDISON source to find a clustering implementation, even if you have to search through it a bit.
I also found this Github project, for what it is worth, but I haven't worked with it before.
LAST NOTE: I also noticed you said "lightweight" implementation. Note that mean shift is not a very efficient algorithm (i think it is something like O(N^3), but I will check that). That said, it can still be efficiently implemented, though how that should be gauged is more ambiguous. Needless to say, Quick Shift, an attempt by UCLA researchers to solve the issues of the more efficient medoid shift, a similar non-parametric mode seeking algorithm, might be more like what you are looking for in a "lightweight" algorithm.
Here is my C++ version of mean shift object tracking method. To run the code successfully, you need to add the tf.h header file to the main code directory.
#include "tf.h" // include the header file
using namespace cv;
using namespace std;
#include <stdio.h>
makerect mkr; // rectangle for encompassing target
// rcs for row coordination of target window center
//ccs for column coordination of target window center
double rcs=0,ccs=0;
// w for width of target window
// l for length of target window
double W=70,L=60;
const int mySizes[3]={16,16,16}; // vector for number of histogram bins
cv::Mat q4hsv = Mat::zeros(3,mySizes,CV_64F); // initializing histogram variable
uchar nbins=16; // var for num of histogram bins
int main(void){
printf("enter 14 or 36: \t"); // enter number of input video name
uint flag; // var for video flag or number
cin>>flag;
Mat ref4frame; // reference frame which is used for initializing mean shift parameters
char filename [50];
sprintf(filename,"im%d.avi",flag);
VideoCapture capture(filename);
if( !capture.isOpened() )
throw "Error when reading steam_avi";
unsigned long int f4counter=0; // frame counter var
histhsv hsv; // instantiating histhsv class
for (f4counter=1;f4counter<=40000000;f4counter++){ // a big number to support many frames
capture >> ref4frame; //reading input image from specified directory
if( !(ref4frame.data )) // checking the read status
{
printf("Cannot load file image %s\n", filename);
break; }
uchar ndiv = uchar(256/nbins); // division value to which intesity values are divided
if (f4counter==1) { // special for 1st frame
char modelname[20];
if(flag==36){
sprintf(modelname,"data%d.png",flag);
}
else if (flag==14){
sprintf(modelname,"data%d.jpg",flag);
}
// imread is defined in tf.h
Mat img = imread(modelname,1);//reads 1st image
if( !(img.data ))//check if file loading is ok
{
printf("Cannot load file image %s\n", modelname);
break; }
hsv.img=img;//assign new image to hsv object (calculates hsv or rgb hist)
hsv.run();//run the histogram calculator
// assign temporary hsv object to reference hist q4hsv object
for (int i=0;i<16;i++){
for(int j=0;j<16;j++){
for(int k=0;k<16;k++){
q4hsv.at<double>(i,j,k)=hsv.q[i][j][k];
}
}
}
}
if(f4counter<5){averageglobalhsv(ref4frame,q4hsv,rcs,ccs);}
averagelocalhsv(ref4frame,q4hsv,rcs,ccs);//normalizing histogram values (0-1)
Point pt1; pt1.x=ccs; pt1.y=rcs;
int thickness=4;//thickness of marker - here is a circle
int lineType=8;
int shift=0;
RNG rng(0xFFFFFFFF);
cv::circle(ref4frame, pt1, 5, randomColor(rng), thickness, lineType,
shift); //marking object center with a circle
myimshow("reference frame",ref4frame);//show current image
waitKey(1);
}
int c=waitKey(0);
//release memory
ref4frame.release();
destroyAllWindows();
return 0;
}
here is the tf.h header file contents:
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc_c.h"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/features2d/features2d.hpp"
#include "opencv2\opencv.hpp"
#include "core\core.hpp"
#include <cstdio>
#include <iostream>
#include <fstream>
#include <math.h>
using namespace cv;
using namespace std;
// makerect class: to create the desired size encompassing window
class makerect
{
public:
double rcs,ccs,w,l; // ctl row, ctl column, width, length
uint rmin,rmax,cmin,cmax,height,length;//min max coordination vars
void run(void);
makerect();
};
makerect::makerect(){
rmin=0;rmax=0;cmin=0;cmax=0;rcs=0;ccs=0;w=0;l=0;
}
void makerect::run(){
//not that all points must be either integer or floating point
rmin=uint(rcs-floor(w/2));//min row coordination
rmax=uint(rmin+w);//max row coordination
cmin=uint (ccs-floor(l/2));//min column coordination
cmax=uint(cmin+l);//max column coordination
if(cmax>length){cmax=length;cmin=cmax-l;// checking column to be inside image}
if(rmax>height){rmax=height;rmin=rmax-w;//checking row to be inside image}
}
static Scalar randomColor(RNG& rng)
{
int icolor = (unsigned)rng;
return Scalar(icolor&255, (icolor>>8)&255, (icolor>>16)&255);
}
//myimshow: is a function to to show image
void myimshow(char* name4window,Mat &tmp4image){
namedWindow(name4window,1); imshow(name4window,tmp4image);
}
void averageglobalhsv(Mat ref4frame,Mat &f,double &rcs,double &ccs)
{
Mat img;
cvtColor(ref4frame,img,CV_BGR2HSV);//rgb2hsv conversion
uint n4rowsref=ref4frame.rows;// num of rows
uint n4colsref=ref4frame.cols;// num of cols
double *im4bp = new double [n4rowsref*n4colsref];//1-D dynamic array equal to image pixels
uint nbins=16;// num of histogram bins
uint ndiv=256/nbins; //division value to which intensities are divided
//vars for image moments (zero to second moments)
double m00=0,m01=0,m10=0,m20=0,m02=0,m11=0,w=0;
uint R=0,G=0,B=0; //red bin num, green bin num, blue bin num
for(uint i=0;i<n4rowsref;i++){ //loop through all image rows
for(uint j=0;j<n4colsref;j++){//loop through all image columns
Vec3b inten=img.at<Vec3b>(i,j);//a vector of three element
R=inten.val[2]; G=inten.val[1]; B=inten.val[0];
R/=ndiv; G/=ndiv; B/=ndiv;//calculating the bin to which current pixel intensity belong
im4bp[i*n4colsref+j]=1.3*sqrt(f.at<double>(R,G,B));//calculating spatial weighted kernel histogram formula
}
}
for(uint i=0;i<n4rowsref;i++){//loop through all image rows
for(uint j=0;j<n4colsref;j++){//loop through all image columns
w=im4bp[j+n4colsref*i];// weight for pixel at (i,j)
m01=m01+double(i)*w;//first moment on y/row coordination
m10=m10+double(j)*w;//first moment on x/column coordination
m00=m00+w;//zeroth moment which is sum of all moments
}
}
if(m00>0){
rcs=ceil(m01/m00);//central point for row
ccs=ceil(m10/m00);}//central point for column
delete im4bp;//cleaning memory
}
void averagelocalhsv(Mat ref4frame,Mat &f,double &rcs,double &ccs)
{
Mat img;
cvtColor(ref4frame,img,CV_BGR2HSV);
makerect mkr;
int sz[]={2,2};
uint n4rowsref=ref4frame.rows;
uint n4colsref=ref4frame.cols;
double *im4bp = new double [n4rowsref*n4colsref];
uint nbins=16;
uint ndiv=256/nbins;
double m00=0,m01=0,m10=0,m20=0,m02=0,m11=0,w=0,Dxx,Dyy,Dxy;
uint R=0,G=0,B=0;
for(uint i=0;i<n4rowsref;i++){
for(uint j=0;j<n4colsref;j++){
Vec3b inten=img.at<Vec3b>(i,j);
R=inten.val[2]; G=inten.val[1]; B=inten.val[0];
R/=ndiv; G/=ndiv; B/=ndiv;
im4bp[i*n4colsref+j]=1.3*sqrt(f.at<double>(R,G,B));
}
}
for(int iter=1;iter<5;iter++){
mkr.rcs=rcs;mkr.ccs=ccs;mkr.w=100;mkr.l=100;mkr.height=ref4frame.rows;
mkr.length=ref4frame.cols; mkr.run();
m00=0;m01=0;m10=0;m20=0;m02=0;m11=0;
for(uint i=mkr.rmin;i<mkr.rmax;i=i+1){
for(uint j=mkr.cmin;j<mkr.cmax;j=j+1){
w=im4bp[j+n4colsref*i];
m01=m01+double(i)*w;
m10=m10+double(j)*w;
m00=m00+w;
}
}
}
if(m00>0){
rcs=ceil(m01/m00);
ccs=ceil(m10/m00);
}
delete im4bp;
}
class histhsv{
public:
histhsv();
void run(void);
Mat img;
double q[16][16][16];
};
histhsv::histhsv(){};
void histhsv::run(void){
//Mat hsv4image;
double sum4hist=0;
uchar nbins=16;
uchar ndiv=256/nbins;
double wmax =0;;
double r_center=0;
double c_center =0;
r_center = img.rows/2;
c_center = img.cols/2;
for (int i=0;i<nbins;i++){for(int j=0;j<nbins;j++){for(int k=0;k<nbins;k++){
q[i][j][k]=0; } } };
cvtColor(img,img,CV_BGR2HSV);
int H=0,S=0,V=0;
for(int r=0;r<img.rows;r++){
for(int c=0;c<img.cols;c++){
Vec3b intensity = img.at<Vec3b>(r,c);
H=intensity.val[0]/ndiv;
S=intensity.val[1]/ndiv;
V=intensity.val[2]/ndiv;
q[H][S][V]+=wmax-(pow(r-r_center,2)+pow(c-c_center,2));
sum4hist+=q[H][S][V];
}
}
for (int i=0;i<nbins;i++){
for(int j=0;j<nbins;j++){
for(int k=0;k<nbins;k++){
q[i][j][k]/=sum4hist;
}
}
}
}