how to extract an object from the camera preview android opencv - java

I am trying to scan a MTG card using OpenCV on android. I have it to where I can detect the edges of the card and even draw an outline around it but am confused on how to extract just the card from the background and then exit from the camera preview. Here is my code so far:
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
Mat result = new Mat();
Mat mask = new Mat();
List<MatOfPoint> contours = new ArrayList<>();
Mat hierarchy = new Mat();
mRgba = inputFrame.rgba();
Imgproc.Canny(mRgba, result, 40, 120);
Imgproc.GaussianBlur(result, result, new Size(9,9), 2, 2);
Imgproc.findContours(result, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE, new Point(0,0));
Imgproc.drawContours(mask, contours, -1, new Scalar(0, 255, 0), 1);
hierarchy.release();
for ( int contourIdx=0; contourIdx < contours.size(); contourIdx++ )
{
// Minimum size allowed for consideration
MatOfPoint2f approxCurve = new MatOfPoint2f();
MatOfPoint2f contour2f = new MatOfPoint2f( contours.get(contourIdx).toArray() );
//Processing on mMOP2f1 which is in type MatOfPoint2f
double approxDistance = Imgproc.arcLength(contour2f, true)*0.02;
Imgproc.approxPolyDP(contour2f, approxCurve, approxDistance, true);
//Convert back to MatOfPoint
MatOfPoint points = new MatOfPoint( approxCurve.toArray() );
// Get bounding rect of contour
Rect rect = Imgproc.boundingRect(points);
Imgproc.rectangle(mRgba, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height), new Scalar(255, 0, 0, 255), 3);
}
Bitmap card = Bitmap.createBitmap(result.cols(), result.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(result, card);
return mRgba;
}
}
Here is an example of what it looks like when run the code. As you can see the card is outlined by a red rectangle now but how do I extract just whats in the rectangle, save it to a mat or bitmap and then exit the camera?
screenshot of outlined card

I didn't test it, but can't you just use...
return new Mat(mRgba, rect);

Related

Opencv detection Java

I will like to detect the hand in the following images. I have tried grabCut algorithm from opencv using the foreground and background extraction. However, there is some inaccuracy in the detection. Could someone help me with it, please. Thank You.
The code is as follow:
public static void DetectionAlg() {
//convert from bitmap to mat
Mat mat = new Mat(myBitmap.getWidth(), myBitmap.getHeight(), CvType.CV_8UC3);
Utils.bitmapToMat(myBitmap, mat);
//covert to bgr
Mat bgrMat = new Mat();
Imgproc.cvtColor(mat, bgrMat, Imgproc.COLOR_BGRA2BGR);
Mat background = new Mat(bgrMat.size(), CvType.CV_8UC3, new Scalar(255, 255, 255));
Mat firstMask = new Mat();
Mat bgModel = new Mat();
Mat fgModel = new Mat();
Mat mask;
Mat source = new Mat(1, 1, CvType.CV_8U, new Scalar(Imgproc.GC_PR_FGD));
Mat dst = new Mat();
//using a user define area
Rect rect = new Rect(GlobalVar.tl, GlobalVar.br);
Imgproc.grabCut(bgrMat, firstMask, rect, bgModel, fgModel, 5, Imgproc.GC_INIT_WITH_RECT);
Core.compare(firstMask, source, firstMask, Core.CMP_EQ);
Mat foreground = new Mat(bgrMat.size(), CvType.CV_8UC3, new Scalar(255, 255, 255));
bgrMat.copyTo(foreground, firstMask);
Scalar color = new Scalar(255, 0, 0, 255);
Imgproc.rectangle(bgrMat, GlobalVar.tl, GlobalVar.br, color);
Mat tmp = new Mat();
Imgproc.resize(background, tmp, bgrMat.size());
background = tmp;
mask = new Mat(foreground.size(), CvType.CV_8UC1, new Scalar(255, 255, 255));
Imgproc.cvtColor(foreground, mask, Imgproc.COLOR_BGR2GRAY);
Imgproc.threshold(mask, mask, 254, 255, Imgproc.THRESH_BINARY_INV);
Mat vals = new Mat(1, 1, CvType.CV_8UC3, new Scalar(0.0));
background.copyTo(dst);
background.setTo(vals, mask);
Core.add(background, foreground, dst, mask);
firstMask.release();
source.release();
bgModel.release();
fgModel.release();
vals.release();
Mat colorMat = new Mat();
Imgproc.cvtColor(dst, colorMat, Imgproc.COLOR_BGR2BGRA);
Bitmap outputBitmap = Bitmap.createBitmap(colorMat.cols(), colorMat.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(colorMat, outputBitmap);
}
Images are in the link:
This is the input image
This is another input image
This is the output using the above method, however, there are some other parts that does not belong to the hand

How to draw a boundingRect with the right rotation angle by using OpenCV?

I am performing Canny edge detector using Android and Opencv on an image to detect the largest contour, extract it using warpPerspective method then find all objects inside that contour. Everything is working as expected but only for image that isn't rotated.
I am using boundingRect to get the contour and use its coordinates to extract it.
Here my code:
private Mat detectLargestContour(Mat origMat) {
// long e1 = Core.getTickCount();
Mat mGray = new Mat();
MatOfDouble mu = new MatOfDouble();
MatOfDouble stddev = new MatOfDouble();
Imgproc.cvtColor(origMat, mGray, Imgproc.COLOR_BGR2GRAY);
Core.meanStdDev(mGray, mu, stddev);
Imgproc.GaussianBlur(mGray, mGray, new Size(5, 5), 5);
//Imgproc.Canny(mGray, mGray, 30, 80, 3, false); //FOR HIGH BRIGHTNESS
//Imgproc.Canny(mGray, mGray, 50, 130, 3, false); // FOR LOW BRIGHTNESS
Imgproc.Canny(mGray, mGray, mu.get(0, 0)[0], stddev.get(0, 0)[0], 3, false);
Mat kernell = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(9,9));
Imgproc.morphologyEx(mGray, mGray, Imgproc.MORPH_CLOSE, kernell);
Imgproc.dilate(mGray, mGray, Imgproc.getStructuringElement(Imgproc.MORPH_CROSS, new Size(3, 3)));
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Mat hierarchy = new Mat();
Imgproc.findContours(mGray, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
//MatOfPoint2f approxCurve = new MatOfPoint2f();
double largest_area=0;
Rect rect = new Rect();
for (int idx = 0; idx < contours.size() ; idx++) {
double a = Imgproc.contourArea(contours.get(idx)); //Find the area of contour
if (a > largest_area) {
largest_area = a;
rect = Imgproc.boundingRect(contours.get(idx));
}
}
if (rect.area() > 100000) {
Imgproc.rectangle(origMat, rect.tl(), rect.br(), new Scalar(0, 255, 0));
p1 = new Point(rect.tl().x, rect.tl().y);
p2 = new Point(rect.tl().x + rect.width, rect.tl().y);
p3 = new Point(rect.tl().x, rect.tl().y + rect.height);
p4 = new Point(rect.tl().x + rect.width, rect.tl().y + rect.height);
card_corners = new ArrayList<>();
card_corners.add(p1);
card_corners.add(p3);
card_corners.add(p4);
card_corners.add(p2);
warpedCard = new Mat(origMat.rows(), origMat.cols(), CvType.CV_8UC3);
final Point p1 = new Point(warpedCard.cols() + marge, warpedCard.rows() + marge);
final Point p2 = new Point(0 - marge, warpedCard.rows() + marge);
final Point p3 = new Point(0 - marge, 0 - marge);
final Point p4 = new Point(warpedCard.cols() + marge, 0 - marge);
LinkedList<Point> sceneList = new LinkedList<Point>();
sceneList.addLast(p4);
sceneList.addLast(p3);
sceneList.addLast(p2);
sceneList.addLast(p1);
MatOfPoint2f scene = new MatOfPoint2f();
scene.fromList(sceneList);
MatOfPoint2f obj = new MatOfPoint2f();
obj.fromList(card_corners);
Mat homography = Calib3d.findHomography(obj, scene);
Imgproc.warpPerspective(origMat, warpedCard, homography, new Size(warpedCard.cols(), warpedCard.rows()));
return warpedCard;
}
return origMat;
}
It's weird but only boundingRect gave me a stable and performant result but the drawn rectangle doesn't rotate with the found contour.
How can I manage to resolve this issue? Any thoughts?
EDIT:
I changed boundingRect with minAreaRect.
Here is the code
int largest_idx = 0;
for (int idx = 0; idx < contours.size() ; idx++) {
double a = Imgproc.contourArea(contours.get(idx)); //Find the area of contour
if (a > largest_area) {
largest_area = a;
// rect = Imgproc.boundingRect(contours.get(idx));
largest_idx = idx;
}
}
MatOfPoint2f new_mat = new MatOfPoint2f( contours.get(largest_idx).toArray() );
RotatedRect rbox = Imgproc.minAreaRect(new_mat);
Log.d("rotatedrect_angle", "" + rbox.angle);
Point points[] = new Point[4];
rbox.points(points);
for(int i=0; i<4; ++i){
Imgproc.line(origMat, points[i], points[(i+1)%4], new Scalar(255,255,255));
}
And here is what I've got:
As you can see the detection isn't as accurate as when I used boundingRect.
A Python demo to find and draw a rotatedRect:
# 2019/03/01
# https://stackoverflow.com/a/54942835/3547485
import numpy as np
import cv2
gray = cv2.imread("tmp.png", cv2.IMREAD_GRAYSCALE)
th, threshed = cv2.threshold(gray, 220, 255, cv2.THRESH_BINARY_INV)
cnts = cv2.findContours(threshed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[-2]
cnt = sorted(cnts, key=cv2.contourArea, reverse=True)[0]
rbox = cv2.minAreaRect(cnt)
pts = cv2.boxPoints(rbox).astype(np.int32)
cv2.drawContours(img, [pts], -1, (0, 255, 0), 1, cv2.LINE_AA)
cv2.imwrite("dst.png", img)
Useful OpenCV functions(in Python) : cv2.minAreaRect, cv2.boxPoints, cv.2drawContours. You can find corresponding functions in Java.

how to find largest contour in java opencv

I have used find contours and boundingrect and display it at my project. then I want to find the largest contours and display it. Is this possible? I am newbie to OpenCV java lang.
heres my code so far:
#Override
public void onCameraViewStarted(int width, int height) {
mRgba = new Mat(height, width, CvType.CV_8UC4);
mHsv = new Mat(height,width,CvType.CV_8UC3);
hierarchy = new Mat();
mHsvMask = new Mat();
mDilated = new Mat();
mEroded = new Mat();
}
#Override
public void onCameraViewStopped() {
mRgba.release();
mHsv.release();
mHsvMask.release();
mDilated.release();
hierarchy.release();
}
#Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
mRgba =inputFrame.rgba();
contours = new ArrayList<MatOfPoint>();
hierarchy =new Mat();
mHsv = new Mat();
mHsvMask =new Mat();
Imgproc.cvtColor(mRgba, mHsv, Imgproc.COLOR_RGB2HSV);
Scalar lowerThreshold = new Scalar ( 0, 0, 0 ); // Blue color – lower hsv values
Scalar upperThreshold = new Scalar ( 179, 255, 10 ); // Blue color – higher hsv values
Core.inRange ( mHsv, lowerThreshold , upperThreshold, mHsvMask );
//just some filter
//Imgproc.dilate ( mHsvMask, mDilated, new Mat() );
//Imgproc.erode(mDilated,mEroded,new Mat());
Imgproc.findContours(mHsvMask, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);
for ( int contourIdx=0; contourIdx < contours.size(); contourIdx++ )
{
//Minimun size allowed for consideration
MatOfPoint2f approxCurve = new MatOfPoint2f();
MatOfPoint2f contour2f = new MatOfPoint2f(contours.get(contourIdx).toArray());
//Processing on mMOP2f1 which is in type MatOfPoint2f
double approxDistance = Imgproc.arcLength(contour2f,true)*0.02;
Imgproc.approxPolyDP(contour2f,approxCurve,approxDistance,true);
//convert to MatofPoint
MatOfPoint point = new MatOfPoint(approxCurve.toArray());
//get boundingrect from contour
Rect rect = Imgproc.boundingRect(point);
Imgproc.rectangle(mRgba,new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height), new Scalar(255, 0, 0, 255),3);
//bisa Imgproc.rectangle(mRgba, rect.tl(), rect.br(), new Scalar(255, 0, 0),1, 8,0);
//show contour kontur
if(Imgproc.contourArea(contours.get(contourIdx))>100) {
Imgproc.drawContours(mRgba, contours, contourIdx, new Scalar(0,255,0), 5);
}
}
return mRgba;
Hopefully, someone has some experience in this. Thanks..
With function Imgproc.contourArea you can just simply find the areas of all of your contours and the contour with the largest area would simply be the largest one.
Code to draw the largest contour would be like this:
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,0), 5);

OpenCV drawContours in Java

I'm trying to draw contours around object in image but i get error
OpenCV Error: Unsupported format or combination of formats ([Start]FindContours supports only CV_8UC1 images when mode != CV_RETR_FLOODFILL otherwise supports CV_32SC1 images only) in cvStartFindContours, file C:\builds\master_PackSlaveAddon-win64-vc12-static\opencv\modules\imgproc\src\contours.cpp, line 198
I tried to convert image but error is still there how to use DrawContour?
Mat imageInMat = Imgcodecs.imread("C:/Users/ja/workspace/imgtomath/bin/imgtomath/lena.png");
if(imageInMat.empty()== true)
{System.out.println("Error no image found!!");}
imageInMat.convertTo(imageInMat, CvType.CV_32SC1);
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Mat hierarchy = new Mat();
Imgproc.findContours(imageInMat, contours, hierarchy, Imgproc.RETR_FLOODFILL, Imgproc.CHAIN_APPROX_SIMPLE);
Imgproc.drawContours(imageInMat, contours, -1, new Scalar(255,0,0));
It should work properly:
Mat image = Imgcodecs.imread("C:/Users/ja/workspace/imgtomath/bin/imgtomath/lena.png");
if(image.empty() == true) {
System.out.println("Error: no image found!");
}
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Mat image32S = new Mat();
image.convertTo(image32S, CvType.CV_32SC1);
Imgproc.findContours(image32S, contours, new Mat(), Imgproc.RETR_FLOODFILL, Imgproc.CHAIN_APPROX_SIMPLE);
// Draw all the contours such that they are filled in.
Mat contourImg = new Mat(image32S.size(), image32S.type());
for (int i = 0; i < contours.size(); i++) {
Imgproc.drawContours(contourImg, contours, i, new Scalar(255, 255, 255), -1);
}
Highgui.imwrite("debug_image.jpg", contourImg); // DEBUG

OpenCV + Android + Vehicle number Plate Recognition

I'm developing a Android app to detect vehicle number plate. i did image processing up to findContours level of image. Now i need to convert following C++ code to Opencv Based Android java.
This is original image
This is after Otsu thresholding image
This is my andoid+opencv code (working 100%)
ImageView imgView = (ImageView) findViewById(R.id.imageView1);
Bitmap bmp = BitmapFactory.decodeResource(getResources(),car);
//First convert Bitmap to Mat
Mat ImageMatin = new Mat ( bmp.getHeight(), bmp.getWidth(), CvType.CV_8U, new Scalar(4));
Mat ImageMatout = new Mat ( bmp.getHeight(), bmp.getWidth(), CvType.CV_8U, new Scalar(4));
Mat ImageMatBk = new Mat ( bmp.getHeight(), bmp.getWidth(), CvType.CV_8U, new Scalar(4));
Mat ImageMatTopHat = new Mat ( bmp.getHeight(), bmp.getWidth(), CvType.CV_8U, new Scalar(4));
Mat temp = new Mat ( bmp.getHeight(), bmp.getWidth(), CvType.CV_8U, new Scalar(4));
Bitmap myBitmap32 = bmp.copy(Bitmap.Config.ARGB_8888, true);
Utils.bitmapToMat(myBitmap32, ImageMatin);
//Converting RGB to Gray.
Imgproc.cvtColor(ImageMatin, ImageMatBk, Imgproc.COLOR_RGB2GRAY,8);
Imgproc.dilate(ImageMatBk, temp, Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(9, 9)));
Imgproc.erode(temp, ImageMatTopHat, Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(9,9)));
//Core.absdiff(current, previous, difference);
Core.absdiff(ImageMatTopHat, ImageMatBk, ImageMatout);
//Sobel operator in horizontal direction.
Imgproc.Sobel(ImageMatout,ImageMatout,CvType.CV_8U,1,0,3,1,0.4,Imgproc.BORDER_DEFAULT);
//Converting GaussianBlur
Imgproc.GaussianBlur(ImageMatout, ImageMatout, new Size(5,5),2);
Imgproc.dilate(ImageMatout, ImageMatout, Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(3,3)));
Mat element = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(17, 3));
Imgproc.morphologyEx(ImageMatout, ImageMatout, Imgproc.MORPH_CLOSE, element);
//threshold image
Imgproc.threshold(ImageMatout, ImageMatout, 0, 255, Imgproc.THRESH_OTSU+Imgproc.THRESH_BINARY);
Now I need to extract number Plate
Please help me to convert following C++ code to java+opencv:.
std::vector rects;
std::vector<std::vector >::iterator itc = contours.begin();
while (itc != contours.end())
{
cv::RotatedRect mr = cv::minAreaRect(cv::Mat(*itc));
float area = fabs(cv::contourArea(*itc));
float bbArea=mr.size.width * mr.size.height;
float ratio = area/bbArea;
if( (ratio < 0.45) || (bbArea < 400) ){
itc= contours.erase(itc);
}else{
++itc;
rects.push_back(mr);
}
}
Looking at http://docs.opencv.org/java and the documentation for findContours in particular
instead of
std::vector<std::vector<cv::Point> > contours;
you will have
java.util.ArrayList<MatOfPoint> contours;
You can use contours.listIterator() to traverse the list. Something like the below (not compiled let alone run,likely to contain major blunders):
import java.util.*;
import org.opencv.imgproc.Imgproc;
import org.opencv.core.*;
/* ... */
ArrayList<RotatedRect> rects = new ArrayList<RotatedRect>()
ArrayList<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Imgproc.findContours(image, contours, new Mat(), Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_NONE);
ListIterator<MatOfPoint> itc = contours.listIterator();
while(itc.hasNext())
{
MatOfPoint2f mp2f = new MatOfPoint2f(itc.next().toArray());
RotatedRect mr = Imgproc.minAreaRect(mp2f);
double area = Math.abs(Imgproc.contourArea(mp2f));
double bbArea= mr.size.area();
double ratio = area / bbArea;
if( (ratio < 0.45) || (bbArea < 400) )
{
itc.remove(); // other than deliberately making the program slow,
// does erasing the contour have any purpose?
}
else
{
rects.add(mr);
}
}

Categories