What would be the best algorithm to generate a list of vertices to draw a plane using triangle strips?
I'm looking for a function which receives the plane's width and height and returns a float array containing correctly indexed vertices.
width represents the number of vertices per row.
height represents the number of vertices per column.
float* getVertices( int width, int height ) {
...
}
void render() {
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, getVertices(width,heigth));
glDrawArrays(GL_TRIANGLE_STRIP, 0, width*height);
glDisableClientState(GL_VERTEX_ARRAY);
}
Thanks you all. I've coded this. Is it correct? Or is the generated strip somehow wrong?
int width;
int height;
float* vertices = 0;
int* indices = 0;
int getVerticesCount( int width, int height ) {
return width * height * 3;
}
int getIndicesCount( int width, int height ) {
return (width*height) + (width-1)*(height-2);
}
float* getVertices( int width, int height ) {
if ( vertices ) return vertices;
vertices = new float[ getVerticesCount( width, height ) ];
int i = 0;
for ( int row=0; row<height; row++ ) {
for ( int col=0; col<width; col++ ) {
vertices[i++] = (float) col;
vertices[i++] = 0.0f;
vertices[i++] = (float) row;
}
}
return vertices;
}
int* getIndices( int width, int height ) {
if ( indices ) return indices;
indices = new int[ iSize ];
int i = 0;
for ( int row=0; row<height-1; row++ ) {
if ( (row&1)==0 ) { // even rows
for ( int col=0; col<width; col++ ) {
indices[i++] = col + row * width;
indices[i++] = col + (row+1) * width;
}
} else { // odd rows
for ( int col=width-1; col>0; col-- ) {
indices[i++] = col + (row+1) * width;
indices[i++] = col - 1 + + row * width;
}
}
}
if ( (mHeight&1) && mHeight>2 ) {
mpIndices[i++] = (mHeight-1) * mWidth;
}
return indices;
}
void render() {
glEnableClientState( GL_VERTEX_ARRAY );
glVertexPointer( 3, GL_FLOAT, 0, getVertices(width,height) );
glDrawElements( GL_TRIANGLE_STRIP, getIndicesCount(width,height), GL_UNSIGNED_INT, getIndices(width,height) );
glDisableClientState( GL_VERTEX_ARRAY );
}
With width=4 and height=4 this is what I got:
And here I'm modifying some vertex height:
Here is some code that does this (not tested, but you get the idea at least):
void make_plane(int rows, int columns, float *vertices, int *indices) {
// Set up vertices
for (int r = 0; r < rows; ++r) {
for (int c = 0; c < columns; ++c) {
int index = r*columns + c;
vertices[3*index + 0] = (float) c;
vertices[3*index + 1] = (float) r;
vertices[3*index + 2] = 0.0f;
}
}
// Set up indices
int i = 0;
for (int r = 0; r < rows - 1; ++r) {
indices[i++] = r * columns;
for (int c = 0; c < columns; ++c) {
indices[i++] = r * columns + c;
indices[i++] = (r + 1) * columns + c;
}
indices[i++] = (r + 1) * columns + (columns - 1);
}
}
The first loop sets up the vertex array in a standard rectangular grid. There are R*C vertices.
The second loop sets up the indices. In general, there are two vertices per square in the grid. Each vertex will cause a new triangle to be drawn (with the previous two vertices), so each square is drawn with two triangles.
The first and last vertex at the start and end of each row is duplicated. This means there are two triangles of zero area (degenerate triangles) between each row. This allows us to draw the entire grid in one big triangle strip. This technique is called stitching.
none of the code above gives a correct mesh generation. A very good article about how to make a strip of triangles on a simple plane: http://www.learnopengles.com/android-lesson-eight-an-introduction-to-index-buffer-objects-ibos/
Here is my test code that actually tested and fully working:
int plane_width = 4; // amount of columns
int plane_height = 2; // amount of rows
int total_vertices = (plane_width + 1) * (plane_height + 1);
planeVert = new CIwFVec2[total_vertices];
memset(planeVert, 0, sizeof(CIwFVec2) * total_vertices);
int numIndPerRow = plane_width * 2 + 2;
int numIndDegensReq = (plane_height - 1) * 2;
int total_indices = numIndPerRow * plane_height + numIndDegensReq;
planeInd = new uint16[total_indices];
make_plane(plane_width, plane_height, planeVert, planeInd);
...
void make_plane(int width, int height, CIwFVec2 *vertices, uint16 *indices)
{
width++;
height++;
int size = sizeof(CIwFVec2);
// Set up vertices
for(int y = 0; y < height; y++)
{
int base = y * width;
for(int x = 0; x < width; x++)
{
int index = base + x;
CIwFVec2 *v = vertices + index;
v->x = (float) x;
v->y = (float) y;
Debug::PrintDebug("%d: %f, %f", index, v->x, v->y);
}
}
Debug::PrintDebug("-------------------------");
// Set up indices
int i = 0;
height--;
for(int y = 0; y < height; y++)
{
int base = y * width;
//indices[i++] = (uint16)base;
for(int x = 0; x < width; x++)
{
indices[i++] = (uint16)(base + x);
indices[i++] = (uint16)(base + width + x);
}
// add a degenerate triangle (except in a last row)
if(y < height - 1)
{
indices[i++] = (uint16)((y + 1) * width + (width - 1));
indices[i++] = (uint16)((y + 1) * width);
}
}
for(int ind=0; ind < i; ind++)
Debug::PrintDebug("%d ", indices[ind]);
}
I was doing something similar and using the first two answers I have come up with this (tested, C#, XNA)
// center x,z on origin
float offset = worldSize / 2.0f, scale = worldSize / (float)vSize;
// create local vertices
VertexPositionColor[] vertices = new VertexPositionColor[vSize * vSize];
for (uint z = 0; z < vSize; z++) {
for (uint x = 0; x < vSize; x++) {
uint index = x + (z * vSize);
vertices[index].Position = new Vector3((scale*(float)x) - offset,
heightValue,
(scale*(float)z) - offset);
vertices[index].Color = Color.White;
}
}
// create local indices
var indices = new System.Collections.Generic.List<IndexType>();
for (int z = 0; z < vSize - 1; z++) {
// degenerate index on non-first row
if (z != 0) indices.Add((IndexType)(z * vSize));
// main strip
for (int x = 0; x < vSize; x++) {
indices.Add((IndexType)(z * vSize + x));
indices.Add((IndexType)((z + 1) * vSize + x));
}
// degenerate index on non-last row
if (z != (vSize-2)) indices.Add((IndexType)((z + 1) * vSize + (vSize - 1)));
}
This is easily convertable to c++, just make indices an std::vector.
The notable features for my solution are that:
a) It doesn't need to change the winding order per substrip - adding two points creates two degenerate triangles, so the order is correct for the next substrip.
b) You should conditionally add the first and last dg triangle vertices.
Related
I had glDrawElements working consistently, initially with a simple box and then with more complex shapes made up of a large amount of vertices. Then it simply stopped drawing the mesh. I have taken the code back to it's most basic, just drawing 2 triangles to make a 2D square. This also no longer works.
void createMesh(void) {
float vertices[12];
vertices[0] = -0.5; vertices[1] = -0.5; vertices[2] = 0.0; // Bottom left corner
vertices[3] = -0.5; vertices[4] = 0.5; vertices[5] = 0.0; // Top left corner
vertices[6] = 0.5; vertices[7] = 0.5; vertices[8] = 0.0; // Top Right corner
vertices[9] = 0.5; vertices[10] = -0.5; vertices[11] = 0.0; // Bottom right corner
short indices[] = { 0, 1, 2, 0, 2, 3};
glEnableClientState(GL_VERTEX_ARRAY); // Enable Vertex Arrays
glVertexPointer(3, GL_FLOAT, 0, vertices); // Set The Vertex Pointer To Our Vertex Data
glDrawElements(GL_TRIANGLES,6 , GL_UNSIGNED_SHORT, indices);
glDisableClientState(GL_VERTEX_ARRAY);
}
The more advanced code that used to work is shown below:
void createMesh(void) {
float vertices[(amountOfHorizontalScans * 480 * 3)];// Amount of vertices
//build the array of vertices from a matrix of data
int currentVertex = -1;
std::vector <std::vector<double>> currentPointCloudMatrix = distanceCalculator.getPointCloudMatrix();
double plotY = 0;
double plotX = 0;
for (int j = 0; j < currentPointCloudMatrix.size(); j++){
std::vector <double> singleDistancesVector = currentPointCloudMatrix.at(j);
for (int i = 0; i < singleDistancesVector.size(); i++){
if (singleDistancesVector.at(i) != 0){
vertices[++currentVertex] = plotX;
vertices[++currentVertex] = plotY;
vertices[++currentVertex] = singleDistancesVector.at(i);
}
plotX += 0.1;
}
plotX = 0;
plotY += 0.2; //increment y by 0.02
}
//Creating the array of indices, 480 is the amount of columns
int i = 0;
short indices2[(amountOfHorizontalScans * 480 * 3)];
for (int row = 0; row<amountOfHorizontalScans - 1; row++) {
if ((row & 1) == 0) { // even rows
for (int col = 0; col<480; col++) {
indices2[i++] = col + row * 480;
indices2[i++] = col + (row + 1) * 480;
}
}
else { // odd rows
for (int col = 480 - 1; col>0; col--) {
indices2[i++] = col + (row + 1) * 480;
indices2[i++] = col - 1 + +row * 480;
}
}
}
glEnableClientState(GL_VERTEX_ARRAY); // Enable Vertex Arrays
glVertexPointer(3, GL_FLOAT, 0, vertices); // Set The Vertex Pointer To Our Vertex Data
glDrawElements(GL_TRIANGLE_STRIP, (amountOfHorizontalScans * 480 * 3), GL_UNSIGNED_SHORT, indices2);
glDisableClientState(GL_VERTEX_ARRAY);
}
I am at a complete loss as to why it has stopped working as it was working perfectly for a good number of runs, then just completely stopped. I have debugged through and all the code is being reached, also the vertices and indices are populated with data. What could cause this to stop working?
EDIT:
So I am really quite confused now. I came back to this issue this morning, and everything worked fine again, as in the meshes would draw with no issues. After doing some tests and running the program a number of times it has simply stopped drawing meshes again!
Could this be something memory related? I am not 100% sure on how glDrawElements stores the data passed to it, so could it be that I have to clear something somewhere that I keep filling up with data?
You cannot allocate dynamically arrays in stack:
short indices2[(amountOfHorizontalScans * 480 * 3)];
In code:
short indices2[(amountOfHorizontalScans * 480 * 3)];
for (int row = 0; row<amountOfHorizontalScans - 1; row++) {
if ((row & 1) == 0) { // even rows
for (int col = 0; col<480; col++) {
indices2[i++] = col + row * 480;
indices2[i++] = col + (row + 1) * 480;
}
}
else { // odd rows
for (int col = 480 - 1; col>0; col--) {
indices2[i++] = col + (row + 1) * 480;
indices2[i++] = col - 1 + +row * 480;
}
}
}
Must be
short* indices2 = new short[(amountOfHorizontalScans * 480 * 3)];
than free allocated memory
delete [] indices2;
Triangle strip is pretty tricky mode did you try to work directly with GL_TRIANGLES.
i want to transport the follow codes into c++:
gaussFilter = fspecial('gaussian', 2*neighSize+1, 0.5*neighSize);
pointFeature = imfilter(pointFeature, gaussFilter, 'symmetric');
where the pointFeature is a [height, width, 24] array.
i try to use filter2D, but it only support the 2D array.
so i want to know if there are functions in opencv that can filtering the multi-dimensional array?
You can use separable kernel filters for make anydimentional filter.
If you are using OpenCV, you could try this for a 3 Dimensional MatND:
void Smooth3DHist(cv::MatND &hist, const int& kernDimension)
{
assert(hist.dims == 3);
int x_size = hist.size[0];
int y_size = hist.size[1];
int z_size = hist.size[2];
int xy_size = x_size*y_size;
cv::Mat kernal = cv::getGaussianKernel(kernDimension, -1, CV_32F);
// Filter XY dimensions for every Z
for (int z = 0; z < z_size; z++)
{
float *ind = (float*)hist.data + z * xy_size; // sub-matrix pointer
cv::Mat subMatrix(2, hist.size, CV_32F, ind);
cv::sepFilter2D(subMatrix, subMatrix, CV_32F, kernal.t(), kernal, Point(-1,-1), 0.0, cv::BORDER_REPLICATE);
}
// Filter Z dimension
float* kernGauss = (float *)kernal.data;
unsigned kernSize = kernal.total();
int kernMargin = (kernSize - 1)/2;
float* lineBuffer = new float[z_size + 2*kernMargin];
for (int y = 0; y < y_size; y++)
{
for (int x = 0; x < x_size; x++)
{
// Copy along Z dimension into a line buffer
float* z_ptr = (float*)hist.data + y * x_size + x;//same as hist.ptr<float>(0, y, x)
for (int z = 0; z < z_size; z++, z_ptr += xy_size)
{
lineBuffer[z + kernMargin] = *z_ptr;
}
// Replicate borders
for (int m = 0; m < kernMargin; m++)
{
lineBuffer[m] = lineBuffer[kernMargin];// replicate left side
lineBuffer[z_size + 2*kernMargin - 1 - m] = lineBuffer[kernMargin + z_size - 1];//replicate right side
}
// Filter line buffer 1D - convolution
z_ptr = (float*)hist.data + y * x_size + x;
for (int z = 0; z < z_size; z++, z_ptr += xy_size)
{
*z_ptr = 0.0f;
for (unsigned k = 0; k < kernSize; k++)
{
*z_ptr += lineBuffer[z+k]*kernGauss[k];
}
}
}
}
delete [] lineBuffer;
}
So I have an image that I want to overlay with a checkerboard pattern.
This is what I have come up with so far:
for ( uint_8 nRow = 0; nRow < image.width(); ++nRow)
for (uint_8 nCol = 0; nCol < image.height(); ++nCol)
if(((nRow/20 + nCol/20) % 2) == 0)
memset(&image.data[nCol + nRow], 0, 1);
Produces a white image unfortunately. I dont think this is very performant because memset is called for every single pixel in the image instead of multiple.
Why does this code not produce a chckerboard pattern? How would you improve it?
For better performance, don't treat the image as a 2-dimensional entity. Instead, look at it as a 1D array of continuous data, where all lines of the image are arranged one after the other.
With this approach, you can write the pattern in one go with a single loop, where in every iteration you memset() multiple adjacent pixels and increase the index by twice the amount of pixels you set:
int data_size = image.width() * image.height();
for (auto it = image.data; it < image.data + data_size; it += 20) {
memset(it, 0, 20);
if (((it - data) + 40) % (20 * 400) == 0) {
it += 40;
} else if (((it - data) + 20) % (20 * 400) != 0) {
it += 20;
}
}
(Replace auto with the type of image.data if you're not using C++11; I suspect it's unsigned char*.)
This is quite friendly for the CPU cache prefetch. It's also friendly for the compiler, which can potentially vectorize and/or perform loop unrolling.
If you have an image's dimensions which are multiple of the checker square size :
(I coded in C but it is fairly easy to transpose to C++)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define uint unsigned int
#define WIDTH 40
#define HEIGHT 40
#define BLOCK_SIZE 5
void create_checker_row(uint* row, uint size_block, uint nb_col, uint offset )
{
uint ic;
for (ic = size_block*offset ; ic < nb_col; ic+= 2*size_block )
{
memset( (row + ic) , 0, size_block*sizeof(uint) );
}
}
int main()
{
uint ir,ic;
// image creation
uint* pixels = (uint*) malloc(WIDTH*HEIGHT*sizeof(uint));
for (ir = 0; ir < WIDTH; ir++)
{
for ( ic = 0; ic < HEIGHT; ic++)
{
// arbitrary numbers
pixels[ir*WIDTH + ic] = (ir*WIDTH + ic) % 57 ;
printf("%d,", pixels[ir*WIDTH + ic] );
}
printf("\n");
}
for (ir = 0; ir < WIDTH; ir++)
{
create_checker_row( pixels + ir*WIDTH , // pointer at the beggining of n-th row
BLOCK_SIZE , // horizontal length for square
WIDTH , // image width
(ir/BLOCK_SIZE) % 2 // offset to create the checker pattern
);
}
// validation
printf("\n");
printf("Validation \n");
printf("\n");
for (ir = 0; ir < WIDTH; ir++)
{
for ( ic = 0; ic < HEIGHT; ic++)
{
printf("%d,", pixels[ir*WIDTH + ic] );
}
printf("\n");
}
return 0;
}
Seems pretty checkered for me : http://ideone.com/gp9so6
I use this and stb_image_write.h
#include <stdlib.h>
#include <stb_image_write.h>
int main(int argc, char *argv[])
{
const int w = 256, h = 256, ch = 4, segments = 8, box_sz = w / segments;
unsigned char rgba_fg[4] = {255, 255, 0, 255}; //yellow
unsigned char rgba_bg[4] = {255, 0, 0, 255}; //red
unsigned char* data = calloc(w * h * ch, sizeof(unsigned char));
int swap = 0;
int fill = 0; /* set to 1 to fill fg first*/
unsigned char* col = NULL;
for(int i = 0; i < w * h; i++)
{
if(i % (w * box_sz) == 0 && i != 0)
swap = !swap;
if(i % box_sz == 0 && i != 0)
fill = !fill;
if(fill)
{
if(swap)
col = rgba_bg;
else
col = rgba_fg;
}else
{
if(swap)
col = rgba_fg;
else
col = rgba_bg;
}
for(int j = 0; j < ch; j++)
{
data[i*ch + j] = col[j];
}
}
stbi_write_png("checker.png", w, h, ch, data, 0);
free(data);
return 0;
}
Its a bit slow with large images but gets the job done if you cache them
I am getting a bit confused as to why my manually created mesh is not appearing correctly. I have created the vertex and index buffers and they seem (although I am not 100% sure) to contain the correct values.
Essentially I am creating a grid of mapSize * mapSize vetrices, at a height of 0, then creating the triangles out of them.
void TerrainGeneration::createTerrainMesh() {
/// Create the mesh via the MeshManager
Ogre::MeshPtr msh = Ogre::MeshManager::getSingleton().createManual("TerrainTest", "General");
Ogre::SubMesh* sub = msh->createSubMesh();
const size_t nVertices = mapSize*mapSize;
const size_t vbufCount = 3*2*nVertices;
float vertices[vbufCount];
size_t vBufCounter = 0;
for(int z = 0; z < mapSize; z++) {
for(int x = 0; x < mapSize; x++) {
//Position
vertices[vBufCounter] = x;
vertices[vBufCounter+1] = 0;
vertices[vBufCounter+2] = z;
//Normal
vertices[vBufCounter+3] = 0;
vertices[vBufCounter+4] = 1;
vertices[vBufCounter+5] = 0;
vBufCounter += 6;
}
}
Ogre::RenderSystem* rs = Ogre::Root::getSingleton().getRenderSystem();
Ogre::RGBA colours[nVertices];
Ogre::RGBA *pColour = colours;
//Create triangles
const size_t ibufCount = 6*(mapSize - 1)*(mapSize - 1);
unsigned int faces[ibufCount];
size_t iBufCounter = 0;
for(int x=0; x <= mapSize -2; x++) {
for(int y=0; y <= mapSize -2; y++) {
faces[iBufCounter] = vertices[(y*mapSize) + x];
faces[iBufCounter+1] = vertices[((y+1)*mapSize) + x];
faces[iBufCounter+2] = vertices[((y+1)*mapSize) + (x+1)];
faces[iBufCounter+3] = vertices[(y*mapSize) + x];
faces[iBufCounter+4] = vertices[((y+1)*mapSize) + (x+1)];
faces[iBufCounter+5] = vertices[(y*mapSize) + (x+1)];
iBufCounter += 6;
}
}
/// Create vertex data structure for n*n vertices shared between submeshes
msh->sharedVertexData = new Ogre::VertexData();
msh->sharedVertexData->vertexCount = nVertices;
/// Create declaration (memory format) of vertex data
Ogre::VertexDeclaration* decl = msh->sharedVertexData->vertexDeclaration;
size_t offset = 0;
// 1st buffer
decl->addElement(0, offset, Ogre::VET_FLOAT3, Ogre::VES_POSITION);
offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);
decl->addElement(0, offset, Ogre::VET_FLOAT3, Ogre::VES_NORMAL);
offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);
/// Allocate vertex buffer of the requested number of vertices (vertexCount)
/// and bytes per vertex (offset)
Ogre::HardwareVertexBufferSharedPtr vbuf =
Ogre::HardwareBufferManager::getSingleton().createVertexBuffer(
offset, msh->sharedVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY);
/// Upload the vertex data to the card
vbuf->writeData(0, vbuf->getSizeInBytes(), vertices, true);
/// Set vertex buffer binding so buffer 0 is bound to our vertex buffer
Ogre::VertexBufferBinding* bind = msh->sharedVertexData->vertexBufferBinding;
bind->setBinding(0, vbuf);
/// Allocate index buffer of the requested number of vertices (ibufCount)
Ogre::HardwareIndexBufferSharedPtr ibuf = Ogre::HardwareBufferManager::getSingleton().
createIndexBuffer(
Ogre::HardwareIndexBuffer::IT_16BIT,
ibufCount,
Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY);
/// Upload the index data to the card
ibuf->writeData(0, ibuf->getSizeInBytes(), faces, true);
/// Set parameters of the submesh
sub->useSharedVertices = true;
sub->indexData->indexBuffer = ibuf;
sub->indexData->indexCount = ibufCount;
sub->indexData->indexStart = 0;
/// Set bounding information (for culling)
msh->_setBounds(Ogre::AxisAlignedBox(-5000,-5000,-5000,5000,5000,5000));
//msh->_setBoundingSphereRadius(Ogre::Math::Sqrt(3*100*100));
/// Notify -Mesh object that it has been loaded
msh->load();
}
I initialise the mesh and load it as follows
Ogre::Entity* thisEntity = mSceneMgr->createEntity("cc", "TerrainTest", "General");
thisEntity->setMaterialName("Examples/Rockwall");
Ogre::SceneNode* thisSceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
thisSceneNode->setPosition(0, 0, 0);
thisSceneNode->attachObject(thisEntity);
Any insight would be greatly appreciated.
Ok so I got an answer off the Ogre3d forums from a very helpful person called bstone.
It turns out that when creating my index list to create the faces I was mistakenly passing coordinates from the vertex list rather than indexes of the vertices.
faces[iBufCounter] = vertices[(y*mapSize) + x];
faces[iBufCounter+1] = vertices[((y+1)*mapSize) + x];
faces[iBufCounter+2] = vertices[((y+1)*mapSize) + (x+1)];
faces[iBufCounter+3] = vertices[(y*mapSize) + x];
faces[iBufCounter+4] = vertices[((y+1)*mapSize) + (x+1)];
faces[iBufCounter+5] = vertices[(y*mapSize) + (x+1)];
Should have been
faces[iBufCounter] = (y*mapSize) + x;
faces[iBufCounter+1] = ((y+1)*mapSize) + x;
faces[iBufCounter+2] = ((y+1)*mapSize) + (x+1);
faces[iBufCounter+3] = (y*mapSize) + x;
faces[iBufCounter+4] = ((y+1)*mapSize) + (x+1);
faces[iBufCounter+5] = (y*mapSize) + (x+1);
However I still have a problem in my code somewhere, although from what others have said it probably isn't in this code that i've posted.
Another user also proposed that I create the terrain ina much simpler way and posted the following code
int mapSize = 16;
Ogre::ManualObject *man = m_sceneManager->createManualObject("TerrainTest");
man->begin("Examples/Rockwall",Ogre::RenderOperation::OT_TRIANGLE_LIST);
for(int z = 0; z < mapSize; ++z)
{
for(int x = 0; x < mapSize; ++x)
{
man->position(x,0,z);
man->normal(0,1,0);
man->textureCoord(x,z);
}
}
for(int z = 0; z < mapSize-1; ++z)
{
for(int x = 0; x < mapSize-1; ++x)
{
man->quad((x) + (z) * mapSize, (x) + (z + 1) * mapSize, (x + 1) + (z + 1) * mapSize, (x + 1) + (z) * mapSize);
}
}
man->end();
m_sceneManager->getRootSceneNode()->attachObject(man);
I want the data at pixel to be compared with the colour and then i want to find contour then take centroid points of the contour ,so i am using like this to find countourdata am i wrong at this statement
int pos = i * w * Channels + j; //channels is 3 as rgb
// if any data exists
if (data->imageData[pos]>0)
Code is like this
for (int i = x; i < x+h; i++) //height of frame pixels
{
for (int j = y; j < y+w; j++)//width of frame pixels
{
int pos = i * w * Channels + j; //channels is 3 as rgb
// if any data exists
if (data->imageData[pos]>0) //Taking data (here is the problem how to take)
{
xPos += j;
yPos += i;
nPix++;
}
}
}
I use the following code structure
/**
* #brief Calculate greeness from an RGB image
*
* Performs the greeness pixelwise transform on the input image.
* Greeness is defined as
* Greeness = 255*G/sqrt(R^2+G^2+B^2)
* The function assumes that the resolution of the two images are identical.
*
* #param imSrc Input RGB image.
* #param imDst Output grayscale (greeness) image.
*/
void rgbToGreeness( IplImage *imSrc , IplImage* imDst) {
// Allocate variables
int tmp_pix;
uchar * _SrcPtr, * _DstPtr;
// Iterate over the image line by line
for(int y = 0 ; y < imSrc->height ; y++ )
{
// Locate pointers to the first data element in the current line
_SrcPtr = ( uchar* )( imSrc->imageData + y * imSrc->widthStep );
_DstPtr = ( uchar* )( imDst->imageData + y * imDst->widthStep );
// Iterate over the elements in the current line
for( int x = 0 ; x < imSrc->width ; x++ )
{
//2*G-B-R - Excessive green
tmp_pix = (int) (255*_SrcPtr[3*x+1]/pow(pow((float)_SrcPtr[3*x],2) + pow((float)_SrcPtr[3*x+1], 2) + pow((float)_SrcPtr[3*x+2], 2), (float) 0.5));
//If value is larger than 255, set it to 255 and lower than 0 set it to 0
_DstPtr[x] = (uchar) ( ( tmp_pix < 0 ) ? 0 : ( ( tmp_pix > 255 ) ? 255 : tmp_pix ) );
}
}
}
Here is some code to access RGB data of a pixel in an image
IplImage* img=cvLoadImage(fileName);
CvScalar s;
s=cvGet2D(img,i,j); // get the (i,j) pixel value
s.val[0]=111; // B-channel
s.val[1]=111; // G-channel
s.val[2]=111; // R-channel
cvSet2D(img,i,j,s); // set the (i,j) pixel value
Source (modified a little): http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html#SECTION00053000000000000000
As requested here is my exact code where i want to calculate centroids from contour
My exact code is like this
1) Taking RGB image as input
2) x=0,y=0,w=width of frame,h=height of frame.are the data passing
void cRecursiveCentroids::ComputeCentroid(int x, int y, int w, int h, IplImage *data, bool splitOnUpDown, int level, int id, int addToId){
if (level == m_Levels-1 ) return;
int Channels = data->nChannels; // Number of channels
std::cout << "Channels: " << Channels << "\n";
int xPos = 0;
int yPos = 0;
int nPix = 0;
for (int i = x; i < x+h; i++) //Tracing the contour
{
for (int j = y; j < y+w; j++)
{
int pos = i * m_Wid * Channels + j; // Here may be the error i am thinking
// if any data exists
if (data->imageData[pos]>0)
{
xPos += j;
//std::cout << "xPos: " << xPos << "\n";
yPos += i;
// std::cout << "yPos: " << yPos << "\n";
nPix++;
}
}
}
Check = nPix;
if (nPix > 0){ // Calculating Position
xPos = (int)((float)xPos / (float)nPix);
yPos = (int)((float)yPos / (float)nPix);
int num = ( id + addToId ) > 16 ? 16 : (id+addToId);
m_Cent[num].posx = xPos;
m_Cent[num].posy = yPos;
m_Cent[num].level = level;
splitOnUpDown = !splitOnUpDown;
level = level+1;
if (splitOnUpDown) //Recursive calling for centroids
{
id *= 2;
ComputeCentroid(x,y,w,(yPos - y), data, splitOnUpDown, level, id, addToId);
ComputeCentroid(x,yPos,w,h-(yPos-y), data, splitOnUpDown, level, id+1, addToId);
} else {
id *= 2;
ComputeCentroid(x,y,(xPos-x),h, data, splitOnUpDown, level, id, addToId);
ComputeCentroid(xPos,y,w - (xPos-x),h, data, splitOnUpDown, level, id+1, addToId);
}
}
DrawCentroidPoints(); //Draw Centroid Points
}