I want to compute the number of black pixel in arbitrary shapes in a picture. There might be several objects, like in the picture at the bottom.
I suspect that the problem is solveable with dynamic programming, i.e. traverse the pixels row-wise and add the black pixels. I just don't know how to correctly unite the size of two parts.
I'm pretty sure there are algorithms that solve my problem, but i obviously use the wrong search terms.
Can you please provide me with a good (fast) algorithm to do so, Bonus points if the algorithm is written in c++ and compatible to Mat from the OpenCV library. ;)
Result for this (zoomed) picture would be something like: 15 for Object at top left, 60 for big blob,...
I think i found a solution (better ones are obviously welcome!):
Integrated the size computation into a Connected Component Algorithm.
In the Connected Component algorithm, we generate a new Image in which there are labels (numbers) instead of the black pixels. All pixel of one area have the same label.
New to CC-Algo is a table in which the total amount of pixel for each label is stored. That way i know for every connected component the correct size.
Process the image from left to right, top to bottom:
1.) If the next pixel to process is white:
do nothing
2.) If the next pixel to process is black:
i.) If only one of its neighbors (top or left) is black, copy its label and +1 in the size table for that label.
ii.) If both are black and have the same label, copy it and +1 in the size table for that label.
iii.) If they have different labels Copy the label from the left. Update the equivalence table and +1 in the size table for the left label.
iv.) Otherwise, assign a new label and update the size table with that label and value 1.
• Re-label with the smallest of equivalent labels and update size table accordingly
The problem can be solved using flood fill in following way : -
Keep 2-D boolean array to track if pixel is already visited initially set to false
scan the image pixel by pixel.
if pixel is unvisited and black then apply flood fill on it,
During floodfill count the number of call also mark visited pixel made as they are the no of pixels connected.
Terminate floodfill when white pixels are encountered.
Count is the size of the region containing the pixel.
Flood Fill
If I well understoud, in an image like your sample you want your alogirthm to return 6 values on for each black shapes. And, each value the number of black pixels.
The algorithm I would use for this is the following :
Invert Pixels colors of your image (so now, you are looking for white pixels)
Find contours in your image. Don't forget to find only EXTERNAL Countours.
For each contours found :
Draw each contour in a small cv::Mat with a pixel value of 1. then compute the moment of order 0 of this image. The moment of order 0 will be the number of pixel in the shape.
Related
I've an image like this one ![enter image description here][1]. The non-black part is expanded at each iteration. So, after a certain point, I need to enlarge the final image so the non-black one can fit in. For now, what I'm doing is to find the contour of the non-black image,find the bounding box of the contours and check the width/height of the box. At a first time it works, but after some iterations my program finds a bounding box of size 1 (it seems that it doesn't find any contour). What the problem could be?
Ps: the program is a mosaic from a video file, I followed the opencv tutorial for find homography and other stuff.
EDIT
Sorry but I had to remove images
Just a suggestion:
It's easier to simply iterate through each element in the matrix and record the coordinates of the uppermost, bottommost, leftmost and rightmost non-zero elements. These will be the four corners of your up-right bounding rectangle. Of course it is not necessarily the rectangle of the minimum area enclosing the non-zero pixels (not a rotated rectangle), but further can be used as a ROI.
I want to obtain all the pixels in an image with pixel values closest to certain pixels in an image. For example, I have an image which has a view of ocean (deep blue), clear sky (light blue), beach, and houses. I want to find all the pixels that are closest to deep blue in order to classify it as water. My problem is sky also gets classified as water. Someone suggested to use K nearest neighbor algorithm, but there are few examples online that use old C style. Can anyone provide me example on K-NN using OpenCv C++?
"Classify it as water" and "obtain all the pixels in an image with pixel values closest to certain pixels in an image" are not the same task. Color properties is not enough for classification you described. You will always have a number of same colored points on water and sky. So you have to use more detailed analysis. For instance if you know your object is self-connected you can use something like water-shred to fill this region and ignore distant and not connected regions in sky of the same color as water (suppose you will successfully detect by edge-detector horizon-line which split water and sky).
Also you can use more information about object you want to select like structure: calculate its entropy etc. Then you can use also K-nearest neighbor algorithm in multi-dimensional space where 1st 3 dimensions is color, 4th - entropy etc. But you can also simply check every image pixel if it is in epsilon-neighborhood of selected pixels area (I mean color-entropy 4D-space, 3 dimension from color + 1 dimension from entropy) using simple Euclidean metric -- it is pretty fast and could be accelerated by GPU .
I am trying to develop box sorting application in qt and using opencv. I want to measure width and length of box.
As shown in image above i want to detect only outermost lines (ie. box edges), which will give me width and length of box, regardless of whatever printed inside the box.
What i tried:
First i tried using Findcontours() and selected contour with max area, but the contour of outer edge is not enclosed(broken somewhere in canny output) many times and hence not get detected as a contour.
Hough line transform gives me too many lines, i dont know how to get only four lines am interested in out of that.
I tried my algorithm as,
Convert image to gray scale.
Take one column of image, compare every pixel with next successive pixel of that column, if difference in there value is greater than some threshold(say 100) that pixel belongs to edge, so store it in array. Do this for all columns and it will give upper line of box parallel to x axis.
Follow the same procedure, but from last column and last row (ie. from bottom to top), it will give lower line parallel to x axis.
Likewise find lines parallel to y axis as well. Now i have four arrays of points, one for each side.
Now this gives me good results if box is placed in such a way that its sides are exactly parallel to X and Y axis. If box is placed even slightly oriented in some direction, it gives me diagonal lines which is obvious as shown in below image.
As shown in image below i removed first 10 and last 10 points from all four arrays of points (which are responsible for drawing diagonal lines) and drew the lines, which is not going to work when box is tilted more and also measurements will go wrong.
Now my question is,
Is there any simpler way in opencv to get only outer edges(rectangle) of box and get there dimensions, ignoring anything printed on the box and oriented in whatever direction?
I am not necessarily asking to correct/improve my algorithm, but any suggestions on that also welcome. Sorry for such a big post.
I would suggest the following steps:
1: Make a mask image by using cv::inRange() (documentation) to select the background color. Then use cv::not() to invert this mask. This will give you only the box.
2: If you're not concerned about shadow, depth effects making your measurment inaccurate you can proceed right away with trying to use cv::findContours() again. You select the biggest contour and store it's cv::rotatedRect.
3: This cv::rotatedRect will give you a rotatedRect.size that defines the width en the height of your box in pixels
Since the box is placed in a contrasting background, you should be able to use Otsu thresholding.
threshold the image (use Otsu method)
filter out any stray pixels that are outside the box region (let's hope you don't get many such pixels and can easily remove them with a median or a morphological filter)
find contours
combine all contour points and get their convex hull (idea here is to find the convex region that bounds all these contours in the box region regardless of their connectivity)
apply a polygon approximation (approxPolyDP) to this convex hull and check if you get a quadrangle
if there are no perspective distortions, you should get a rectangle, otherwise you will have to correct it
if you get a rectangle, you have its dimensions. You can also find the minimum area rectangle (minAreaRect) of the convexhull, which should directly give you a RotatedRect
I am working on the problem of segmentation in videos. The user segments the first frame correctly (using grabcut here) and I generate a mask from here. The black pixels all correspond to the background and white pixels correspond to foreground. In all subsequent frame I want to shift these white pixels according to some rule. That is I want to shift all the white pixels by some amount. Is there anyway ( a function probably?) that can help me do this shifting?
As in, a brute-force way will be to visit every pixel and if it white, make the pixel to the (right/left) of it to move by whatever amount I want to shift the mask. I was wondering if there is a smarter way to do this?
So you binarized the image by a threshold, resulting in back- and foreground pixels (like canny)?
You could apply a contour on the foreground pixels. Each contour is stored as a vector of points, therefore you can apply/move a contour on the next frame.
For finding contours in an Image use findContours.
I took the difference of two consecutive frames of a video. What I got (as you know) a black frame except the moving objects. The moving objects are white. I want to count the number of white pixels in the frame. I mean, I want to go through the image row by row and if the value of the ith pixel is greater than a specified number (say 50) then they must be stored in an array. Later on I will use this array to check if there is actually an object or just a noise. For example, if a car is moving in the video then after frame differencing I will check each pixel of the frames, containing the car, row by row, to detect that car. As a result when there is a moving car in the video the pixels' values are greater than 0 after frame differencing. Any idea how can I sum all the pixels of the moving car that will enable me to decide if it is a car or just a noise.
Thanks in advance :)
You'll probably find that the difference is non-trivial. For instance, you will probably find that the biggest difference is near the edges of the car, perpendicular to the movement of the car. One of those two edges will have negative values, one positive. Therefore, the biggest advantage of the "difference image" is that you restrict your search area. In isolation it's not very useful.
So, what should you do? Well, use an edge detection algorithm on the normal image, and compare the edge found there with the 2 edges found in the difference image. The edges belonging to the car will connect the 2 edges from the difference image.
You could use blob detection: http://www.labbookpages.co.uk/software/imgProc/blobDetection.html
to detect a blob of white pixels in each "difference image". Once you have the blobs you can find their center by finding the average of their pixel positions. Then you can find the path swept out by these centers and check it against some criterion.
Without knowing more about your images I cannot suggest a criterion, but for example if you are watching them move down a straight road you might expect all the points to be roughly co-linear. In this case, you can get the gradient and a point where a blob is found and use the point-gradient form of a line to get the lines equation:
y - y_1 = m(x - x_1)
For example given a point (4, 2) and gradient 3 you would get
y - 2 = 3(x - 4)
y = 3x - 2
You can then check all points against this line to see if they lie along it.