How do I produce a Negative Buffer of a polygon in GEOS? - buffer

I am trying to produce a negative buffer (offset) of a polygon using GEOS (with the c api).
So far I have accomplished a positive offset (see image), however when I try to make the width '-'ve, it produces nothing. I suspect that 'offset' requires clipping somehow prior to retrieving the exterior ring.
Had a good look through documentation but can't solve it, any help would be greatly appreciated!!
My code is below if that helps at all:
#include <geos_c.h>
// Define coordinate sequence
int noPoints = 6 + 1; // +1 because it is a loop
GEOSCoordSequence* points = GEOSCoordSeq_create(noPoints, 2 /*# ordinates*/);
GEOSCoordSeq_setX(points, 0, 0);
GEOSCoordSeq_setY(points, 0, 0);
GEOSCoordSeq_setX(points, 1, 0);
GEOSCoordSeq_setY(points, 1, 50);
GEOSCoordSeq_setX(points, 2, 50);
GEOSCoordSeq_setY(points, 2, 50);
GEOSCoordSeq_setX(points, 3, 50);
GEOSCoordSeq_setY(points, 3, 0);
GEOSCoordSeq_setX(points, 4, 30);
GEOSCoordSeq_setY(points, 4, 15);
GEOSCoordSeq_setX(points, 5, 20);
GEOSCoordSeq_setY(points, 5, 15);
GEOSCoordSeq_setX(points, noPoints-1, 0);
GEOSCoordSeq_setY(points, noPoints-1, 0);
// Define linear ring
GEOSGeometry* ploop = GEOSGeom_createLinearRing(points);
// Define offset
double width = 15;
int quadsegs = 100;
int endCapStyle = 1;
int joinStyle = 1;
double mitreLimit = 1.0;
const GEOSGeometry* offset = GEOSBufferWithStyle(ploop, width, quadsegs, endCapStyle, joinStyle, mitreLimit);
// Get exterior ring
const GEOSGeometry* exteriorRing = GEOSGetExteriorRing(offset);
// Convert to coord sequence and draw points
const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq(exteriorRing);
uint numPoints = GEOSGeomGetNumPoints(exteriorRing);
double xCoord, yCoord;
for (uint p = 0; p < numPoints; p++) {
GEOSCoordSeq_getX(coordSeq, p, &xCoord);
GEOSCoordSeq_getY(coordSeq, p, &yCoord);
printf("x: %g\ty:%g\n", xCoord, yCoord);
drawPoint(xCoord, yCoord);
}
Thanks in advance!

Finally solved it! the geometry I was feeding in was a linear ring, rather than a polygon. For anyone with a similar issue in the future:
Replace:
GEOSGeometry* ploop = GEOSGeom_createLinearRing(points);
With:
GEOSGeometry* rloop = GEOSGeom_createLinearRing(points);
const GEOSGeometry* ploop = GEOSGeom_createPolygon(rloop, NULL, 0);

Related

Strange errors during drawing hollow circles in the 3D space

I am trying to draw two hollow circles that are surrounding a cube which is located at the 0, 0, 0 position..
so far I've implemented the cube and the two circles here is what I get.
there are two strange things happening here.
One is that I want to draw the circles but I can see the lines radiating from the origin.
and two is that interpolated colors, even though I set just one color for the fragment shader.
here is you can see clearly those lines with interpolated color...
here is my vertex shader code and the fragment shader code
"use strict";
const loc_aPosition = 1;
const loc_aColor = 2;
const loc_UVCoord = 3;
const VSHADER_SOURCE =
`#version 300 es
layout(location=${loc_aPosition}) in vec4 aPosition;
layout(location=${loc_aColor}) in vec4 aColor;
layout(location=${loc_UVCoord}) in vec2 UVCoord;
out vec4 vColor;
out vec2 vUVCoord;
uniform mat4 uMVP;
void main()
{
gl_Position = uMVP * aPosition;
vColor = aColor;
vUVCoord = UVCoord;
}`;
const FSHADER_SOURCE =
`#version 300 es
precision mediump float;
in vec4 vColor;
out vec4 fColor;
void main()
{
fColor = vColor;
}`;
and the initilize functions for the two circles and there is the only difference is the target plane.
function init_equator(gl)
{
let vertices = []; // for the vertices
let color = [1, 0, 0]; // red color
for(var i = 0; i <= 360; i+=10)
{
let j = i * Math.PI/180;
let vert = [R * Math.cos(j), 0, R * Math.sin(j)]; // drawing a circle at the XZ plane since it has to be an equator for the cube...
vertices.push( vert[0], vert[1], vert[2] ); // push the vertices
vertices.push( color[0], color[1], color[2]); // set the color
}
const SZ = vertices.BYTES_PER_ELEMENT;
let vao = gl.createVertexArray();
gl.bindVertexArray(vao);
let vbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.vertexAttribPointer(loc_aPosition, 3, gl.FLOAT, false, SZ * 6, 0); // stride is 6, 3 for positions and 3 for the color
gl.enableVertexAttribArray(loc_aPosition);
gl.vertexAttribPointer(loc_aColor, 3, gl.FLOAT, false, SZ * 6, SZ * 3); // stride is 6, offset is this is because 3 color elements are located after 3 position elements..
gl.enableVertexAttribArray(loc_aColor);
gl.bindVertexArray(null);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
return { vao, n : vertices.length / 3 }; // since it has three coordinates so devide by 3
}
function init_latitude(gl)
{
let vertices = []; // for the vertices
let color = [1, 0, 0]; // supposed to be the red
for(var i = 0; i <= 360; i+=10)
{
let j = i * Math.PI/180;
let vert = [0, R * Math.cos(j), R * Math.sin(j)]; // drawing a circle on the YZ plane
vertices.push( vert[0], vert[1], vert[2] );
vertices.push( color[0], color[1], color[2]);
}
const SZ = vertices.BYTES_PER_ELEMENT;
let vao = gl.createVertexArray();
gl.bindVertexArray(vao);
let vbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.vertexAttribPointer(loc_aPosition, 3, gl.FLOAT, false, SZ * 6, 0); // stride is 6, 3 for positions and 3 for the color
gl.enableVertexAttribArray(loc_aPosition);
gl.vertexAttribPointer(loc_aColor, 3, gl.FLOAT, false, SZ * 6, SZ * 3); // stride is 6, offset is this is because 3 color elements are located after 3 position elements..
gl.enableVertexAttribArray(loc_aColor);
gl.bindVertexArray(null);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
return { vao, n : vertices.length / 3 }; // since it has three coordinates so devide by 3
}
I refer these drawing fucntions from here drawing circle
in the main function I called the draw function like this..
........
MVP.setOrtho(LEFT, RIGHT, BOTTOM, TOP, NEAR, FAR); // setting MVP matrix to orthographic mode
MVP.lookAt(FIXED_X, FIXED_Y, FIXED_Z, 0,0,0, 0,1,0); // Eye position x, y, z Look at position 0, 0, 0 Up vector 0, 1, 0
gl.uniformMatrix4fv(loc_MVP, false, MVP.elements);
gl.bindVertexArray(cube.vao);
gl.drawElements(gl.TRIANGLES, cube.n, gl.UNSIGNED_BYTE, 0)
gl.bindVertexArray(null);
gl.bindVertexArray(equator.vao);
gl.drawArrays(gl.LINE_LOOP, 0, equator.n);
gl.bindVertexArray(null);
gl.bindVertexArray(latitudeCircle.vao);
gl.drawArrays(gl.LINE_LOOP, 0, latitudeCircle.n);
gl.bindVertexArray(null);
I have no ideas why the lines are radiating from the origin and the mixed color...
could somebody help me?
this line, which appears twice in the code you posted
const SZ = vertices.BYTES_PER_ELEMENT;
is SZ will be undefined. vertices is a native JavaScript array, not a typedarray array like Float32Array. After that every calculation with SZ will be 0 or NaN
In other words these lines
gl.vertexAttribPointer(loc_aPosition, 3, gl.FLOAT, false, SZ * 6, 0);
gl.vertexAttribPointer(loc_aColor, 3, gl.FLOAT, false, SZ * 6, SZ * 3);
Will be
gl.vertexAttribPointer(loc_aPosition, 3, gl.FLOAT, false, 0, 0);
gl.vertexAttribPointer(loc_aColor, 3, gl.FLOAT, false, 0, 0);
Which means every other position is a color, and every other color is a position which explains why lines go to the center and why colors are interpolated.
Note that if you had stepped through the code in the debugger you'd have probably seen this issue so it would be good to learn how to use the debugger.
Also FYI unrelated to your issue you don't need to call gl.bindVertexArray twice in a row, once with null and once with the next thing you want to draw with.
this
gl.bindVertexArray(cube.vao);
gl.drawElements(gl.TRIANGLES, cube.n, gl.UNSIGNED_BYTE, 0)
gl.bindVertexArray(null);
gl.bindVertexArray(equator.vao);
gl.drawArrays(gl.LINE_LOOP, 0, equator.n);
gl.bindVertexArray(null);
gl.bindVertexArray(latitudeCircle.vao);
gl.drawArrays(gl.LINE_LOOP, 0, latitudeCircle.n);
gl.bindVertexArray(null);
can just be this
gl.bindVertexArray(cube.vao);
gl.drawElements(gl.TRIANGLES, cube.n, gl.UNSIGNED_BYTE, 0)
gl.bindVertexArray(equator.vao);
gl.drawArrays(gl.LINE_LOOP, 0, equator.n);
gl.bindVertexArray(latitudeCircle.vao);
gl.drawArrays(gl.LINE_LOOP, 0, latitudeCircle.n);
gl.bindVertexArray(null); // this is also not technically needed
Also also, you can use the spread operator.
This
vertices.push( vert[0], vert[1], vert[2] ); // push the vertices
vertices.push( color[0], color[1], color[2]); // set the color
can be this
vertices.push( ...vert ); // push the vertices
vertices.push( ...color ); // set the color
Also you might find these tutorials useful.

Plot histogram of Sobel operator magnitude and angle in OpenCV

I want to plot histogram in OpenCV C++. The task is that x-axis should be angle and y-axis should be magnitude of histogram. I calculate magnitude and angle by using Sobel operator. Now how can I plot histogram by using magnitude and angle?
Thanks in advance. The simple code of problem is
// Read image
Mat img = imread("abs.jpg");
img.convertTo(img, CV_32F, 1 / 255.0);
/*GaussianBlur(img, img, Size(3, 3), 0, 0, BORDER_CONSTANT);*/
// Calculate gradients gx, gy
Mat gx, gy;
Sobel(img, gx, CV_32F, 1, 0, 1);
Sobel(img, gy, CV_32F, 0, 1, 1);
// C++ Calculate gradient magnitude and direction (in degrees)
Mat mag, angle;
cartToPolar(gx, gy, mag, angle, 1);
imshow("magnitude of image is", mag);
imshow("angle of image is", angle);
Ok, So the first part of it is to calculate the histogram of each of them. Since both are separated already (in their own Mat) we do not have to split them or anything, and we can use them directly in the calcHist function of OpenCV.
By the documentation we have:
void calcHist(const Mat* images, int nimages, const int* channels, InputArray mask, OutputArray hist, int dims, const int* histSize, const float** ranges, bool uniform=true, bool accumulate=false )
So you would have to do:
cv::Mat histMag, histAng;
// number of bins of the histogram, adjust to your liking
int histSize = 10;
// degrees goes from 0-360 if radians then change acordingly
float rangeAng[] = { 0, 360} ;
const float* histRangeAng = { rangeAng };
double minval, maxval;
// get the range for the magnitude
cv::minMaxLoc(mag, &minval, &maxval);
float rangeMag[] = { static_cast<float>(minval), static_cast<float>(maxval)} ;
const float* histRangeMag = { rangeMag };
cv::calcHist(&mag, 1, 0, cv::NoArray(), histMag, 1, &histSize, &histRangeMag, true, false);
cv::calcHist(&angle, 1, 0, cv::NoArray(), histAng, 1, &histSize, &histRangeAng, true, false);
Now you have to plot the two histograms found in histMag and histAng.
In the turtorial I posted in the comments you have lines in the plot, for the angle it would be something like this:
// Draw the histograms for B, G and R
int hist_w = 512; int hist_h = 400;
int bin_w = cvRound( (double) hist_w/histSize );
cv::Mat histImage( hist_h, hist_w, CV_8UC3, Scalar( 0,0,0) );
/// Normalize the result to [ 0, histImage.rows ]
cv::normalize(histAng, histAng, 0, histImage.rows, cv::NORM_MINMAX, -1, Mat() );
// Draw the lines
for( int i = 1; i < histSize; i++ )
{
cv::line( histImage, cv::Point( bin_w*(i-1), hist_h - cvRound(histAng.at<float>(i-1)) ) ,
cv::Point( bin_w*(i), hist_h - cvRound(histAng.at<float>(i)) ),
cv::Scalar( 255, 0, 0), 2, 8, 0 );
}
With this you can do the same for the magnitude, or maybe turn it into a function which draws histograms if they are supplied.
In the documentation they have another option, to draw rectangles as the bins, adapting it to our case, we get something like:
// Draw the histograms for B, G and R
int hist_w = 512; int hist_h = 400;
int bin_w = std::round( static_cast<double>(hist_w)/static_cast<double>(histSize) );
cv::Mat histImage( hist_h, hist_w, CV_8UC3, Scalar( 0,0,0) );
/// Normalize the result to [ 0, histImage.rows ]
cv::normalize(histAng, histAng, 0, histImage.rows, cv::NORM_MINMAX, -1, Mat() );
for( int i = 1; i < histSize; i++ )
{
cv::rectangle(histImage, cv::Point(bin_w*(i-1), hist_h - static_cast<int>(std::round(histAng.at<float>(i-1)))), cv::Point(bin_w*(i), hist_h),);
}
Again, this can be done for the magnitude as well in the same way. This are super simple plots, if you need more complex or beautiful plots, you may need to call an external library and pass the data inside the calculated histograms. Also, this code has not been tested, so it may have a typo or error, but if something fails, just write a comment and we can find a solution.
I hope this helps you, and sorry for the late answer.

Trouble with using cvSnakeImage in iOS

- (UIImage*) snake:(UIImage *)processingImage :(UIImageView *)contourImage{
CvMat cvMatImage = [processingImage CVGrayscaleMat];
cv::Mat cvMatImage2 = &cvMatImage;
IplImage copy = cvMatImage2;
IplImage* snakeImage = ©
cvThreshold(snakeImage, snakeImage, 170, 255, CV_THRESH_BINARY);
float alpha = 0.1;
float beta = 0.5;
float gamma = 0.4;
CvSize size;
size.width = 5;
size.height = 5;
CvTermCriteria criteria;
criteria.type = CV_TERMCRIT_ITER;
criteria.max_iter = 10000;
criteria.epsilon = 0.1;
CvPoint temp;
std::vector<cv::Point> snakeCurve;
cvSnakeImage(snakeImage, temp, 40, 0.1, 0.5, 0.4, CV_VALUE, size, criteria);
}
I have already put #include as well. When I type the cvsnakeimage, the assistance pop up shown the function. However, when I finished, it shows no function match to call for...balabala....
Thanks in advance!

OpenCV CvConvexityDefects can't work and can't detect fingers!

I'm using OpenCv to detect fingertips, but now I meet with some obstacles!
The findContours & convexHull & convexityDefects method doesn't work with my project.
It's my first time to ask a question. Thanks everybody!
My code is below:
int main()
{
CvScalar white = cvRealScalar(255);
CvScalar black = cvRealScalar(0);
CvSeq* contours = 0;
CvMemStorage* storage = cvCreateMemStorage(0);
IplImage* img = cvLoadImage("h.jpg");
IplImage* imgGray = cvCreateImage(cvGetSize(img), img->depth, 1);
IplImage* contoursImg = cvCreateImage(cvGetSize(img), img->depth, 1);
cvZero(contoursImg);
int nWidth = contoursImg->width;
int nHeight = contoursImg->height;
int nChannels = contoursImg->nChannels;
int nStep = contoursImg->widthStep;
for (int i=0; i<nHeight; i++)
for(int j=0; j<nWidth; j++)
contoursImg->imageData[i*nStep + j*nChannels] = 255 - contoursImg->imageData[i*nStep + j*nChannels];
cvCvtColor(img, imgGray, CV_BGR2GRAY);
cvThreshold(imgGray, imgGray, 230, 255, CV_THRESH_BINARY);
cvSmooth(imgGray, imgGray);
cvFindContours(imgGray, storage, &contours, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0));
contours = cvApproxPoly(contours, sizeof(CvContour), storage, CV_POLY_APPROX_DP, 35, 1);
cvDrawContours(contoursImg, contours, CV_RGB(0, 0, 0), CV_RGB(0, 0, 0), 1, 1, 8, cvPoint(0, 0));
CvSeq* hull = cvConvexHull2(contours, 0, CV_CLOCKWISE, 0);
CvSeq* defect = cvConvexityDefects(contours, hull, NULL);
for(; defect; defect = defect->h_next)
{
int nomdef = defect->total;
if(!nomdef)
continue;
CvConvexityDefect* defectArray = (CvConvexityDefect*)malloc(sizeof(CvConvexityDefect)*nomdef);
cvCvtSeqToArray(defect, defectArray, CV_WHOLE_SEQ);
for(int i = 0; i < nomdef; i++)
{
cvCircle(contoursImg, *(defectArray[i].end), 5, CV_RGB(255, 0, 0), -1, 8, 0);
cvCircle(contoursImg, *(defectArray[i].start), 5, CV_RGB(0, 255, 0), -1, 8, 0);
cvCircle(contoursImg, *(defectArray[i].depth_point), 5, CV_RGB(0, 0, 255), -1, 8, 0);
}
free(defectArray);
}
cvNamedWindow("image", 1);
cvShowImage("image", contoursImg);
cvWaitKey(0);
cvReleaseMemStorage(&storage);
cvReleaseImage(&img);
cvReleaseImage(&imgGray);
cvReleaseImage(&contoursImg);
cvDestroyWindow("image");
return 0;
}
This is my hand picture:
http://www.billwang.net/bbs/oldattach/2006/12/29/billwang_5315954-038-embed.jpg
The methods are working fine. You have one rectangular contour detected on the outside of the image. As this covers the entire image, there are no defects and so, none detected. See attached image.

create a customized sequence of CvPoint in OpenCv

I want to use cvDrawContours to draw my own contours created from CvSeq (normally, contours are retured from other functions of OpenCV). This is my solution but it does not work :(
IplImage* g_gray = NULL;
CvMemStorage *memStorage = cvCreateMemStorage(0);
CvSeq* seq = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvPoint)*4, memStorage);
CvPoint points[4];
points[0].x = 10;
points[0].y = 10;
points[1].x = 1;
points[1].y = 1;
points[2].x = 20;
points[2].y = 50;
points[3].x = 10;
points[3].y = 10;
cvSeqPush(seq, &points);
g_gray = cvCreateImage( cvSize(300,300), 8, 1 );
cvNamedWindow( "MyContour", CV_WINDOW_AUTOSIZE );
cvDrawContours(
g_gray,
seq,
cvScalarAll(100),
cvScalarAll(255),
0,
3);
cvShowImage( "MyContour", g_gray );
cvWaitKey(0);
cvReleaseImage( &g_gray );
cvDestroyWindow("MyContour");
return 0;
I picked the method to create a customized contour sequence from CvPoint from this post
OpenCV sequences -- how to create a sequence of point pairs?
FOr the second try, I did it with Cpp OpenCV:
vector<vector<Point2i>> contours;
Point2i P;
P.x = 0;
P.y = 0;
contours.push_back(P);
P.x = 50;
P.y = 10;
contours.push_back(P);
P.x = 20;
P.y = 100;
contours.push_back(P);
Mat img = imread(file, 1);
drawContours(img, contours, -1, CV_RGB(0,0,255), 5, 8);
Perhaps I used the data incorrectly. The compiler alerts errors & does not allow push_back points to vectors like that. Why??
The error is like this:
Error 2 error C2664: 'std::vector<_Ty>::push_back' : cannot convert parameter 1 from 'cv::Point2i' to 'const std::vector<_Ty> &'
I've finally finished it.
Mat g_gray_cpp = imread(file, 0);
vector<vector<Point2i>> contours;
vector<Point2i> pvect;
Point2i P(0,0);
pvect.push_back(P);
P.x = 50;
P.y = 10;
pvect.push_back(P);
P.x = 20;
P.y = 100;
pvect.push_back(P);
contours.push_back(pvect);
Mat img = imread(file, 1);
drawContours(img, contours, -1, CV_RGB(0,0,255), 5, 8);
namedWindow( "Contours", 0 );
imshow( "Contours", img );
because 'contours' is vector>, contours.push_back(var) -> var should be a vector
Thanks! I've learnt a bug
In your first example you created a sequence of point foursomes with single element. Sequence elem_size should be sizeof(CvPoint) (not multiplied by four) and add points one by one:
CvMemStorage *memStorage = cvCreateMemStorage(0);
// without these flags the drawContours() method does not consider the sequence
// as contour and just draws nothing
CvSeq* seq = cvCreateSeq(CV_32SC2 | CV_SEQ_KIND_CURVE,
sizeof(CvSeq), sizeof(CvPoint), memStorage);
cvSeqPush(cvPoint(10, 10));
cvSeqPush(cvPoint(1, 1));
cvSeqPush(cvPoint(20, 50));
Note you don't need to insert the last point to draw the contour, contour is automatically closed.

Resources