I want to clear a floor plan and detect walls. I found this solution but it is quite difficult understand the code.
Specially this line (how does it remove texts and others objects inside rooms?)
DeleteSmallComponents[Binarize[img, {0, .2}]];
https://mathematica.stackexchange.com/questions/19546/image-processing-floor-plan-detecting-rooms-borders-area-and-room-names-t
img = Import["http://i.stack.imgur.com/qDhl7.jpg"]
nsc = DeleteSmallComponents[Binarize[img, {0, .2}]];
m = MorphologicalTransform[nsc, {"Min", "Max"}]
How can I do the same with OpenCV?
In opencv there is slightly different approach to process images. In order to do some calculation you have to think in more low-level way. By low-level I mean thinking in basic image processing operations.
For example, line you showed:
DeleteSmallComponents[Binarize[img, {0, .2}]];
Could be expressed in opencv by algorithm:
binarize image
morphological open/close or simple dilation/erosion (based on what is color of objects and background):
cv::threshold(img, img, 100, 255, CV_THRESH_BINARY);
cv::dilate(img, img, cv::Mat());
cv::dilate(img, img, cv::Mat());
Further you can implement your own distance transformation, or use for example hit-and-miss routine (which as being basic is implemented in opencv) to detect corners:
cv::Mat kernel = (cv::Mat_<int>(7, 7) <<
0, 1, 0,0,0,0,0,
-1, 1, 0,0,0,0,0,
-1, 1, 0,0,0,0,0,
-1,1,0,0,0,0,0,
-1,1,0,0,0,0,0,
-1,1,1,1,1,1,1,
-1,-1,-1,-1,-1,-1,0);
cv::Mat left_down,left_up,right_down,right_up;
cv::morphologyEx(img, left_down, cv::MORPH_HITMISS, kernel);
cv::flip(kernel, kernel, 1);
cv::morphologyEx(img, right_down, cv::MORPH_HITMISS, kernel);
cv::flip(kernel, kernel, 0);
cv::morphologyEx(img, right_up, cv::MORPH_HITMISS, kernel);
cv::flip(kernel, kernel, 1);
cv::morphologyEx(img, left_up, cv::MORPH_HITMISS, kernel);
and then you will have picture like this:
One more picture with bigger dots (after single dilation):
Finally you can process coordinates of corners found to determine rooms.
EDIT: for images with "double wall lines" like:
We have to "merge" double wall lines first, so code will be visible like this:
cv::threshold(img, img, 220, 255, CV_THRESH_BINARY);
cv::dilate(img, img, cv::Mat()); //small object textures
cv::erode(img, img, cv::getStructuringElement(CV_SHAPE_RECT, cv::Size(5, 5)),cv::Point(-1,-1),2);
cv::dilate(img, img, cv::getStructuringElement(CV_SHAPE_RECT, cv::Size(5, 5)), cv::Point(-1, -1), 3);
And result image:
Sadly, if image properties change you will have to slightly change algorithm parameters. There is posibility to provide general solution, but you have to determine most of possible variants of problem and it will be more complex.
Related
I want to extract the darker contours from images with opencv. I have tried using a simple threshold such as below (c++)
cv::threshold(gray, output, threshold, 255, THRESH_BINARY_INV);
I can iterate threshold lets say from 50 ~ 200
then I can get the darker contours in the middle
for images with a clear distinction such as this
here is the result of the threshold
but if the contours near the border, the threshold will fail because the pixel almost the same.
for example like this image.
What i want to ask is there any technique in opencv that can extract darker contour in the middle of image even though the contour reach the border and having almost the same pixel as the border?
(updated)
after threshold darker contour in the middle overlapped with border top.
It makes me fail to extract character such as the first two "SS".
I think you can simply add a edge preserving smoothing step to solve this :
// read input image
Mat inputImg = imread("test2.tif", IMREAD_GRAYSCALE);
Mat filteredImg;
bilateralFilter(inputImg, filteredImg, 5, 60, 20);
// compute laplacian
Mat laplaceImg;
Laplacian(filteredImg, laplaceImg, CV_16S, 1);
// threshold
Mat resImg;
threshold(laplaceImg, resImg, 10, 1, THRESH_BINARY);
// write result
imwrite("res2.tif", resImg);
This will give you the following result : result
Regards,
I think using laplacian could partialy solve your problem :
// read input image
Mat inputImg = imread("test2.tif", IMREAD_GRAYSCALE);
// compute laplacian
Mat laplaceImg;
Laplacian(inputImg, laplaceImg, CV_16S, 1);
Mat resImg;
threshold(laplaceImg, resImg, 30, 1, THRESH_BINARY);
// write result
imwrite("res2.tif", resImg);
Using this code you should obtain something like :
this result
You can then play with final threshold value and with laplacian kernel size.
You will probably have to remove small artifacts after this operation.
Regards
I have the following image.
this image
I would like to remove the orange boxes/rectangle around numbers and keep the original image clean without any orange grid/rectangle.
Below is my current code but it does not remove it.
Mat mask = new Mat();
Mat src = new Mat();
src = Imgcodecs.imread("enveloppe.jpg",Imgcodecs.CV_LOAD_IMAGE_COLOR);
Imgproc.cvtColor(src, hsvMat, Imgproc.COLOR_BGR2HSV);
Scalar lowerThreshold = new Scalar(0, 50, 50);
Scalar upperThreshold = new Scalar(25, 255, 255);
Mat mask = new Mat();
Core.inRange(hsvMat, lowerThreshold, upperThreshold, mask);
//src.setTo(new scalar(255,255,255),mask);
what to do next ?
How can i remove the orange boxes/rectangle from the original images ?
Update:
For information , the mask contains exactly all the boxes/rectangle that i want to remove. I don't know how to use this mask to remove boxes/rectangle from the source (src) image as if they were not present.
This is what I did to solve the problem. I solved the problem in C++ and I used OpenCV.
Part 1: Find box candidates
Firstly I wanted to isolate the signal that was specific for red channel. I splitted the image into three channels. I then subtracted the red channel from blue channel and the red from green channel. After that I subtracted both previous subtraction results from one another. The final subtraction result is shown on the image below.
using namespace cv;
using namespace std;
Mat src_rgb = imread("image.jpg");
std::vector<Mat> channels;
split(src_rgb, channels);
Mat diff_rb, diff_rg;
subtract(channels[2], channels[0], diff_rb);
subtract(channels[2], channels[1], diff_rg);
Mat diff;
subtract(diff_rb, diff_rg, diff);
My next goal was to divide the parts of obtained image into separate "groups". To do that, I smoothed the image a little bit with a Gaussian filter. Then I applied a threshold to obtain a binary image; finally I looked for external contours within that image.
GaussianBlur(diff, diff, cv::Size(11, 11), 2.0, 2.0);
threshold(diff, diff, 5, 255, THRESH_BINARY);
vector<vector<Point>> contours;
findContours(diff, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
Click to see subtraction result, Gaussian blurred image, thresholded image and detected contours.
Part 2: Inspect box candidates
After that, I had to make an estimate whether the interior of each contour contained a number or something else. I made an assumption that numbers will always be printed with black ink and that they will have sharp edges. Therefore I took a blue channel image and I applied just a little bit of Gaussian smoothing and convolved it with a Laplacian operator.
Mat blurred_ch2;
GaussianBlur(channels[2], blurred_ch2, cv::Size(7, 7), 1, 1);
Mat laplace_result;
Laplacian(blurred_ch2, laplace_result, -1, 1);
I then took the resulting image and applied the following procedure for every contour separately. I computed a standard deviation of the pixel values within the contour interior. Standard deviation was high inside the contours that surrounded numbers; and it was low inside the two contours that surrounded the dog's head and the letters on top of the stamp.
That is why I could appliy the standard deviation threshold. Standard deviation was approx. twice larger for contours containing numbers so this was an easy way to only select the contours that contained numbers. Then I drew the contour interior mask. I used erosion and subtraction to obtain the "box edge mask".
The final step was fairly easy. I computed an estimate of average pixel value nearby the box on every channel of the image. Then I changed all pixel values under the "box edge mask" to those values on every channel. After I repeated that procedure for every box contour, I merged all three channels into one.
Mat mask(src_rgb.size(), CV_8UC1);
for (int i = 0; i < contours.size(); ++i)
{
mask.setTo(0);
drawContours(mask, contours, i, cv::Scalar(200), -1);
Scalar mean, stdev;
meanStdDev(laplace_result, mean, stdev, mask);
if (stdev.val[0] < 10.0) continue;
Mat eroded;
erode(mask, eroded, cv::Mat(), cv::Point(-1, -1), 6);
subtract(mask, eroded, mask);
for (int c = 0; c < src_rgb.channels(); ++c)
{
erode(mask, eroded, cv::Mat());
subtract(mask, eroded, eroded);
Scalar mean, stdev;
meanStdDev(channels[c], mean, stdev, eroded);
channels[c].setTo(mean, mask);
}
}
Mat final_result;
merge(channels, final_result);
imshow("Final Result", final_result);
Click to see red channel of the image, the result of convolution with Laplacian operator, drawn mask of the box edges and the final result.
Please note
This code is far from being optimal, especially the last loop does quite a lot of unnecessary work. But I think that in this case readability is more important (and the author of the question did not request an optimized solution anyway).
Looking towards more general solution
After I posted the initial reply, the author of the question noted that the digits can be of any color and their edges are not necessarily sharp. That means that above procedure can fail because of various reasons. I altered the input image so that it contains different kinds of numbers (click to see the image) and you can run my algorithm on this input and analyze what goes wrong.
The way I see it, one of these approaches is needed (or perhaps a mixture of both) to obtain a more "general" solution:
concentrate only on rectangle shape and color (confirm that the box candidate is really an orange box and remove it regardless of what is inside)
concentrate on numbers only (run a proper number detection algorithm inside the interior of every box candidate; if it contains a single number, remove the box)
I will give a trivial example of the first approach. If you can assume that orange box size will always be the same, just check the box size instead of standard deviation of the signal in the last loop of the algorithm:
Rect rect = boundingRect(contours[i]);
float area = rect.area();
if (area < 1000 || area > 1200) continue;
Warning: actual area of rectangles is around 600Px^2, but I took into account the Gaussian Blurring, which caused the contour to expand. Please also note that if you use this approach you no longer need to perform blurring or laplace operations on blue channel image.
You can also add other simple constraints to that condition; ratio between width and height is the first one that comes to my mind. Geometric properties can also be a good option (right angles, straight edges, convexness ...).
I'm working on a project to design a low vision aid. What is the image processing operation which can simulate cataract vision to the normal eye using OpenCV?
It would be useful if you described the symptoms of the cataract and what happens to the retinal images since not all the people here are experts in computer vision and eye deceases at the same time. If a retinal image gets out of focus and gets a yellow tint you can used openCV blur() function and also boost RGB values with yellow a bit. If there are different degree of blur across a visual field I recommend using integral images, see this post
I guess there are at least three operations to do: add noise, blur, whiten:
Rect rect2(0, 0, w/2, h);
Mat src = I4(rect2).clone();
Mat Mnoise(h, w/2, CV_8UC3);
randn(Mnoise, 100, 50);
src = src*0.5+Mnoise*0.5; // add noise
Mat Mblur;
blur(src, Mblur, Size(12, 12)); // blur
Rect rect3(w, 0, w/2, h);
Mat Mblurw = Mblur*0.8+ Scalar(255, 255, 255)*0.2; //whiten
This question has been annoying me over 2 weeks.
My goal is to analyze a set of products stored in cartons on a shelf.
Right now, I have tried using the following methods from OpenCV Python module: findContours, canny,HoughLines,cv2.HoughLinesP, but I can't find the result grid.
My goal is to check if the products been filled up in carton.
Here is the original image: http://postimg.org/image/hyz1jpd7p/7a4dd87c/
My first step is to use closing transformation:
closing = cv2.morphologyEx(opening, cv2.MORPH_CLOSE, kernel, iterations=1)]
This gives me the contours (I have not enough reputation to post this url, this image is similar with the last image below, but without red lines!).
Finally, the question is, how could I find the carton grid (i.e., the products in it one by one).
I have added the red lines in the image below.
Please give me the hints, thank you very much!
Red lines: http://postimg.org/image/6i0di4gsx/
I've played a little bit with the input and found a way to extract basically the grid with HoughLinesP after thresholding the Hue channel.
edit: I'm using C++, but similar python methods should be available I guess.
cv::Mat image = cv::imread("box1.png");
cv::Mat output; image.copyTo(output);
cv::Mat hsv;
cv::cvtColor(image, hsv, CV_BGR2HSV);
std::vector<cv::Mat> hsv_channels;
cv::split(hsv, hsv_channels);
// thresholding here is a little sloppy, maybe you have to use some smarter way
cv::Mat h_thres = hsv_channels[0] < 50;
// unfortunately, HoughLinesP couldnt detect all the lines if they were too wide
// to make this part more robust I would suggest a ridge detection on the distance transformed image instead of 'some erodes after a dilate'
cv::dilate(h_thres, h_thres, cv::Mat());
cv::erode(h_thres, h_thres, cv::Mat());
cv::erode(h_thres, h_thres, cv::Mat());
cv::erode(h_thres, h_thres, cv::Mat());
std::vector<cv::Vec4i> lines;
cv::HoughLinesP( h_thres, lines, 1, CV_PI/(4*180.0), 50, image.cols/4, 10 );
for( size_t i = 0; i < lines.size(); i++ )
{
cv::line( output, cv::Point(lines[i][0], lines[i][1]),
cv::Point(lines[i][2], lines[i][3]), cv::Scalar(155,255,155), 1, 8 );
}
here are the images:
hue channel after hsv convert:
threshholded hue channel:
output:
maybe someone else has an idea how to improve the HoughLinesP without those erode steps...
Hope this method helps you a bit and you can improve it further to use it for your needs.
I have problems getting the contours of an object in my picture(s).
In order to delete all noise, I use adjustROI() and Canny().
I also tried erode() and dillate(), Laplacian(), GaussianBlur(), Sobel()... and I even found this code snippet to sharpen a picture:
GaussianBlur(src, dst_gaussian, Size(0, 0), 3);
addWeighted(src, 1.5, dst_gaussian, -0.5, 0, dst);
But my result is always the same: My object is filled with black and white colour (like noise on a TV-screen) so that it is impossible to get the contours with findContours() (findContours() finds a million of contours, but not the one of the whole object. I check this with drawContours()).
I use C++ and I load my picture as a grayscale Mat (for Canny it has to be grayscale). My object has a diffenent shape on every picture, but it is always around the middle of the picture.
I either need to find a way to get a better coloured object by image processing - but I don't know what else to try - or a way how to fill the object with colour after image processing (without having it's contours, because this is what I want in the end).
Any ideas are welcome. Thank you in advance.
I found a solution that works in most cases. I fill my object using the probabilistic Hough transform HoughLinesP().
vector<Vec4i> lines;
HoughLinesP(dst, lines, 1, CV_PI/180, 80, 30, 10);
for(size_t i = 0; i < lines.size(); i++)
{
line(color_dst, Point(lines[i][0], lines[i][1]), Point(lines[i][2], lines[i][3]), Scalar(0,0,255), 3, 8);
}
This is from some sample code OpenCV documentation provides.
After using some edge-detection algorithm (like Canny()), the probabilistic Hough transform finds objects in binary pictures. The algorithm finds lines, which, if drawn, represent the whole object. Of course some of the parameters have to be adapted for some kind of picture.
I'm not sure if this will work on every picture or every object, but in my case, it does.