Detection of red circle-alike | openCV | Java - java

I am writing a program which need to detect red circle-alikes from this picture.
I have tried canny edge detection and find contours but none of them find this red "circles". I also tried to convert this to hsv and detect this by color but I couldn't determine good range for this color, maybe background color confuses it?
I put here a piece of my code with my final attempt..
Mat image = new Mat();
image = Imgcodecs.imread("image.jpg");
Mat hsvImage = new Mat();
Mat grayscaleImage = new Mat();
Mat binaryImage = new Mat();
Imgproc.blur(image, image, new Size(1, 1));
Imgproc.cvtColor(image, hsvImage, Imgproc.COLOR_BGR2HSV);
Imgproc.cvtColor(image, grayscaleImage, Imgproc.COLOR_BGR2GRAY);
Imgproc.equalizeHist(grayscaleImage, grayscaleImage);
Imgproc.Canny(grayscaleImage, grayscaleImage, 50, 150, 3,false);
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Imgproc.findContours(grayscaleImage.clone(), contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
for (int id=0;id<contours.size();id++){
MatOfPoint2f mop2f = new MatOfPoint2f();
contours.get(id).convertTo(mop2f,CvType.CV_32F);
RotatedRect rectangle = Imgproc.minAreaRect(mop2f);
if (rectangle.boundingRect().width>80)
Imgproc.drawContours(image,contours,id,new Scalar(0,255,0));
}

If you want to process that marked image, you really might want to detect colors. Typically this is done in HSV color-space.
Here is some C++ code to detect "red" color. The result isn't good enough to use findContours yet, but maybe after some dilation. Maybe you can convert the code to Java.
If you want to detect different color, change the line redMask = thresholdHue(hsv, 0, 20, 50, 50); to mask = thresholdHue(hsv, yourWantedHueColorValue, 20, 50, 50);`
// for example to shift a circluar hue-channel
cv::Mat shiftChannel(cv::Mat H, int shift, int maxVal = 180)
{
// CV_8UC1 only!
cv::Mat shiftedH = H.clone();
//int shift = 25; // in openCV hue values go from 0 to 180 (so have to be doubled to get to 0 .. 360) because of byte range from 0 to 255
for (int j = 0; j < shiftedH.rows; ++j)
for (int i = 0; i < shiftedH.cols; ++i)
{
shiftedH.at<unsigned char>(j, i) = (shiftedH.at<unsigned char>(j, i) + shift) % maxVal;
}
return shiftedH;
}
cv::Mat thresholdHue(cv::Mat hsvImage, int hueVal, int range = 30, int minSat = 50, int minValue = 50)
{
// hsvImage must be CV_8UC3 HSV image.
// hue val and range are in openCV's hue range (0 .. 180)
// range shouldnt be bigger than 90, because that's max (all colors), after shifting the hue channel.
// this function will
// 1. shift the hue channel, so that even colors near the border (red color!) will be detectable with same code.
// 2. threshold the hue channel around the value 90 +/- range
cv::Mat mask; // return-value
std::vector<cv::Mat> channels;
cv::split(hsvImage, channels);
int targetHueVal = 180 / 2; // we'll shift the hue-space so that the target val will always be 90 afterwards, no matter which hue value was chosen. This can be important if
int shift = targetHueVal - hueVal;
if (shift < 0) shift += 180;
cv::Mat shiftedHue = shiftChannel(channels[0], shift, 180);
// merge the channels back to hsv image
std::vector<cv::Mat> newChannels;
newChannels.push_back(shiftedHue);
newChannels.push_back(channels[1]);
newChannels.push_back(channels[2]);
cv::Mat shiftedHSV;
cv::merge(newChannels, shiftedHSV);
// threshold
cv::inRange(shiftedHSV, cv::Vec3b(targetHueVal - range, minSat, minValue), cv::Vec3b(targetHueVal + range, 255, 255), mask);
return mask;
}
int main(int argc, char* argv[])
{
cv::Mat input = cv::imread("C:/StackOverflow/Input/redCircleLikeContours.jpg");
cv::Mat redMask;
cv::Mat hsv;
cv::cvtColor(input, hsv, CV_BGR2HSV);
redMask = thresholdHue(hsv, 0, 20, 50, 50);
cv::imshow("red", redMask);
cv::imshow("input", input);
cv::imwrite("C:/StackOverflow/Output/redCircleLikeContoursMask.png", redMask);
cv::waitKey(0);
return 0;
}
Here's the result:

Here is my code if somebody would want to look :)
public static void main (String args[]){
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
Mat image = new Mat();
image = Imgcodecs.imread("imageorg.jpg");
if ( image == null) System.out.println("Image is fine");
else System.out.println("Wrong path to image");
Mat hsvImage = new Mat();
Imgproc.blur(image, image, new Size(3,3));
Imgproc.cvtColor(image, hsvImage, Imgproc.COLOR_BGR2HSV);
Mat redMask = new Mat();
redMask = thresholdHue(hsvImage,0,20,50,50);
Mat kernel = new Mat();
kernel = Imgproc.getStructuringElement(Imgproc.MORPH_DILATE, new Size(2,2));
Mat dilateMat = new Mat();
Imgproc.dilate(redMask, dilateMat, kernel);
Imgcodecs.imwrite("redCircleLikeContours.png", redMask);
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Imgproc.findContours(dilateMat.clone(), contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
List<MatOfPoint> removedContoursList = new ArrayList<MatOfPoint>();
for (int id=0;id<contours.size();id++){
MatOfPoint2f mop2f = new MatOfPoint2f();
contours.get(id).convertTo(mop2f,CvType.CV_32F);
RotatedRect rectangle = Imgproc.minAreaRect(mop2f);
if (rectangle.boundingRect().height<10){
removedContoursList.add(contours.get(id));
System.out.println("removing: "+rectangle.boundingRect());
contours.remove(id);
id--;
}
}
}
public static Mat thresholdHue(Mat hsvImage, int hueVal, int range, int minSat, int minValue)
{
Mat mask = new Mat();
List<Mat> channels = new ArrayList<Mat>();
Core.split(hsvImage, channels);
int targetHueVal = 180 / 2;
int shift = targetHueVal - hueVal;
if (shift < 0) shift += 180;
Mat shiftedHue = shiftChannel(channels.get(0), shift, 180);
List<Mat> newChannels = new ArrayList<Mat>();
newChannels.add(shiftedHue);
newChannels.add(channels.get(1));
newChannels.add(channels.get(2));
Mat shiftedHSV = new Mat();
Core.merge(newChannels, shiftedHSV);
Core.inRange(shiftedHSV, new Scalar(targetHueVal - range, minSat, minValue), new Scalar(targetHueVal + range, 255, 255), mask);
return mask;
}
private static Mat shiftChannel(Mat H, int shift, int maxVal)
{
Mat shiftedH = H.clone();
for (int j = 0; j < shiftedH.rows(); ++j)
for (int i = 0; i < shiftedH.cols(); ++i)
{
shiftedH.put(j, i,(shiftedH.get(j,i)[0] + shift) % maxVal);
}
return shiftedH;
}

Related

Detecting the outer circle using Hough Circle Transform

When i apply houghCircle to my image it always detect the inner circle, i spent much time tuning the parameters but the result always the same.
My procedure is convert image into HSV color space then Threshold on a red color to get a binary image then applying houghCircle.
My Image
After Hough Circle
Code
Mat hsv = new Mat();
Imgproc.cvtColor(bgr, hsv, Imgproc.COLOR_BGR2HSV); //BGR to HSV
Mat mask1 = new Mat();
Mat mask2 = new Mat();
Core.inRange(hsv, new Scalar(0, 100, 100), new Scalar(10, 255, 255), mask1);
Core.inRange(hsv, new Scalar(160, 100, 100), new Scalar(179, 255, 255), mask2);
Mat hsvThres = new Mat();
Core.bitwise_or(mask1, mask2, hsvThres);
//
Mat circles = new Mat();
int iCannyUpperThreshold = 1; //100
int iMinRadius = -1; //20 //90
int iMaxRadius = -1; //400 //150-1000
int iAccumulator = 1;//300
Imgproc.HoughCircles(hsvThres, circles, Imgproc.CV_HOUGH_GRADIENT, //2
1.0, hsvThres.rows() , iCannyUpperThreshold, iAccumulator, iMinRadius, iMaxRadius);
if (circles.cols() > 0)
for (int x = 0; x < circles.cols(); x++)
{
double vCircle[] = circles.get(0,x);
if (vCircle == null)
break;
Point pt = new Point(Math.round(vCircle[0]), Math.round(vCircle[1]));
int radius = (int)Math.round(vCircle[2]);
Core.circle(bgr, pt, radius, new Scalar(0,255,0), 4, 8, 0);
}
NOTE
1) When i changed minRadius to 90 it detected the outer one instead of the inner but i want smth generic that will work for images/signs not only for this test image.
2) If i converted the Original image into Grey space instead of HSV it detects the outer rather than the inner, but also it is not my case since i have to convert my image into HSV to do Thresholding.

Segmentation number from water meter

i have a problem with character segmentation from water meter,
i have a image water meter
Then i have threshold this image after threshold
And i have get a register meter after segmentation
My problem is to get each character from register meter..
Here my code:
System.out.println("Character Segmentation the image at " + path + "... ");
// get the jpeg image from the internal resource folder
Mat image = MatImageFromPath;
// convert the image in gray scale
Imgproc.cvtColor(image, image, Imgproc.COLOR_BGR2GRAY);
// thresholding the image to make a binary image
Imgproc.threshold(image, image, 100, 255, Imgproc.THRESH_BINARY_INV);
// finding the contours
ArrayList<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Mat hierarchy = new Mat();
Imgproc.findContours(image, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);
Imgproc.drawContours(image, contours, 3, new Scalar(0, 255, 0),3);
// finding best bounding rectangle for a contour whose distance is closer to the image center that other ones
Rect rect = new Rect();
ArrayList<Rect> contourRects = new ArrayList<Rect>();
Point p1 = new Point(rect.x,rect.y);
Point p2 = new Point((rect.x+rect.width),(rect.y+rect.height));
int i=0;
for (MatOfPoint contour : contours) {
rect = Imgproc.boundingRect(contour);
contourRects.add(rect);
contour2f = new MatOfPoint2f( contours.get(i).toArray() );
i++;
}
Collections.sort(contourRects, new Comparator<Rect>(){
#Override
public int compare(Rect o1, Rect o2){
return o1.x-o2.x;
}
});
for (int j = 0; j <= contourRects.size()-1; j++) {
if ((contourRects.get(j).width >= 50 && contourRects.get(j).width <= 120) &&
(contourRects.get(j).height >= 150 && contourRects.get(j).height <= 210)) {
System.out.println(contourRects.get(j).width);
Mat result = image.submat(contourRects.get(j));
Imgcodecs.imwrite("BoundingBox/"+(j+1)+".png", result);
// write the new image on disk
path = "BoundingBox/"+(j+1)+".png";
} else {
continue;
}
}
Imgcodecs.imwrite("BoundingBox/draw.png", image);
label2.setIcon(ImageFromPath(path));
All images at : images
can anyone help my problem? Thanks in advance

OpenCV Java text segmentation

I am working on a licence plate recognition software using OpenCV, Tesseract and Java but experiencing issues, I cant seem to segment my text correctly, its not always that I get all characters to be detected and bounded with a bounding box these are some of my outputs with my code...and also when I detect the characters I never know which character is in which box so when I pass them through tesseract they get jumbled, how do I format my string?
This one fails despite the clearly visible characters:
Here Z and 6 fail to be detected even when 6 is clearly visible:
Below is my code:
originalFrame = image.clone();
roiColor = image.clone();
Imgproc.cvtColor(image, image, Imgproc.COLOR_BGR2GRAY, 0);
originalFrameGrayScale = image.clone();
Mat morph = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(9, 9));
Imgproc.morphologyEx(image, image, Imgproc.MORPH_TOPHAT, morph);
Imgproc.Sobel(image, image, -1, 2, 0);
Imgproc.GaussianBlur(image, image, new Size(5,5), 3,3);
Imgproc.morphologyEx(image, image, Imgproc.MORPH_CLOSE, morph);
Imgproc.threshold(image, image, 200, 255, Imgproc.THRESH_OTSU);
Vector<Rect> rectangles = detectionContour(image);
Mat roi = originalFrameGrayScale.clone();
if(!rectangles.isEmpty()){
roi = originalFrameGrayScale.submat(rectangles.get(0));
roiBlack = roi.clone();
roiColor = roiColor.submat(rectangles.get(0));
Imgproc.rectangle(originalFrame, rectangles.get(0).br(), rectangles.get(0).tl(), new Scalar(0,0,255), 2);
}
Imgproc.medianBlur(roi, roi, 3);
Imgproc.adaptiveThreshold(roi, roi, 225, Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C, Imgproc.THRESH_BINARY, 15, 3);
roiBinarize = roi.clone();
Mat erode = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(1, 1));
Mat dilate = Imgproc.getStructuringElement(Imgproc.MORPH_RECT,new Size(1, 1));
Imgproc.morphologyEx(roi, roi, Imgproc.MORPH_OPEN, dilate);
Imgproc.morphologyEx(roi, roi, Imgproc.MORPH_OPEN, erode);
Imgproc.Canny(roi, roi, 150, 150 * 3, 3, true);
Vector<Rect> letters = detectionPlateCharacterContour(roi);
doTesseractOCR(letters, roiBinarize);
private static void doTesseractOCR(Vector<Rect> letters, Mat plate){
Tesseract instance = new Tesseract(); //
instance.setLanguage(LANGUAGE);
String resultPlate = "";
for(int i= 0; i < letters.size(); i++){
BufferedImage letter = OpenCvUtils.Mat2bufferedImage(plate.submat(letters.get(i)));
try {
String result = instance.doOCR(letter);
resultPlate += result + " position "+i;
} catch (TesseractException e) {
System.err.println(e.getMessage());
}
System.out.println("Tesseract output: "+resultPlate);
}
}
private static Vector<Rect> detectionPlateCharacterContour(Mat roi) {
Mat contHierarchy = new Mat();
Mat imageMat = roi.clone();
Rect rect = null;
List<MatOfPoint> contours = new ArrayList<>();
Imgproc.findContours(imageMat, contours, contHierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_NONE);
Vector<Rect> rect_array = new Vector<>();
for (int i = 0; i < contours.size(); i++) {
rect = Imgproc.boundingRect(contours.get(i));
double ratio = 0;
if(rect.height > rect.width){
ratio = rect.height/rect.width;
}else{
ratio = rect.width/rect.height;
}
Logger.printMessage("Ratio of letter: "+ratio);
double contourarea = Imgproc.contourArea(contours.get(i));
if (contourarea >= 100 && contourarea <= 1000 && ( ratio >= 1 && ratio <= 2)) {
Imgproc.rectangle(roiColor, rect.br(), rect.tl(), new Scalar(255,0,0));
rect_array.add(rect);
}
}
contHierarchy.release();
return rect_array;
}

why dilate functin give different result when the parameteer is the same

I try to dilate three image of characters in java opencv. i found out than ever it same character with same font and size, after dilate the result is different. so i try with same image the result is still different. Here is my test code.
for (int j = 0; j < 3; j++) {
Mat InputSrc = openFile("src\\myOpencv\\ocr\\crop1.png");
Mat tempImg =new Mat();
Imgproc.cvtColor(InputSrc, tempImg, Imgproc.COLOR_BGR2GRAY);
Imgproc.threshold(tempImg, tempImg, 0, 255, Imgproc.THRESH_OTSU);
imageViewer.show(tempImg, "src");
Mat kernal5 = new Mat(5, 5, CV_8U);
Point midPoint = new Point(-1, -1);
Scalar scalarOne = new Scalar(1);
Mat binImg2 = new Mat();
Imgproc.dilate(tempImg, binImg2, kernal5, midPoint, 1, 1, scalarOne);
imageViewer.show(binImg2, "dilate");
}
thank

Find circles image processing in OpenCV/JaVa

I'm using opencv and java to find circles on an image, I have the image below so far. I'm using Hough to find the circles with the code like this :
public static Vector<Mat> circles(Mat img){
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
long start_time = System.nanoTime();
Imgproc.resize(img, img, new Size(450,250));
Mat gray = new Mat();
Imgproc.cvtColor(img, gray, Imgproc.COLOR_BGR2GRAY);
Imgproc.blur(gray, gray, new Size(3, 3));
Mat edges = new Mat();
int lowThreshold = 40;
int ratio = 3;
Imgproc.Canny(gray, edges, lowThreshold, lowThreshold * ratio);
Mat circles = new Mat();
Vector<Mat> circlesList = new Vector<Mat>();
Imgproc.HoughCircles(edges, circles, Imgproc.CV_HOUGH_GRADIENT, 1, 60, 200, 20, 30, 0 );
System.out.println("#rows " + circles.rows() + " #cols " + circles.cols());
double x = 0.0;
double y = 0.0;
int r = 0;
for( int i = 0; i < circles.rows(); i++ )
{
double[] data = circles.get(i, 0);
for(int j = 0 ; j < data.length ; j++){
x = data[0];
y = data[1];
r = (int) data[2];
}
Point center = new Point(x,y);
// circle center
Core.circle( img, center, 3, new Scalar(0,255,0), -1);
// circle outline
Core.circle( img, center, r, new Scalar(0,0,255), 1);
Imshow im1 = new Imshow("Hough");
im1.showImage(img);
Rect bbox = new Rect((int)Math.abs(x-r), (int)Math.abs(y-r), (int)2*r, (int)2*r);
Mat croped_image = new Mat(img, bbox);
Imgproc.resize(croped_image, croped_image, new Size(160,160));
circlesList.add(croped_image);
Imshow m2 = new Imshow("cropedImage");
m2.showImage(croped_image);
}
long end_time = System.nanoTime();
long duration = (end_time - start_time)/1000000; //divide by 1000000 to get milliseconds.
System.out.println("duration : " + duration * 0.001 + " s");
return circlesList;
}
BUT it always detects only one circle.
My Question is how I can detect all the circles in an image using java/OpenCV ?
Note:-
1- I'm using Mat called circles in HoughCircles function parameters , because the function requires a Mat in Java.
2- I'm using openCV 2.4.11 version.
The circles are saved in columns of the mat circles
try replacing for loop as:
for( int i = 0; i < circles.cols(); i++)
{
double[] data = circles.get(0,i);
...

Categories