OpenCV - Closing contours (Java) - java

I'm currently trying to close the contours on the right of this picture:
Sample.
The reason for the opened contour lies in kabeja, a library to convert DXF files to images. It seems that on some images it doesn't convert the last pixel column (or row) and that's why the Sample picture is open.
I had the idea to use Core.copyMakeBorder() in Opencv, to add some space to the picture. After that I tried to use Imgproc.approxPolyDP() to close the contour, but this doesn't work. I tried this with different Epsilon values: Pics EDIT: Can't post more than 2 links
The reason for that is maybe that the contour surrounds the line. It never closes the contour where i want it to do.
I tried another method using Imgproc.convexHull(), which delivers this one: ConvexHull.
This could be useful for me, but i have no idea how to take out the part of the convex hull i need and merge it together with the contour to close it.
I hope that someone has an idea.
Here is my method for Imgproc.approxPolyDP()
public static ArrayList<MatOfPoint> makeComplete(Mat mat) {
System.out.println("makeComplete: START");
Mat dst = new Mat();
Core.copyMakeBorder(mat, dst, 10, 10, 10, 10, Core.BORDER_CONSTANT);
ArrayList<MatOfPoint> cnts = Tools.getContours(dst);
ArrayList<MatOfPoint2f> opened = new ArrayList<>();
//convert to MatOfPoint2f to use approxPolyDP
for (MatOfPoint m : cnts) {
MatOfPoint2f temp = new MatOfPoint2f(m.toArray());
opened.add(temp);
System.out.println("First loop runs");
}
ArrayList<MatOfPoint> closed = new ArrayList<>();
for (MatOfPoint2f conts : opened) {
MatOfPoint2f temp = new MatOfPoint2f();
Imgproc.approxPolyDP(conts, temp, 3, true);
MatOfPoint closedTemp = new MatOfPoint(temp.toArray());
closed.add(closedTemp);
System.out.println("Second loop runs");
}
System.out.println("makeComplete: END");
return closed;
}
And here the code for Imgproc.convexHull()
public static ArrayList<MatOfPoint> getConvexHull(Mat mat) {
Mat dst = new Mat();
Core.copyMakeBorder(mat, dst, 10, 10, 10, 10, Core.BORDER_CONSTANT);
ArrayList<MatOfPoint> cnts = Tools.getContours(dst);
ArrayList<MatOfPoint> out = new ArrayList<MatOfPoint>();
MatOfPoint mopIn = cnts.get(0);
MatOfInt hull = new MatOfInt();
Imgproc.convexHull(mopIn, hull, false);
MatOfPoint mopOut = new MatOfPoint();
mopOut.create((int) hull.size().height, 1, CvType.CV_32SC2);
for (int i = 0; i < hull.size().height; i++) {
int index = (int) hull.get(i, 0)[0];
double[] point = new double[]{
mopIn.get(index, 0)[0], mopIn.get(index, 0)[1]
};
mopOut.put(i, 0, point);
}
out.add(mopOut);
return out;
}
Best regards,
Brk

Assuming the assumption is correct, that the last row (for column it is similar) isn't converting (i.e. missing), then try the following. Assume x goes from left to right and y from top to bottom. We add one row of empty (white?) pixels at the image bottom and then go from left to right. Below is pseudo code:
// EMPTY - value of backgroung e.g. white for the sample image
PixelType curPixel = EMPTY;
int y = height - 1; // last row, the one we added
for (int x = 0; x < width; ++x)
{
// img(y,x) - current pixel, is "empty"
// img (y-1, x) - pixel above the current
if (img(y-1, x) != img(y, x))
{
// pixel above isn't empty, so we make current pixel non-empty
img(y, x) = img(y-1, x);
// if we were drawing, then stop, otherwise - start
if (curPixel == EMPTY)
curPixel = img(y-1, x);
else
curPixel = EMPTY;
}
else
{
img(y, x) = curPixel;
}
}

Related

In android how do I count the number of lines in a image using openCV

This is what I tried.I got the output as a grayscale image in img2 of an Imageview object.
The problem is as lines.cols() considers everything as a line.
I want the count to be exactly the number of larger lines as shown in the 1stpic (I mean the lines that seperates the parking lot,in which the car can ocupy) My output image Can anyone guide me how to get the exact count of parking lines.I have used openCV version 2.4.I have been working on this for the past 2 days
public String getCount() {
Bitmap bitmap = BitmapFactory.decodeResource(getApplicationContext().getResources(), R.drawable.park);
mat = new Mat();
edges = new Mat();
Mat mRgba = new Mat(612, 816, CvType.CV_8UC1);
Mat lines = new Mat();
Utils.bitmapToMat(bitmap, mat);
Imgproc.Canny(mat, edges, 50, 90);
int threshold = 50;
int minLineSize = 20;
int lineGap = 20;
Imgproc.HoughLinesP(edges, lines, 1, Math.PI / 180, threshold, minLineSize, lineGap);
int count = lines.cols();
int coun= lines.rows();
System.out.println("count = " + count);
System.out.println("coun = " + coun);
String cou = String.valueOf(count);
for (int x = 0; x < lines.cols(); x++) {
double[] vec = lines.get(0, x);
double x1 = vec[0],
y1 = vec[1],
x2 = vec[2],
y2 = vec[3];
Point start = new Point(x1, y1);
Point end = new Point(x2, y2);
Core.line(mRgba, start, end, new Scalar(255, 0, 0), 3);
}
Bitmap bmp = Bitmap.createBitmap(mRgba.cols(), mRgba.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(mRgba, bmp);
bitmap = bmp;
Drawable d = new BitmapDrawable(Resources.getSystem(), bitmap);
img2.setImageDrawable(d);
return cou;
}
You should modify one of the many answers regarding counting by OpenCV where kind of detection varies for every distinct case. You should make your own model for parking lines. Check some of these approaches/detectors Haar cascade classifier, latent SVM or Bag of Words.
You could also modify some of the answers that works for something else like this answer below for coins, only you should search for shape of parking lines instead of coins:
http://answers.opencv.org/question/36111/how-to-count-number-of-coins-in-android-opencv/

OpenCV detecting largest rectangle yields puzzling results

My aim is to detect the largest rectangle in an image, whether its skewed or not. After some research and googling I came up with a code that theoretically should work, however in half of the cases I see puzzling results.
I used OpenCV for Android, here is the Code:
private void find_parallels() {
Utils.bitmapToMat(selectedPicture,img);
Mat temp = new Mat();
Imgproc.resize(img,temp,new Size(640,480));
img = temp.clone();
Mat imgGray = new Mat();
Imgproc.cvtColor(img,imgGray,Imgproc.COLOR_BGR2GRAY);
Imgproc.GaussianBlur(imgGray,imgGray,new Size(5,5),0);
Mat threshedImg = new Mat();
Imgproc.adaptiveThreshold(imgGray,threshedImg,255,Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C,Imgproc.THRESH_BINARY,11,2);
List<MatOfPoint> contours = new ArrayList<>();
Mat hierarchy = new Mat();
Mat imageContours = imgGray.clone();
Imgproc.cvtColor(imageContours,imageContours,Imgproc.COLOR_GRAY2BGR);
Imgproc.findContours(threshedImg,contours,hierarchy,Imgproc.RETR_TREE,Imgproc.CHAIN_APPROX_SIMPLE);
max_area = 0;
int num = 0;
for (int i = 0; i < contours.size(); i++) {
area = Imgproc.contourArea(contours.get(i));
if (area > 100) {
MatOfPoint2f mop = new MatOfPoint2f(contours.get(i).toArray());
peri = Imgproc.arcLength(mop, true);
Imgproc.approxPolyDP(mop, approx, 0.02 * peri, true);
if(area > max_area && approx.toArray().length == 4) {
biggest = approx;
num = i;
max_area = area;
}
}
}
selectedPicture = Bitmap.createBitmap(640,480, Bitmap.Config.ARGB_8888) ;
Imgproc.drawContours(img,contours,num,new Scalar(0,0,255));
Utils.matToBitmap(img, selectedPicture);
imageView1.setImageBitmap(selectedPicture);}
In some cases it works excellent as can be seen in this image(See the white line between monitor bezel and screen.. sorry for the color):
Example that works:
However when in this image, and most images where the screen is greyish it gives crazy result.
Example that doesn't work:
Try use morphology, dilate and then erode with same kernel should make it better.
Or use pyrDown + pyrUp, or just blur it.
In short use low-pass filter class of methods, because your object of interest is much larger than noise.

How to get the Chain Code from OpenCVs findContours method

I already have spend some time trying to implement a chain code from a contour of a plant's leaf until I came across this question which claimed that the OpenCV method findContours can be used to find the chain code.
So far I have this code:
public static List<MatOfPoint> chainCode;
public static void main(String[] args) {
// load the Core OpenCV library by name
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
// create a display window
Imshow window1 = new Imshow("My Image - original");
// load an image from file (read and decode JPEG file)
Mat inputImg = Highgui.imread("files/11. Acer palmaturu/iPAD2_C11_EX01.JPG");
Mat grayImg = new Mat(inputImg.height(), inputImg.width(), CvType.CV_8UC1);
//turn into binary image and invert
Imgproc.cvtColor(inputImg, grayImg, Imgproc.COLOR_RGB2GRAY);
Imgproc.threshold(grayImg, grayImg, 0, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU);
Mat invertcolormatrix= new Mat(grayImg.rows(),grayImg.cols(), grayImg.type(), new Scalar(255,255,255));
Core.subtract(invertcolormatrix, grayImg, grayImg);
//get chain code
chainCode = new Vector<MatOfPoint>();
Imgproc.findContours(grayImg, chainCode, new Mat(), Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_NONE);
//loop through indices of individual contours to find largest contour
double largest_area = 0;
int largest_contour_index = 0;
for(int i = 0; i< chainCode.size(); i++){
//find the area of the contour
double a = Imgproc.contourArea(chainCode.get(i),false);
//find largest contour and it's index
if(a > largest_area){
largest_area= a;
largest_contour_index = i;
}
}
//draw largest contour
if(chainCode.size() > 0){
Imgproc.drawContours(inputImg, chainCode, largest_contour_index, new Scalar (0, 0, 255), 1);
System.out.println("chain code at " + largest_contour_index + "; " + chainCode.get(largest_contour_index));
}
//show image
window1.showImage(inputImg);
}
Right now I am stuck from where I can get the original chain code as in V = 000567454....
Or should I try it with a completely different approach? Can anybody give me some help on this please as I am really stuck!
I want to use the chain code for further processing and image recognition with a Fourier transform later on.

Determining time to collision

I am working on an android application that will determine distance from an obstacle infront of the camera. if I calculate the time to collision, I will be able to determine distance. I am able to obtain the optical flow vectors, how do I continue from there to determine time to collision.
this is what I have so far;
mRgba = inputFrame.rgba();
if (mMOP2fptsPrev.rows() == 0) {
//Log.d("Baz", "First time opflow");
// first time through the loop so we need prev and this mats
// plus prev points
// get this mat
Imgproc.cvtColor(mRgba, matOpFlowThis, Imgproc.COLOR_RGBA2GRAY);
// copy that to prev mat
matOpFlowThis.copyTo(matOpFlowPrev);
// get prev corners
Imgproc.goodFeaturesToTrack(matOpFlowPrev, MOPcorners, iGFFTMax, 0.05, 20);
mMOP2fptsPrev.fromArray(MOPcorners.toArray());
// get safe copy of this corners
mMOP2fptsPrev.copyTo(mMOP2fptsSafe);
}
else
{
//Log.d("Baz", "Opflow");
// we've been through before so
// this mat is valid. Copy it to prev mat
matOpFlowThis.copyTo(matOpFlowPrev);
// get this mat
Imgproc.cvtColor(mRgba, matOpFlowThis, Imgproc.COLOR_RGBA2GRAY);
// get the corners for this mat
Imgproc.goodFeaturesToTrack(matOpFlowThis, MOPcorners, iGFFTMax, 0.05, 20);
mMOP2fptsThis.fromArray(MOPcorners.toArray());
// retrieve the corners from the prev mat
// (saves calculating them again)
mMOP2fptsSafe.copyTo(mMOP2fptsPrev);
// and save this corners for next time through
Video.calcOpticalFlowPyrLK(matOpFlowPrev, matOpFlowThis, mMOP2fptsPrev, mMOP2fptsThis, mMOBStatus, mMOFerr);
cornersPrev = mMOP2fptsPrev.toList();
cornersThis = mMOP2fptsThis.toList();
byteStatus = mMOBStatus.toList();
y = byteStatus.size() - 1;
for (x = 0; x < y; x++) {
if (byteStatus.get(x) == 1) {
pt = cornersThis.get(x);
pt2 = cornersPrev.get(x);
Core.circle(mRgba, pt, 5, colorRed, iLineThickness - 1);
}
}

How to extract width and height of contour in javacv?

I am developing project on component identification using javacv package (Opencv ). I used a method to return set of rectangles on the image as "CvSeq" What I need to know is how to do following things
How can I get each rectangle from methods output (from CvSeq)?
How to access the lengths and width of the rectangle ?
This is the method that returns the rectangles
public static CvSeq findSquares( final IplImage src, CvMemStorage storage)
{
CvSeq squares = new CvContour();
squares = cvCreateSeq(0, sizeof(CvContour.class), sizeof(CvSeq.class), storage);
IplImage pyr = null, timg = null, gray = null, tgray;
timg = cvCloneImage(src);
CvSize sz = cvSize(src.width() & -2, src.height() & -2);
tgray = cvCreateImage(sz, src.depth(), 1);
gray = cvCreateImage(sz, src.depth(), 1);
pyr = cvCreateImage(cvSize(sz.width()/2, sz.height()/2), src.depth(), src.nChannels());
// down-scale and upscale the image to filter out the noise
cvPyrDown(timg, pyr, CV_GAUSSIAN_5x5);
cvPyrUp(pyr, timg, CV_GAUSSIAN_5x5);
cvSaveImage("ha.jpg", timg);
CvSeq contours = new CvContour();
// request closing of the application when the image window is closed
// show image on window
// find squares in every color plane of the image
for( int c = 0; c < 3; c++ )
{
IplImage channels[] = {cvCreateImage(sz, 8, 1), cvCreateImage(sz, 8, 1), cvCreateImage(sz, 8, 1)};
channels[c] = cvCreateImage(sz, 8, 1);
if(src.nChannels() > 1){
cvSplit(timg, channels[0], channels[1], channels[2], null);
}else{
tgray = cvCloneImage(timg);
}
tgray = channels[c]; // try several threshold levels
for( int l = 0; l < N; l++ )
{
// hack: use Canny instead of zero threshold level.
// Canny helps to catch squares with gradient shading
if( l == 0 )
{
// apply Canny. Take the upper threshold from slider
// and set the lower to 0 (which forces edges merging)
cvCanny(tgray, gray, 0, thresh, 5);
// dilate canny output to remove potential
// // holes between edge segments
cvDilate(gray, gray, null, 1);
}
else
{
// apply threshold if l!=0:
cvThreshold(tgray, gray, (l+1)*255/N, 255, CV_THRESH_BINARY);
}
// find contours and store them all as a list
cvFindContours(gray, storage, contours, sizeof(CvContour.class), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
CvSeq approx;
// test each contour
while (contours != null && !contours.isNull()) {
if (contours.elem_size() > 0) {
approx = cvApproxPoly(contours, Loader.sizeof(CvContour.class),storage, CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0);
if( approx.total() == 4
&&
Math.abs(cvContourArea(approx, CV_WHOLE_SEQ, 0)) > 1000 &&
cvCheckContourConvexity(approx) != 0
){
double maxCosine = 0;
//
for( int j = 2; j < 5; j++ )
{
// find the maximum cosine of the angle between joint edges
double cosine = Math.abs(angle(new CvPoint(cvGetSeqElem(approx, j%4)), new CvPoint(cvGetSeqElem(approx, j-2)), new CvPoint(cvGetSeqElem(approx, j-1))));
maxCosine = Math.max(maxCosine, cosine);
}
if( maxCosine < 0.2 ){
cvSeqPush(squares, approx);
}
}
}
contours = contours.h_next();
}
contours = new CvContour();
}
}
return squares;
}
This is the sample original image that I used
And this is the image that I got after drawing lines around the matching rectangles
Actually in above images I'm tying to remove those large rectangles and just need to identify other rectangles so I need some code example to understand how to archive above goals. Please be kind enough to share your experience with me. Thanks !
OpenCV finds contours of the white objects in black background. In your case it is reverse, objects are black. And in that way, even image border is also an object. So to avoid that, just reverse image such that background is black.
Below I have demonstrated it (using OpenCV-Python):
import numpy as np
import cv2
im = cv2.imread('sofsqr.png')
img = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(img,127,255,1)
Remember, instead of using seperate function for inverting, I used it in threshold. Just convert the threshold type to BINARY_INV (ie '1').
Now you have an image as below :
Now we find the contours. Then for each contour, we approximate it and check if it is a rectangle by looking at the length of approximated contour, which should be four for a rectangle.
If drawn, you get like this:
And at the same time, we also find the bounding rect of each contour. The bounding rect has a shape like this : [initial point x, initial point y, width of rect, height of rect]
So you get the width and height.
Below is the code:
contours,hierarchy = cv2.findContours(thresh,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
approx = cv2.approxPolyDP(cnt,cv2.arcLength(cnt,True)*0.02,True)
if len(approx)==4:
cv2.drawContours(im,[approx],0,(0,0,255),2)
x,y,w,h = cv2.boundingRect(cnt)
EDIT :
After some comments, I understood that, the real aim of the question is to avoid large rectangles and select only smaller ones.
It can be done using bounding rect values we obtained. ie, Select only those rectangles whose length is less than a threshold value, or breadth or area. As an example, in this image, I took area should be less than 10000.(A rough estimate). If it is less than 10000, it should be selected and we denote it in red color, otherwise, false candidate, represented in blue color ( just for visualization).
for cnt in contours:
approx = cv2.approxPolyDP(cnt,cv2.arcLength(cnt,True)*0.02,True)
if len(approx)==4:
x,y,w,h = cv2.boundingRect(approx)
if w*h < 10000:
cv2.drawContours(im,[approx],0,(0,0,255),-1)
else:
cv2.drawContours(im,[approx],0,(255,0,0),-1)
Below is the output i got :
How to get that threshold value? :
It completely depends on you and your application. Or you can find it by trial and error methods. ( i did so).
Hope that solves your problem. All functions are standard opencv functions. So i think you won't find any problem to convert to JavaCV.
Just noticed that there is a bug in the code provided in the question:
IplImage channels[] = {cvCreateImage(sz, 8, 1), cvCreateImage(sz, 8, 1), cvCreateImage(sz, 8, 1)};
channels[c] = cvCreateImage(sz, 8, 1);
if(src.nChannels() > 1){
cvSplit(timg, channels[0], channels[1], channels[2], null);
}else{
tgray = cvCloneImage(timg);
}
tgray = channels[c];
This means if there is a single channel, tgray will be an empty image.
It should read:
IplImage channels[] = {cvCreateImage(sz, 8, 1), cvCreateImage(sz, 8, 1), cvCreateImage(sz, 8, 1)};
channels[c] = cvCreateImage(sz, 8, 1);
if(src.nChannels() > 1){
cvSplit(timg, channels[0], channels[1], channels[2], null);
tgray = channels[c];
}else{
tgray = cvCloneImage(timg);
}

Categories

Resources