Thin line removal in binary images using OpenCV - opencv

I'm currently using OpenCV for detecting blobs in a binary image. I'd like to erase small lines without changing the big objects.
Here's an example: The original image is
And I want to convert it into the following
"Opening" didn't work, because when applying it the edges of the triangle were cut off. Is there any other method for removing the lines, without losing information of the big triangle?

Use Erosion to remove such a noise,
The code look like,
Mat src;//load source
Mat dst;//destination image
Mat element = getStructuringElement( MORPH_RECT,Size(5,5), Point( -1, -1 ) ); // kernel performing drode
erode( src, dst, element );
Edit
Adding #Bull comments here as it more appropriate method, which suggest erosion followed by dilation will get you very close to what you want.

Related

Can't determine document edges from camera with OpenCV

I need find edges of document that in user hands.
1) Original image from camera:
2) Then i convert image to BG:
3) Then i make blur:
3) Finds edges in an image using the Canny:
4) And use dilate :
As you can see on the last image the contour around the map is torn and the contour is not determined. What is my error and how to solve the problem in order to determine the outline of the document completely?
This is code how i to do it:
final Mat mat = new Mat();
sourceMat.copyTo(mat);
//convert the image to black and white
Imgproc.cvtColor(mat, mat, Imgproc.COLOR_BGR2GRAY);
//blur to enhance edge detection
Imgproc.GaussianBlur(mat, mat, new Size(5, 5), 0);
if (isClicked) saveImageFromMat(mat, "blur", "blur");
//convert the image to black and white does (8 bit)
int thresh = 128;
Imgproc.Canny(mat, mat, thresh, thresh * 2);
//dilate helps to connect nearby line segments
Imgproc.dilate(mat, mat,
Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(3, 3)),
new Point(-1, -1),
2,
1,
new Scalar(1));
This answer is based on my above comment. If someone is holding the document, you cannot see the edge that is behind the user's hand. So, any method for detecting the outline of the document must be robust to some missing parts of the edge.
I suggest using a variant of the Hough transform to detect the document. The Wikipedia article about the Hough transform makes it sound quite scary (as Wikipedia often does with mathematical subjects), but don't be discouraged, actually they are not too difficult to understand or implement.
The original Hough transform detected straight lines in images. As explained in this OpenCV tutorial, any straight line in an image can be defined by 2 parameters: an angle θ and a distance r of the line from the origin. So you quantize these 2 parameters, and create a 2D array with one cell for every possible line that could be present in your image. (The finer the quantization you use, the larger the array you will need, but the more accurate the position of the found lines will be.) Initialize the array to zeros. Then, for every pixel that is part of an edge detected by Canny, you determine every line (θ,r) that the pixel could be part of, and increment the corresponding bin. After processing all pixels, you will have, for each bin, a count of how many pixels were detected on the line corresponding to that bin. Counts which are high enough probably represent real lines in the image, even if parts of the line are missing. So you just scan through the bins to find bins which exceed the threshold.
OpenCV contains Hough detectors for straight lines and circles, but not for rectangles. You could either use the line detector and check for 4 lines that form the edges of your document; or you could write your own Hough detector for rectangles, perhaps using the paper Jung 2004 for inspiration. Rectangles have at least 5 degrees of freedom (2D position, scale, aspect ratio, and rotation angle), and memory requirement for a 5D array obviously goes up pretty fast. But since the range of each parameter is limited (ie, the document's aspect ratio is known, and you can assume the document will be well centered and not rotated much) it is probably feasible.

Region of interest in fingerprint image

I am working on a fingerprint recognition project with OpenCV. Currently I need to extract the inner region in fingerprint (ellipse in image), but I am not sure how to do it.
Any suggestion is appreciated.
EDIT:
I need to check if a fingerprint from sensor device and another from identification card match or not. The fingerprint in sensor is as follow (left) meanwhile in identification card is as right fingerprint. In order to validate them, it is required to segment this fingerprint (outside the ellipse doesn't provide useful information but indeed adds "noise" for this purpose).
Thank you.
#API55's comment is correct, for clarity:
create a mask (white inside the ellipse and black outside) you can do this with ellipse function and -1 in the thickness. Then copy the image using the mask (bitwise_and for python or copyTo for c++ should do it)... you will always have a squared image, but you will have black (or the color you want) outside the ellipse
These steps are pretty much spot on,
Create your circular mask in the correct place in the image
Copy the image using that mask
Your new image contains the mask data, and black data everywhere else.
below is an example of how to implement this in code:
( I lovingly borrowed from here)
Mat img = imread("small1.png", 0); // load gray
Rect region(10,10,40,40); // example roi
Mat roi(img, region); // part of img
Mat mask(Size(40,40), CV_8U, Scalar(0)); // all black
circle(mask, Point(20,20), 20, Scalar(255), -1, LINE_AA); // filled circle
Mat circRoi;
bitwise_and(roi, roi, circRoi, mask); // retain only pixels inside the circle
//
// now you can do your intensity calculation on circRoi
//
imshow("circle masked", mask);
imshow("masked roi", circRoi);
waitKey();
Useful Links
Why ROIs don't have to be circular but Mats do
Older code example, useful for learning the theory but I wouldnt recommend implementing using IPLimage
Creating a custom ROI of any shape or size

OpenCv inRange function behaves differently when called from Objective C++

I have used the excellent answer to the question here:
How to detect bullet holes on the target using python
I have verified that it works in both Python 2 and 3.6, but I would like to use the concept in an iOS application written in Objective C(++). This is my attempt at translating it. Ultimately, I need it to work with an image taken by the camera, so I don't want to use imread, but I've checked that this makes no difference.
UIImage *nsi = [UIImage imageNamed:#"CANDX.jpg"];
cv::Mat original;
UIImageToMat(nsi, original);
cv::Mat thresholded;
cv::inRange(original, cv::Scalar(40,40,40), cv::Scalar(160,160,160), thresholded);
cv::Mat kernel = cv::Mat::ones(10, 10, CV_64FC1);
cv::Mat opening;
cv::morphologyEx(thresholded, opening, cv::MORPH_OPEN, kernel);
vector<vector<cv::Point>> contours;
cv::findContours(opening, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
The call to inRange, with the same values as the Python version, gives a completely black image. Indeed, it is impossible to pick values for lower- and upper-bounds that do not result in this outcome. I've tried converting the image to HSV and using HSV values for lower- and upper-bound. This makes a slight difference in that I can get some vaguely recognisable outcomes, but nothing like the useful result I should be getting.
If I substitute the 'thresholded' image from the answer and comment out the inRange call, the morphology and findContours calls work okay.
Am I doing something wrong in setting up the inRange call?
As you mention in the comments, the data type of original is CV_8UC4 -- i.e. it's a 4 channel image. However, in your call to cv::inRange, you provide ranges for only 3 channels.
cv::Scalar represents a 4-element vector. When you call the constructor with only 3 values, a default value of 0 is used for the 4-th element.
Hence, your call to inRange is actually equivalent to this:
cv::inRange(original, cv::Scalar(40,40,40,0), cv::Scalar(160,160,160,0), thresholded);
You're looking only for pixels that have the alpha channel set to 0 (fully transparent). Since the image came from a camera, it's highly unlikely there will be any transparent pixels -- the alpha channel is probably just all 255s.
There are 2 options to solve this:
Drop the unneeded alpha channel. One way to do this is to use cv::cvtColor, e.g.
cv::cvtColor(original, original, cv::COLOR_BGRA2BGR);
Specify desired range for all the channels, e.g.
cv::inRange(original, cv::Scalar(40,40,40,0), cv::Scalar(160,160,160,255), thresholded);

OpenCV - Separating letter contours in text segmentation

I’ve been working with text recognition in a dataset of images. I want to segment the characters of the image using components and finding contours of a thresholded image. However, many of the characters are merged with each other and with other components in the image.
Can you give me some idea for separating them? Thanks for the help!
Below are some examples, and part of my code:
Mat placa_contornos = processContourns(img_placa_adaptativeTreshold_mean);
vector<vector<Point>> contours_placa;
findContours(placa_contornos,
contours_placa,
CV_RETR_EXTERNAL, externos)
CV_CHAIN_APPROX_NONE);
vector<vector<Point> >::iterator itc = contours_placa.begin();
while (itc != contours_placa.end()) {
//Create bounding rect of object
Rect mr = boundingRect(Mat(*itc));
rectangle(imagem_placa_cor, mr, Scalar(0, 255, 0));
++itc;
}
imshow("placa con rectangles", imagem_placa_cor);
Results examples
original image, binarized image, result
I would try to erode the binary image more to see if that helps. You may also want to try fixing the skew and then removing the bottom line that connects the letters
Also, this might be relevant: Recognize the characters of license plate
You can try an opening operation on your thresholded image to get rid of the noise. You can adjust the kernel size based on your need.
// Get a rectangular kernel with size 7
Mat element = getStructuringElement(0, Size(7, 7), Point(1, 1));
// Apply the morphology operation
morphologyEx(placa_contornos, result, CV_MORPH_OPEN, element);
It gives the following intermediate output on your thresholded image, I guess it would improve your detection.

Converting a Matrix to a Vector in openCV

I have an image 16x16 pixel image , how can I put it in a matrix 1x256 pixel and then convert it back to a 16x16 pixel Using opencv ?
I tried reshape but it didn't succeed as when i make cout<< image.cols << image.rows give me the same number which is 16,16 also sometimes the image is not continuous so reshape won't work
Btw I need it in coding a neural network classifier.
// create matrix for the result
Mat image1x256(Size(256,1), image.type());
// use reshape function
Mat image16x16 = image1x256.reshape(image.channels(), 16);
// copy the data from your image to new image
image.copyTo(image16x16);
Since image16x16 and image1x256 are just different pointers to same data, then copying the data to one of them will actually change both.
Note that reshape function creates a new header (i.e. new smart pointer) that may be used instead of old one, but it is not changing properties of the original header that still exist and can be used.

Resources