I've looked at JavaCV wrapper for OpenCV library and I saw that it is possible to use that library in Java for face detection on an image, but I was wondering is it possible to use that library for detecting traffic warning signs on an image and how?
I have pictures taken from the road that look like this: http://www.4shared.com/photo/5QxoVDwd/041.html
and the result of detection should look sometning like this or similar: http://www.4shared.com/photo/z_pL0lSK/overlay-0.html
EDIT:
After I detect red color I get this image:
And I have a problem detecting just the warning sign triangle shape and ignore all other shapes. I tried changing the cvApproxPoly parameters but with no result. This is my code:
public void myFindContour(IplImage image)
{
IplImage grayImage = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, 1);
cvCvtColor(image, grayImage, CV_BGR2GRAY);
CvMemStorage mem;
CvSeq contours = new CvSeq();
CvSeq ptr = new CvSeq();
cvThreshold(grayImage, grayImage, 150, 255, CV_THRESH_BINARY);
mem = cvCreateMemStorage(0);
cvFindContours(grayImage, mem, contours, Loader.sizeof(CvContour.class) , CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0));
Random rand = new Random();
while (contours != null && !contours.isNull()) {
if (contours.elem_size() > 0) {
CvSeq points = cvApproxPoly(contours, Loader.sizeof(CvContour.class),
mem, CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0);
Color randomColor = new Color(rand.nextFloat(), rand.nextFloat(), rand.nextFloat());
CvScalar color = CV_RGB( randomColor.getRed(), randomColor.getGreen(), randomColor.getBlue());
cvDrawContours(image, points, color, CV_RGB(0,0,0), -1, CV_FILLED, 8, cvPoint(0,0));
}
contours = contours.h_next();
}
cvSaveImage("myfindcontour.png", image);
}
This is the output that i get (I used different colors for every shape, but in the final output i will use only white for detected warning sign and everything other left black):
You have to do the following:
Detect red color on image - you will get 1bit image where: 0=non-red, 1=red.
Detect triangles on created in previous step image. You can do that using approxPoly function.
see ,the first find the contour area.
compare it with the precalculated value and keep it with in a range
like
if(area>Mincontourarea && area<maxcontourare)
{
thats it now we have the signboard do
}
the value if calculated wouldnot be bigger than the car conotur,
to get the contoutr
up to my knowledge u need
Moments operator
code for the momnts operator:
Moments moment = moments((cv::Mat)contours[index]);
area = moment.m00; //m00 gives the area of the detected contour
put the above code before the if block discussed above
if you want the x and y coordinates put a post again..
take a look of my answer, it is in c++ but using opencv it is able to detect road signs, you can take it as a good example.
https://stackoverflow.com/a/52651521/8035924
Related
i am a newcomer in OpenCV and i am creating an OMR(Optical Mark Recognition) system in Java in order to detect the answers on a multiple choice paper sheet.I have created a form that consists of one big rectangle that is used for answering the questions by drawing the right circle, and one smaller rectangle that is for detecting a unique number that is the identity of the one answering.
Here is the image of the form:
Now my programm is detecting the upper rectangle the AM one but cannot detect the bigger one. My image is passing though 6 stages 1st dilation,2nd gray, 3rd threshold,4th blur,5th canny and 6th adaptiveThreshold. Here you can see that
dilated1 = new Mat(source1.size(), CV_8UC1);
dilate(source1, dilated1, getStructuringElement(MORPH_RECT, new Size(3, 3)));
gray1 = new Mat(dilated1.size(), CV_8UC1);
cvtColor(dilated1, gray1, COLOR_BGR2GRAY);
thresh1 = new Mat(gray1.rows(), gray1.cols(), gray1.type());
threshold(gray1, thresh1, 0, 255, THRESH_BINARY + THRESH_OTSU );
blur1 = new Mat(thresh1.size(), CV_8UC1);
blur(gray1, blur1, new Size(5.,5.));
canny1 = new Mat(blur1.size(), CV_8UC1);
Canny(blur1, canny1,160, 80);
adaptiveThresh1 = new Mat(canny1.rows(), gray1.cols(), gray1.type());
adaptiveThreshold(canny1, adaptiveThresh1, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 11,2);
I am also using findContours like that
findContours(adaptiveThresh1.clone(), contours1, hierarchy1, RETR_TREE, CHAIN_APPROX_SIMPLE);
I have created two different java classes because there are other things to detect in the small rectangle and other on the bigger. The code above is what i am using in order to try detecting the bigger rectangle. I have tried many different numbers in all the steps and still nothing.
When i am using only the bigger rectangle in an image it works just fine, but in combination with the other one it cannot detect it. It's for my thesis and its really important for me. Any help is appreciated and whatever you want me to add in order to help you please let me know.
You can find the largest contour and second largest contour.
Suggested stages:
Convert image to Grayscale (as you did).
Draw thick white rectangle around the image - making sure there is not black contour around the image.
Apply threshold and convert to binary (as you did).
The code I posted also inverse polarity, for the contours to be white.
Find contours.
Use RETR_EXTERNAL instead of RETR_TREE, because you don't need to find contours within contours.
Iterate the contours, and find the one with the largest area, and the one with the second largest area.
The contours with the largest area is the lower rectangle.
The contours with the second largest area is the upper rectangle.
Here is a Python implementation (not JAVA, but close enough):
import cv2
# Read input image
img = cv2.imread('image.png')
# Draw thick rectangle around the image - making sure there is not black contour around the image
cv2.rectangle(img, (0, 0), (img.shape[1], img.shape[0]), (255, 255, 255), thickness = 5)
# Convert from BGR to Grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Apply threshold on gray image - use automatic threshold algorithm (use THRESH_OTSU) and invert polarity.
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
# Find contours
cnts, heir = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
max_a = 0 # Maximum area
smax_a = 0 # Second maximum area
max_c = [] # Contour with maximum area
smax_c = [] # Contour with second maximum area (maximum excluding max_c)
# Iterate contours
for c in cnts:
area = cv2.contourArea(c)
if area > max_a: # If area is grater than maximum, second max = max, and max = area
smax_a = max_a
smax_c = max_c # Second max contour gets maximum contour
max_a = area
max_c = c # Maximum contour gets c
elif area > smax_a: # If area is grater than second maximum, replace second maximum
smax_a = area
smax_c = c
#Get bounding rectangle of contour with maximum area, and mark it with green rectangle
x, y, w, h = cv2.boundingRect(max_c)
cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), thickness = 2)
#Get bounding rectangle of contour with second maximum area, and mark it with blue rectangle
x, y, w, h = cv2.boundingRect(smax_c)
cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), thickness = 2)
# Show result (for testing).
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:
I am using OpenCV in an Android application. I want the mobile application to automatically take a photo when a rectangle (something in the shape of a receipt for example) is in view. I am using Canny edge detection but when I am looking for contours, the array size is greater than 1500. Obviously it is not optimal to loop through all the contours and find the largest one so I was wondering is it possible to filter out the largest contour automatically through an api?
My code so far:
ArrayList contours;
#Override
public Mat onCameraFrame(final CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
// Clear contours array on each frame
contours.clear();
// Get Grayscale image
final Mat gray = inputFrame.gray();
// Canny edge detection
Imgproc.Canny(gray, gray, 300, 1000, 5, true);
// New empty black matrix to store the edges captured
Mat dest = new Mat();
Core.add(dest, Scalar.all(0), dest);
// Copy the edge data over to the empty black matrix
gray.copyTo(dest);
// Is there a way to filter the size of contours so that not everything is returned? Right now this function is returning a lot of contours (1500 +)
Imgproc.findContours(gray, contours, hirearchy, Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
return dest;
}
EDIT
The user will be holding the phone and I want the application to automatically take a photo when the receipt is in view.
Example receipt
I have covered the basic techniques you may use, in the following Python code, it won't be hard to translate the code in the language of your choice, java in this case. So the technique involves:
Estimate the color of object you want to segment, which is white in your case, so safe limits for upper and lower bound can be approximated as:
RECEIPT_LOWER_BOUND = np.array([200, 200, 200])
RECEIPT_UPPER_BOUND = np.array([255, 255, 255])
Apply some Blur to input image to make the color distribution smooth, which would reduce the smaller contours in future.
img_blurred = cv2.blur(img, (5, 5))
Apply dilation to the binary image to remove the neighbouring smaller contours which surround your target largest contour
kernel = np.ones((10, 10), dtype=np.uint8)
mask = cv2.dilate(mask, kernel)
Now find contours in the mask after applying above operations and filter out the contour on the basis of contourArea.
im, contours, hierarchy = cv2.findContours(receipt_mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
largest_contour = max(contours, key=lambda x: cv2.contourArea(x))
Finally you may apply some threshold over the area to verify if the input was really a ticket or not.
Code:
import cv2
import numpy as np
# You may change the following ranges to define your own lower and upper BGR bounds.
RECEIPT_LOWER_BOUND = np.array([200, 200, 200])
RECEIPT_UPPER_BOUND = np.array([255, 255, 255])
def segment_receipt(img):
# Blur the input image to reduce the noise which in-turn reduces the number of contours
img_blurred = cv2.blur(img, (5, 5))
mask = cv2.inRange(img_blurred, RECEIPT_LOWER_BOUND, RECEIPT_UPPER_BOUND)
# Also dilate the binary mask which further reduces the salt and pepper noise
kernel = np.ones((10, 10), dtype=np.uint8)
mask = cv2.dilate(mask, kernel)
return mask
def get_largest_contour_rect(image):
receipt_mask = segment_receipt(image)
im, contours, hierarchy = cv2.findContours(receipt_mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
print "Number of contours found :", len(contours)
# Sorting the contours to get the largest one
largest_contour = max(contours, key=lambda x: cv2.contourArea(x))
# Return the last contour in sorted list as the list is sorted in increasing order.
return cv2.boundingRect(largest_contour)
image = cv2.imread("path/to/your/image.jpg")
rect = get_largest_contour_rect(image)
Output:
#J.Doe I am currently working on such a project and I have successfully being able to isolate the largest contour in the image after a whole lot of processing. The only part remaining is recognizing a rectangular contour and taking a picture.
mRgba = inputFrame.rgba();
Imgproc.Canny(mRgba,mCanny,50,200);
Imgproc.cvtColor(mRgba, mGray, Imgproc.COLOR_RGB2GRAY);
Imgproc.GaussianBlur(mGray, mGray1, new Size(3, 3), 1);
Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT,new Size(9,9));
Imgproc.dilate(mGray1, mGray2, kernel);
Imgproc.Canny(mGray2, mCanny, 50, 200);
Imgproc.findContours(mCanny,contours,hierarchy,Imgproc.RETR_TREE,Imgproc.CHAIN_APPROX_SIMPLE);
double maxVal = 0;
int maxValIdx = 0;
for(int contourIdx = 0; contourIdx < contours.size(); contourIdx++){
double contourArea = Imgproc.contourArea(contours.get(contourIdx));
if(maxVal < contourArea)
{
maxVal = contourArea;
maxValIdx = contourIdx;
}
}
Imgproc.drawContours(mRgba,contours,maxValIdx,new Scalar(0,255,255),-1);
return mRgba;
Be wary of the image names i changed them over different processes.
Formula for focal length is given below:
F = (P x D) / W
But I am unable to find pixel value (P) of the rectangle that appears on the detected face in real time:
Want to find the width of rectangle drawn around the mobile phone in the image:
It was done using Python and OpenCV but I am confused as to how to implement it in Java OpenCV.
http://www.pyimagesearch.com/2015/01/19/find-distance-camera-objectmarker-using-python-opencv/
In the image you added you have drawn a square around the phone so you already have the width of the square. What I understand from your question is that you want to get the real rectangle around the phone.
For this to be achieved there can be several solutions but one done through working with contours is like the following code:
// localImage would be the cropped image of the square you have drawn,
// the global image is the original image and phoneSquare is the Rect you
// have drawn
localImage = new Mat(globalImage, phoneSqure).clone();
// make the phone black and surroundings white
Imgproc.threshold(localImage, localImage, 127, 255, Imgproc.THRESH_OTSU + Imgproc.THRESH_BINARY_INV);
// get contours
ArrayList<MatOfPoint> contours = new ArrayList<>();
Mat hierarchy = new Mat();
Imgproc.findContours(canny, contours, hierarchy, Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_NONE);
// sort contours by size and get the biggest which is assumed to be the outer contour of the phone
contours.sort(new Comparator<MatOfPoint>() {
#Override
public int compare(MatOfPoint o1, MatOfPoint o2) {
return (int) Math.signum(o2.size().area() - o1.size().area());
}
});
MatOfPoints biggestContour = contours.get(contours.size() - 1);
// get the bounding rectangle of the phone, the you can get the width
Rect whatYouWant = Imgproc.boundingRect(biggestContour);
I'm working on a java image processing program(based on OpenCV library).
I need to remove the noise from the first image to get a clean image like the second image below.
In this specific case, what are the best ways to remove noise?
The most important part is how to remove the black parts that surround the image.
First image:
Second image:
get rid of gray: threshold the image so that any gray becomes white
get rid of border: floodfill at location (0.0) with white
That will leave just a few remaining issues to clean up: detect each black blob in the image, if the area of a blob is less than some amount floodfill the blob with white. One way to implement that follows.
Note that floodfill returns the number of pixels it filled. This allows you to scan for a black pixel, when you find one fill with gray. If the filled are is too small, fill again with white to erase the blob and then keep scanning, otherwise leave the blob as gray and keep scanning for black. At the end everything you want will be gray, so scan the image again and whenever you find gray flood fill with black.
Following program may be helpful to solve your problem,
This program thresholds the input image and selects only connected
components with particular size
#include <iostream>
#include<cv.h>
#include<highgui.h>
using namespace std;
using namespace cv;
int main(int argc, char *argv[])
{
IplImage *img1 = cvLoadImage(argv[1] , 0);
IplImage *img3 = cvLoadImage(argv[1]);
IplImage *img2 = cvCloneImage(img1);
cvNamedWindow("Orig");
cvShowImage("Orig",img1);
cvWaitKey(0);
cvAdaptiveThreshold(img1, img1, 255, CV_ADAPTIVE_THRESH_GAUSSIAN_C,
CV_THRESH_BINARY_INV, 15);
cvNamedWindow("Thre");
cvShowImage("Thre",img1);
cvWaitKey(0);
IplImage *tempImg = cvCloneImage(img1);
CvMemStorage *storage = cvCreateMemStorage(0);
CvSeq *contour = NULL;
cvFindContours(tempImg, storage, &contour, sizeof(CvContour),
CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
for (; contour != 0; contour = contour->h_next)
{
CvRect r = cvBoundingRect(contour);
int area = r.width * r.height;
if (area < 50 || area > 500) continue;
cvRectangle(img3, cvPoint(r.x, r.y), cvPoint(r.x + r.width, r.y + r.height),
CV_RGB(255, 0, 0), 1);
}
cvNamedWindow("D");
cvShowImage("D",img3);
cvWaitKey(0);
}
Currently I am developing image processing project and I'm using javacv to develop image processing components. I was able to extract some interesting parts of a image and now I need to read the x and y coordinates of those objects.
This is the image that I haveextracted
And I need to identify those objects and draw square around those objects. I went through some tutorials and try to identify objects using following code.
IplImage img="sourceimage";
CvSize sz = cvSize(img.width(), img.height());
IplImage gry=cvCreateImage(sz, img.depth(), 1);
cvCvtColor(img, gry, CV_BGR2GRAY);
cvThreshold(gry, gry, 200, 250, CV_THRESH_BINARY);
CvMemStorage mem = CvMemStorage.create();
CvSeq contours = new CvSeq();
CvSeq ptr = new CvSeq();
cvFindContours(gry, mem, contours, Loader.sizeof(CvContour.class) , CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0));
CvRect boundbox;
for (ptr = contours; ptr != null; ptr = ptr.h_next()) {
boundbox = cvBoundingRect(ptr, 0);
if(boundbox.height()>10||boundbox.height()>10){
cvRectangle( gry, cvPoint( boundbox.x(), boundbox.y() ), cvPoint( boundbox.x() + boundbox.width(), boundbox.y() + boundbox.height()),CvScalar.BLUE, 0, 0, 0 );
System.out.println(boundbox.x()+", "+boundbox.y());
}
}
cvShowImage("contures", gry);
cvWaitKey(0);
But it doesn't draw as rectangle around the objects. I would like to know whether I can use cvFindContours method to identify those objects ? Please can some one explain how to archive my objective using javacv/opencv?
Try to go through following code and it will give answer for your question.
IplImage img=cvLoadImage("pathtosourceimage");
CvSize cvSize = cvSize(img.width(), img.height());
IplImage gry=cvCreateImage(cvSize, img.depth(), 1);
cvCvtColor(img, gry, CV_BGR2GRAY);
cvThreshold(gry, gry, 200, 255, CV_THRESH_BINARY);
cvAdaptiveThreshold(gry, gry, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY_INV, 11, 5);
CvMemStorage storage = CvMemStorage.create();
CvSeq contours = new CvContour(null);
int noOfContors = cvFindContours(gry, storage, contours, Loader.sizeof(CvContour.class), CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE, new CvPoint(0,0));
CvSeq ptr = new CvSeq();
int count =1;
CvPoint p1 = new CvPoint(0,0),p2 = new CvPoint(0,0);
for (ptr = contours; ptr != null; ptr = ptr.h_next()) {
CvScalar color = CvScalar.BLUE;
CvRect sq = cvBoundingRect(ptr, 0);
System.out.println("Contour No ="+count);
System.out.println("X ="+ sq.x()+" Y="+ sq.y());
System.out.println("Height ="+sq.height()+" Width ="+sq.width());
System.out.println("");
p1.x(sq.x());
p2.x(sq.x()+sq.width());
p1.y(sq.y());
p2.y(sq.y()+sq.height());
cvRectangle(img, p1,p2, CV_RGB(255, 0, 0), 2, 8, 0);
cvDrawContours(img, ptr, color, CV_RGB(0,0,0), -1, CV_FILLED, 8, cvPoint(0,0));
count++;
}
cvShowImage("contures",img);
cvWaitKey(0);
This is the output that I got for your given image.
Did you know that findContours function finds white contours on black background?
Just do bitwise_not (or analogue in JavaCV) to your image and after this apply findContours. This is actually fix to your problem.
No need for third party libraries! What you are looking for can be achieved in OpenCV using a technique known as bounding box:
The trick is to use cvFindContours() to retrieve the contours and then cvApproxPoly() to improve the result. Notice that you'll have to invert the colors of the input image using cvNot() because cvFindContours() search for white contours.
There's a nice introduction to contours in this post.
There's a Java demo that implements a version of the bounding box to detect car number plates.
By the way cvMinEnclosingCircle() returns the center of the circle as CvPoint2D32f.
cvFindContours is the right solution to your problem.
And I have verified that it works on your images.
You have System.out.println inside, what's the output?
cvFindContours could be tricky to use, I have wrapped it into a more general C++ function. Also I use a class called vBlob to describe an object cvFindContours detects. From what I see from your java code, java version API is very similar to C/C++ version. So it won't be hard to rewrite it.
ps. Astor gave the right answer.
I m using c++ api but I think there should also be some function in javacv named drawContours or cvDrawContours which uses the output of findContours.