Separate image object into N sections of equal pixels (Approach) - algorithm

Sorry in advance, this is more of an algorithmic problem rather than a coding problem, but I wasn't sure where to put it. For simplicity sake, say you have a binary image (white background, solid black object in foreground)
Example:
sample input
I want to divide this object (meaning only the black pixels) into N sections, all with the same number of pixels (so each section should contain (1/N)*(total # of black pixels)).
With the current algorithm that I'm using, I (1) find the total number of black pixels and (2) divide by N. Then I (3) scan the image row by row marking all black pixels. The result looks something like this:
current output sketch
The problem with this is the last (yellow) section, which isn't continuous. I want to divide the image in a way that makes more sense, like this:
ideal output
Basically, I'd like the boundary between the sections to be as short as possible.
I've been stumped on this for a while, but my old code just isn't cutting it anymore. I only need an approach to identifying the sections, I'll ultimately be outputting each section as individual images, as well as a grayscale copy of the input image where every pixel's value corresponds to its section number (these things I don't need help with). Any ideas?

I only need an approach to identifying the sections
According to this, I tried couple of approaches, these may help for guidelines:
Find contour of the image
Find the moments of contour and detect mass center.
For outer corners, you can simply use convex hull
Find the closest contour points(which are will be inner corners) to mass center
Then you can seperate it to desired regions by using these important points
Here is the result and code:
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;
vector<Point>innerCorners;
bool isClose(Point test);
int main()
{
Mat src_gray;
int thresh = 100;
Mat src = imread("image/dir/star.png");
cvtColor( src, src_gray, COLOR_BGR2GRAY );
namedWindow( "Source",WINDOW_NORMAL );
Mat canny_output;
Canny( src_gray, canny_output, thresh, thresh*2 );
vector<vector<Point> > contours;
findContours( canny_output, contours, RETR_TREE, CHAIN_APPROX_SIMPLE );
vector<Vec4i> hierarchy;
vector<vector<Point> >hull( contours.size() );
vector<Moments> mu(contours.size() );
for( int i = 0; i <(int)contours.size(); i++ )
{ mu[i] = moments( contours[i], false ); }
for( size_t i = 0; i < contours.size(); i++ )
{
if(contours[i].size()>20)
convexHull( contours[i], hull[i] );
}
vector<Point2f> mc( contours.size() );
for( int i = 0; i <(int)contours.size(); i++ )
{ mc[i] = Point2f( mu[i].m10/mu[i].m00 , mu[i].m01/mu[i].m00 ); }
Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
int onlyOne = 1;
for( size_t i = 0; i< contours.size(); i++ )
{
if(contours[i].size()>20 && onlyOne)
{
circle( src, mc[i], 4, Scalar(0,255,255), -1, 8, 0 );
Scalar color = Scalar(255,0,0);
drawContours( drawing, contours, (int)i, color );
drawContours( src, hull, (int)i, color,5 );
Point centerMass = mc[i];
for(int a=0; a<(int)contours[i].size();a++)
{
if(cv::norm(cv::Mat(contours[i][a]),Mat(centerMass))<200 && isClose(contours[i][a]))
{
circle(src,contours[i][a],5,Scalar(0,0,255),10);
innerCorners.push_back(contours[i][a]);
line(src,contours[i][a],centerMass,Scalar(0,255,255),5);
}
}
onlyOne = 0;
}
}
namedWindow( "Hull demo",WINDOW_NORMAL );
imshow( "Hull demo", drawing );
imshow("Source", src );
waitKey();
return 0;
}
bool isClose(Point test){
if(innerCorners.size()==0)
return 1;
for(Point a:innerCorners)
if((cv::norm(cv::Mat(a),cv::Mat(test)))<70)
return 0;
return 1;
}

Related

Using Processing for image visualization: pixel color thresholds

Image to be manipulated, hoping to identify each white dot on each picture with a counter
PImage blk;
void setup() {
size(640, 480);
blk=loadImage("img.png");
}
void draw () {
loadPixels();
blk.loadPixels();
int i = 0;
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int loc = x+y*width;
pixels [loc] = blk.pixels[loc];
if (blk.pixels[loc] == 0) {
if (blk.pixels [loc]+1 != 0) {
i++;
}
}
float r = red(blk.pixels[loc]);
float g = green(blk.pixels[loc]);
float b = blue(blk.pixels[loc]);
pixels [loc] = color(r, g, b);
}
}
System.out.println (i);
updatePixels();
}
The main problem is within my if statement, not sure to approach it logically.
I'm unsure where this is exactly going, but I can help you find the white pixels. Here, I just counted 7457 "white" pixels (then I turned them red so you can see where they are and adjust the threshold if you want to get more or less of them):
Of course, this is just a proof of concept which you should be able to adapt to your needs.
PImage blk;
void setup() {
size(640, 480);
blk=loadImage("img.png");
blk.loadPixels();
int whitePixelsCount = 0;
// I'm doing this in the 'setup()' method because I don't need to do it 60 times per second
// Once it's done once I can just use the image as modified unless you want several
// different versions (which you can calculate once anyway then store in different PImages)
for (int i = 0; i < blk.width * blk.height; i++) {
float r = red(blk.pixels[i]);
float g = green(blk.pixels[i]);
float b = blue(blk.pixels[i]);
// In RGB, the brightness of each color is represented by it's intensity
// So here I'm checking the "average intensity" of the color to see how bright it is
// And I compare it to 100 since 255 is the max and I wanted this simple, but you can
// play with this threshold as much as you like
if ((r+g+b)/3 > 100) {
whitePixelsCount++;
// Here I'm making those pixels red so you can see where they are.
// It's easier to adjust the threshold if you can see what you're doing
blk.pixels[i] = color(255, 0, 0);
}
}
println(whitePixelsCount);
updatePixels();
}
void draw () {
image(blk, 0, 0);
}
In short (you'll read this in the comments too), we count the pixels according to a threshold we can adjust. To make things more obvious for you, I colored the "white" pixels red. You can lower or raise the threshold according to what you see this way, and once you know what you want you can get rid of the color.
There is a difficulty here, which is that the image isn't "black and white", but more greyscale - which is totally normal, but makes things harder for what you seem to be trying to do. You'll probably have to tinker a lot to get to the exact ratio which interests you. It could help a lot if you edited the original image in GiMP or another image software which lets you adjust contrast and brightness. It's kinda cheating, but it it doesn't work right off the bat this strategy could save you some work.
Have fun!

Estimate Image line gradient ( not pixel gradient)

I have a problem whereby I want to estimate the gradient of the line on the contour. Please note that I dont need the pixel gradient but the rate of change of line.
If you see the attached image, you will see a binary image with green contour. I want to label each pixel based on the gradient of the pixel on the contour.
Why I need the gradient is because I want to compute the points where the gradient orientation changes from + to - or from - to +.
I cannot think of a good method, to estimate this point on the image. Could someone help me with suggestion on how I can estimate this points.
Here is a small program that computes the tangent at each contour pixel location in a very simple way (there exist other and probably better ways! the easy ones are: http://en.wikipedia.org/wiki/Finite_difference#Forward.2C_backward.2C_and_central_differences):
for a contour pixel c_{i} get the neighbors c_{i-1} and c_{i+1}
tangent direction at c_i is (c_{i-1} - c_{i+1}
So this is all on CONTOUR PIXELS but maybe you could so something similar if you compute the orthogonal to the full image pixel gradient... not sure about that ;)
here's the code:
int main()
{
cv::Mat input = cv::imread("../inputData/ContourTangentBin.png");
cv::Mat gray;
cv::cvtColor(input,gray,CV_BGR2GRAY);
// binarize
cv::Mat binary = gray > 100;
// find contours
std::vector<std::vector<cv::Point> > contours;
std::vector<cv::Vec4i> hierarchy;
findContours( binary.clone(), contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE ); // CV_CHAIN_APPROX_NONE to get each single pixel of the contour!!
for( int i = 0; i< contours.size(); i++ )
{
std::vector<cv::Point> & cCont = contours[i];
std::vector<cv::Point2f> tangents;
if(cCont.size() < 3) continue;
// 1. compute tangent for first point
cv::Point2f cPoint = cCont.front();
cv::Point2f tangent = cCont.back() - cCont.at(1); // central tangent => you could use another method if you like to
tangents.push_back(tangent);
// display first tangent
cv::Mat tmpOut = input.clone();
cv::line(tmpOut, cPoint + 10*tangent, cPoint-10*tangent, cv::Scalar(0,0,255),1);
cv::imshow("tangent",tmpOut);
cv::waitKey(0);
for(unsigned int j=1; j<cCont.size(); ++j)
{
cPoint = cCont[j];
tangent = cCont[j-1] - cCont[(j+1)%cCont.size()]; // central tangent => you could use another method if you like to
tangents.push_back(tangent);
//display current tangent:
tmpOut = input.clone();
cv::line(tmpOut, cPoint + 10*tangent, cPoint-10*tangent, cv::Scalar(0,0,255),1);
cv::imshow("tangent",tmpOut);
cv::waitKey(0);
//if(cv::waitKey(0) == 's') cv::imwrite("../outputData/ContourTangentTangent.png", tmpOut);
}
// now there are all the tangent directions in "tangents", do whatever you like with them
}
for( int i = 0; i< contours.size(); i++ )
{
drawContours( input, contours, i, cv::Scalar(0,255,0), 1, 8, hierarchy, 0 );
}
cv::imshow("input", input);
cv::imshow("binary", binary);
cv::waitKey(0);
return 0;
}
I used this image:
and got outputs like:
in the result you get a vector with a 2D tangent information (line direction) for each pixel of that contour.

Prospective algorithmic approach for this image in OpenCV

I am looking for advice from people having extensive experience with computer vision. I have a collection of ultrasonographic B&W images like the one below (without the stars and dotted line):
What I would like to do is detect the contour of a blood vessel (for example, the one highlighted by the yellow star). Of course my first step would be to define the ROI and maximize the contrast. But what would then be the best algorithm to use? Segmentation with the watershed algorithm? Something else?
I am little unsettled because of the image blur...
Edit:
As requested in the comments, here would be an example of source and result images:
Following is a simple approach to your problem, if I understood you correctly. My result is shown below.
And here is the code
int max_area_threshold = 10000;
int min_area_threshold = 1000;
float rational_threshold = 0.7;
cv::Mat img = cv::imread("sample.jpg", CV_8UC1);
cv::Mat img_binary;
//Create binary imae by tresholding
cv::threshold(img, img_binary, 25, 255, CV_THRESH_BINARY);
//Invert black-white
cv::bitwise_not(img_binary, img_binary);
//Eliminating small segments
cv::erode(img_binary, img_binary, cv::Mat(), cv::Point(-1, -1), 2, 1, 1);
cv::dilate(img_binary, img_binary, cv::Mat(), cv::Point(-1, -1), 1, 1, 1);
//Find contours
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours( img_binary, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
for( int i = 0; i< contours.size(); i++ )
{
if(contours[i].size() < 5)
continue;
//Fit ellipse to contour
cv::RotatedRect boundRect = cv::fitEllipse(contours[i]);
//Check the squareness of the bounding box
if(abs((boundRect.size.width / (float)boundRect.size.height)-1.0) > rational_threshold)
continue;
//Elliminate too big segments
if(boundRect.boundingRect().area() > max_area_threshold)
continue;
//Elliminate too small segments
if(boundRect.boundingRect().area() < min_area_threshold)
continue;
drawContours(img, contours, i, cv::Scalar(255), 0.2, 8, hierarchy, 0, cv::Point() );
}
cv::imwrite("result.jpg", img);
I hope it helps.

Detect Only Circles using OpenCV

I am using the following code to detect the circles only . But it is also detecting the other shapes . Please help to do this . I have used the HoughCircles but it is not giving the Good results. My requirement is have to detect the circles only .
Mat src, src_gray;
/// Read the image
src = t2;
if(! src.data ) // Check for invalid input
{
cout << "Could not open or find the image" << std::endl ;
cv::waitKey(5000);
}
/// Convert it to gray
cvtColor( src, src_gray, CV_BGR2GRAY );
blur( src_gray, src_gray, Size(3,3) );
/// Reduce the noise so we avoid false circle detection
GaussianBlur( src_gray, src_gray, Size(9, 9), 2, 2 );
Mat src_lines; Mat src_gray_lines;
int thresh_lines = 100;
RNG rng_lines(12345);
Mat threshold_output;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
src_gray_lines = src_gray;
/// Detect edges using Threshold
threshold( src_gray_lines, threshold_output, thresh_lines, 255, THRESH_BINARY );
/// Find contours
findContours( threshold_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
/// Find the rotated rectangles and ellipses for each contour
vector<RotatedRect> minRect( contours.size() );
vector<RotatedRect> minEllipse( contours.size() );
for( size_t i = 0; i < contours.size(); i++ )
{
minRect[i] = minAreaRect( Mat(contours[i]) );
}
/// Draw contours + rotated rects + ellipses
Mat drawing = Mat::zeros( threshold_output.size(), CV_8UC3 );
for( size_t i = 0; i< contours.size(); i++ )
{
// rotated rectangle
Point2f rect_points[4];
minRect[i].points( rect_points );
for( int j = 0; j < 4; j++ )
line( src, rect_points[j], rect_points[(j+1)%4], Scalar(255,0,0), 1, 8 );
}
Please let me know if my question is not clear .
You have contour vectors so you can easily check their length. Circle has also area.
Circle shape should have specific ratio area to length (you should compute what this ratio should be). Now eliminating shapes which does not fit this ratio (with some delta) you are getting only circles.

Problems in finding contours and convex hull in openCV

I have written the following code
#include"opencv2/opencv.hpp"
#include<iostream>
#include<math.h>
using namespace std;
using namespace cv;
main()
{
Mat img1,img2,sub,gray1,gray2,lab,ycbcr;
int v[3];
int row,col,i,j,t;
VideoCapture cap(0);
namedWindow("current");
cap>>img1;
sub=img1;
row=img1.rows;
col=img1.cols;
cvtColor(img1,gray1,CV_BGR2GRAY);
vector<vector<Point> > cont;
vector<Vec4i> hierarchy;
while (1) {
cap>>img2;
cvtColor(img2,gray2,CV_BGR2GRAY);
for(i=0;i<row;++i)
{
for (j=0; j<col; ++j)
{
if(abs(gray1.at<uchar>(i,j) - gray2.at<uchar>(i,j))>10)
{
sub.at<Vec3b>(i,j)[0] = img2.at<Vec3b>(i,j)[0];
sub.at<Vec3b>(i,j)[1] = img2.at<Vec3b>(i,j)[1];
sub.at<Vec3b>(i,j)[2] = img2.at<Vec3b>(i,j)[2];
}
else
{
sub.at<Vec3b>(i,j)[0]=0;
sub.at<Vec3b>(i,j)[1]=0;
sub.at<Vec3b>(i,j)[2]=0;
}
}
}
cvtColor(sub,ycbcr,CV_BGR2YCrCb);
inRange(ycbcr,Scalar(7,133,106),Scalar(255,178,129),ycbcr);
findContours(ycbcr,cont,hierarchy,CV_RETR_LIST,CV_CHAIN_APPROX_SIMPLE);
Scalar color = CV_RGB(255,0,0);
for(int i1 = 0 ;i1 >= 0; i1 = hierarchy[i1][0] )
drawContours( ycbcr, cont, i1, color,2, CV_AA, hierarchy );
vector<Point2f > hullPoints;
// convexHull(Mat(cont),hullPoints,false);
imshow("current",ycbcr);
a
if(waitKey(33)=='q')
break;
img1=img2.clone();
}
}
1.)Why the contours are not displaying in red color although i specified it through CV_RGB(255,0,0).
2.)When i uncomment the line
convexHull(Mat(cont),hullPoints,false);
,the program shows runtime error.Why is it happening.Can anybody tell me the exact format of convexHull()
and the meaning of its arguments
1) Try (0,0,255) for red color. OpenCV uses BGR format.
2) For finding convex hull, try the code in OpenCV tutorial.
Also try similar question as yours on Convexhull. How to calculate convex hull area using openCV functions?
Your hullPoints vector is empty. If you check the size of vector,you don't get an error.
if(hullPoints.size() > 2) {
convexHull(....);
}

Resources