I am trying to use this code http://androiderstuffs.blogspot.com/2016/06/detecting-rectangle-using-opencv-java.html to detect card. But instead of putting card on plane surface, I will be holding this card in hand in-front of my Head. Problem is, its not detecting card rectangle. I am new to OpenCV. See my code below, this code will highlight all found rectangles in output image. Problem is, it never find card rectangle.
private void findRectangleOpen(Bitmap image) throws Exception {
Mat tempor = new Mat();
Mat src = new Mat();
Utils.bitmapToMat(image, tempor);
Imgproc.cvtColor(tempor, src, Imgproc.COLOR_BGR2RGB);
Mat blurred = src.clone();
Imgproc.medianBlur(src, blurred, 9);
Mat gray0 = new Mat(blurred.size(), CvType.CV_8U), gray = new Mat();
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
List<Mat> blurredChannel = new ArrayList<Mat>();
blurredChannel.add(blurred);
List<Mat> gray0Channel = new ArrayList<Mat>();
gray0Channel.add(gray0);
MatOfPoint2f approxCurve;
int maxId = -1;
for (int c = 0; c < 3; c++) {
int ch[] = {c, 0};
Core.mixChannels(blurredChannel, gray0Channel, new MatOfInt(ch));
int thresholdLevel = 1;
for (int t = 0; t < thresholdLevel; t++) {
if (t == 0) {
Imgproc.Canny(gray0, gray, 10, 20, 3, true); // true ?
Imgproc.dilate(gray, gray, new Mat(), new Point(-1, -1), 1); // 1
// ?
} else {
Imgproc.adaptiveThreshold(gray0, gray, thresholdLevel,
Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C,
Imgproc.THRESH_BINARY,
(src.width() + src.height()) / 200, t);
}
Imgproc.findContours(gray, contours, new Mat(),
Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
int i = 0;
for (MatOfPoint contour : contours) {
MatOfPoint2f temp = new MatOfPoint2f(contour.toArray());
double area = Imgproc.contourArea(contour);
approxCurve = new MatOfPoint2f();
Imgproc.approxPolyDP(temp, approxCurve,
Imgproc.arcLength(temp, true) * 0.02, true);
if (approxCurve.total() == 4 && area >= 200 && area <= 40000) {
double maxCosine = 0;
List<Point> curves = approxCurve.toList();
for (int j = 2; j < 5; j++) {
double cosine = Math.abs(angle(curves.get(j % 4),
curves.get(j - 2), curves.get(j - 1)));
maxCosine = Math.max(maxCosine, cosine);
}
if (maxCosine < 0.3) {
Imgproc.drawContours(src, contours, i, new Scalar(255, 0, 0), 3);
Bitmap bmp;
bmp = Bitmap.createBitmap(src.cols(), src.rows(),
Bitmap.Config.ARGB_8888);
Utils.matToBitmap(src, bmp);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
//File origFile = getFileForSaving();
savePhoto(byteArray);
bmp.recycle();
}
}
i++;
}
}
}
}
private static double angle(org.opencv.core.Point p1, org.opencv.core.Point p2, org.opencv.core.Point p0) {
double dx1 = p1.x - p0.x;
double dy1 = p1.y - p0.y;
double dx2 = p2.x - p0.x;
double dy2 = p2.y - p0.y;
return (dx1 * dx2 + dy1 * dy2)
/ sqrt((dx1 * dx1 + dy1 * dy1) * (dx2 * dx2 + dy2 * dy2)
+ 1e-10);
}
Sample output image is:
Output of detecting rectangle
I'm OpenCV learner. I was trying Image Comparison. I have used OpenCV 2.4.13.3
I have these two images 1.jpg and cam1.jpg.
When I use the following command in openCV
File sdCard = Environment.getExternalStorageDirectory();
String path1, path2;
path1 = sdCard.getAbsolutePath() + "/1.jpg";
path2 = sdCard.getAbsolutePath() + "/cam1.jpg";
FeatureDetector detector = FeatureDetector.create(FeatureDetector.ORB);
DescriptorExtractor extractor = DescriptorExtractor.create(DescriptorExtractor.BRIEF);
DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMING);
Mat img1 = Highgui.imread(path1);
Mat img2 = Highgui.imread(path2);
Mat descriptors1 = new Mat();
MatOfKeyPoint keypoints1 = new MatOfKeyPoint();
detector.detect(img1, keypoints1);
extractor.compute(img1, keypoints1, descriptors1);
//second image
// Mat img2 = Imgcodecs.imread(path2);
Mat descriptors2 = new Mat();
MatOfKeyPoint keypoints2 = new MatOfKeyPoint();
detector.detect(img2, keypoints2);
extractor.compute(img2, keypoints2, descriptors2);
//matcher image descriptors
MatOfDMatch matches = new MatOfDMatch();
matcher.match(descriptors1,descriptors2,matches);
// Filter matches by distance
MatOfDMatch filtered = filterMatchesByDistance(matches);
int total = (int) matches.size().height;
int Match= (int) filtered.size().height;
Log.d("LOG", "total:" + total + " Match:"+Match);
Method filterMatchesByDistance
static MatOfDMatch filterMatchesByDistance(MatOfDMatch matches){
List<DMatch> matches_original = matches.toList();
List<DMatch> matches_filtered = new ArrayList<DMatch>();
int DIST_LIMIT = 30;
// Check all the matches distance and if it passes add to list of filtered matches
Log.d("DISTFILTER", "ORG SIZE:" + matches_original.size() + "");
for (int i = 0; i < matches_original.size(); i++) {
DMatch d = matches_original.get(i);
if (Math.abs(d.distance) <= DIST_LIMIT) {
matches_filtered.add(d);
}
}
Log.d("DISTFILTER", "FIL SIZE:" + matches_filtered.size() + "");
MatOfDMatch mat = new MatOfDMatch();
mat.fromList(matches_filtered);
return mat;
}
Log
total:122 Match:30
As we can see from the log match is 30.
But as we can see both images have same visual element (in).
How can I get match=90 using openCV?
It would be great if somebody can help with code snippet.
If using opencv it is not possible then what are the other
alternatives we can look for?
But as we can see both images have same visual element (in).
So, we should compare not whole images, but "same visual element" on it. You can improve Match value more if you do not compare the "template" and "camera" images themselves, but processed same way (converted to binary black/white, for example) "template" and "camera" images. For example, try to find blue (background of template logo) square on both ("template" and "camera") images and compare that squares (Region Of Interest). The code may be something like that:
Bitmap bmImageTemplate = <get your template image Bitmap>;
Bitmap bmTemplate = findLogo(bmImageTemplate); // process template image
Bitmap bmImage = <get your camera image Bitmap>;
Bitmap bmLogo = findLogo(bmImage); // process camera image same way
compareBitmaps(bmTemplate, bmLogo);
where
private Bitmap findLogo(Bitmap sourceBitmap) {
Bitmap roiBitmap = null;
Mat sourceMat = new Mat(sourceBitmap.getWidth(), sourceBitmap.getHeight(), CvType.CV_8UC3);
Utils.bitmapToMat(sourceBitmap, sourceMat);
Mat roiTmp = sourceMat.clone();
final Mat hsvMat = new Mat();
sourceMat.copyTo(hsvMat);
// convert mat to HSV format for Core.inRange()
Imgproc.cvtColor(hsvMat, hsvMat, Imgproc.COLOR_RGB2HSV);
Scalar lowerb = new Scalar(85, 50, 40); // lower color border for BLUE
Scalar upperb = new Scalar(135, 255, 255); // upper color border for BLUE
Core.inRange(hsvMat, lowerb, upperb, roiTmp); // select only blue pixels
// find contours
List<MatOfPoint> contours = new ArrayList<>();
List<Rect> squares = new ArrayList<>();
Imgproc.findContours(roiTmp, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
// find appropriate bounding rectangles
for (MatOfPoint contour : contours) {
MatOfPoint2f areaPoints = new MatOfPoint2f(contour.toArray());
RotatedRect boundingRect = Imgproc.minAreaRect(areaPoints);
double rectangleArea = boundingRect.size.area();
// test min ROI area in pixels
if (rectangleArea > 400) {
Point rotated_rect_points[] = new Point[4];
boundingRect.points(rotated_rect_points);
Rect rect = Imgproc.boundingRect(new MatOfPoint(rotated_rect_points));
double aspectRatio = rect.width > rect.height ?
(double) rect.height / (double) rect.width : (double) rect.width / (double) rect.height;
if (aspectRatio >= 0.9) {
squares.add(rect);
}
}
}
Mat logoMat = extractSquareMat(roiTmp, getBiggestSquare(squares));
roiBitmap = Bitmap.createBitmap(logoMat.cols(), logoMat.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(logoMat, roiBitmap);
return roiBitmap;
}
method extractSquareMat() just extract Region of Interest (logo) from whole image
public static Mat extractSquareMat(Mat sourceMat, Rect rect) {
Mat squareMat = null;
int padding = 50;
if (rect != null) {
Rect truncatedRect = new Rect((int) rect.tl().x + padding, (int) rect.tl().y + padding,
rect.width - 2 * padding, rect.height - 2 * padding);
squareMat = new Mat(sourceMat, truncatedRect);
}
return squareMat ;
}
and compareBitmaps() just wrapper for your code:
private void compareBitmaps(Bitmap bitmap1, Bitmap bitmap2) {
Mat mat1 = new Mat(bitmap1.getWidth(), bitmap1.getHeight(), CvType.CV_8UC3);
Utils.bitmapToMat(bitmap1, mat1);
Mat mat2 = new Mat(bitmap2.getWidth(), bitmap2.getHeight(), CvType.CV_8UC3);
Utils.bitmapToMat(bitmap2, mat2);
compareMats(mat1, mat2);
}
your code as method:
private void compareMats(Mat img1, Mat img2) {
FeatureDetector detector = FeatureDetector.create(FeatureDetector.ORB);
DescriptorExtractor extractor = DescriptorExtractor.create(DescriptorExtractor.BRIEF);
DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMING);
Mat descriptors1 = new Mat();
MatOfKeyPoint keypoints1 = new MatOfKeyPoint();
detector.detect(img1, keypoints1);
extractor.compute(img1, keypoints1, descriptors1);
//second image
// Mat img2 = Imgcodecs.imread(path2);
Mat descriptors2 = new Mat();
MatOfKeyPoint keypoints2 = new MatOfKeyPoint();
detector.detect(img2, keypoints2);
extractor.compute(img2, keypoints2, descriptors2);
//matcher image descriptors
MatOfDMatch matches = new MatOfDMatch();
matcher.match(descriptors1,descriptors2,matches);
// Filter matches by distance
MatOfDMatch filtered = filterMatchesByDistance(matches);
int total = (int) matches.size().height;
int Match= (int) filtered.size().height;
Log.d("LOG", "total:" + total + " Match:" + Match);
}
static MatOfDMatch filterMatchesByDistance(MatOfDMatch matches){
List<DMatch> matches_original = matches.toList();
List<DMatch> matches_filtered = new ArrayList<DMatch>();
int DIST_LIMIT = 30;
// Check all the matches distance and if it passes add to list of filtered matches
Log.d("DISTFILTER", "ORG SIZE:" + matches_original.size() + "");
for (int i = 0; i < matches_original.size(); i++) {
DMatch d = matches_original.get(i);
if (Math.abs(d.distance) <= DIST_LIMIT) {
matches_filtered.add(d);
}
}
Log.d("DISTFILTER", "FIL SIZE:" + matches_filtered.size() + "");
MatOfDMatch mat = new MatOfDMatch();
mat.fromList(matches_filtered);
return mat;
}
As result for resized (scaled for 50%) images saved from your question result is:
D/DISTFILTER: ORG SIZE:237
D/DISTFILTER: FIL SIZE:230
D/LOG: total:237 Match:230
NB! This is a quick and dirty example just to demonstrate approach only for given template.
P.S. getBiggestSquare() can be like that (based on compare by area):
public static Rect getBiggestSquare(List<Rect> squares) {
Rect biggestSquare = null;
if (squares != null && squares.size() >= 1) {
Rect square;
double maxArea;
int ixMaxArea = 0;
square = squares.get(ixMaxArea);
maxArea = square.area();
for (int ix = 1; ix < squares.size(); ix++) {
square = squares.get(ix);
if (square.area() > maxArea) {
maxArea = square.area();
ixMaxArea = ix;
}
}
biggestSquare = squares.get(ixMaxArea);
}
return biggestSquare;
}
I am trying to implement Paper detection through OpenCV. I am able to understand the concept of how can I get it,
Input-> Canny-> Blur-> Find Conture-> Search (closed)Quadrilateral-> Draw Conture
but still, I am new to OpenCV programming. So having issues in implementing it. I was able to find help through this answer
Android OpenCV Paper Sheet detection
but it's drawing contour on every possible lining. Here is the code I am trying to implement.
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
mRgba = inputFrame.rgba();
Imgproc.drawContours(mRgba,findContours(mRgba), 0, new Scalar(0 , 255, 0), 5);
return mRgba;
}
public static class Quadrilateral {
public MatOfPoint contour;
public Point[] points;
public Quadrilateral(MatOfPoint contour, Point[] points) {
this.contour = contour;
this.points = points;
}
}
public static Quadrilateral findDocument( Mat inputRgba ) {
ArrayList<MatOfPoint> contours = findContours(inputRgba);
Quadrilateral quad = getQuadrilateral(contours);
return quad;
}
private static ArrayList<MatOfPoint> findContours(Mat src) {
double ratio = src.size().height / 500;
int height = Double.valueOf(src.size().height / ratio).intValue();
int width = Double.valueOf(src.size().width / ratio).intValue();
Size size = new Size(width,height);
Mat resizedImage = new Mat(size, CvType.CV_8UC4);
Mat grayImage = new Mat(size, CvType.CV_8UC4);
Mat cannedImage = new Mat(size, CvType.CV_8UC1);
Imgproc.resize(src,resizedImage,size);
Imgproc.cvtColor(resizedImage, grayImage, Imgproc.COLOR_RGBA2GRAY, 4);
Imgproc.GaussianBlur(grayImage, grayImage, new Size(5, 5), 0);
Imgproc.Canny(grayImage, cannedImage, 75, 200);
ArrayList<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Mat hierarchy = new Mat();
Imgproc.findContours(cannedImage, contours, hierarchy, Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
hierarchy.release();
Collections.sort(contours, new Comparator<MatOfPoint>() {
#Override
public int compare(MatOfPoint lhs, MatOfPoint rhs) {
return Double.valueOf(Imgproc.contourArea(rhs)).compareTo(Imgproc.contourArea(lhs));
}
});
resizedImage.release();
grayImage.release();
cannedImage.release();
return contours;
}
private static Quadrilateral getQuadrilateral(ArrayList<MatOfPoint> contours) {
for ( MatOfPoint c: contours ) {
MatOfPoint2f c2f = new MatOfPoint2f(c.toArray());
double peri = Imgproc.arcLength(c2f, true);
MatOfPoint2f approx = new MatOfPoint2f();
Imgproc.approxPolyDP(c2f, approx, 0.02 * peri, true);
Point[] points = approx.toArray();
// select biggest 4 angles polygon
if (points.length == 4) {
Point[] foundPoints = sortPoints(points);
return new Quadrilateral(c, foundPoints);
}
}
return null;
}
private static Point[] sortPoints(Point[] src) {
ArrayList<Point> srcPoints = new ArrayList<>(Arrays.asList(src));
Point[] result = { null , null , null , null };
Comparator<Point> sumComparator = new Comparator<Point>() {
#Override
public int compare(Point lhs, Point rhs) {
return Double.valueOf(lhs.y + lhs.x).compareTo(rhs.y + rhs.x);
}
};
Comparator<Point> diffComparator = new Comparator<Point>() {
#Override
public int compare(Point lhs, Point rhs) {
return Double.valueOf(lhs.y - lhs.x).compareTo(rhs.y - rhs.x);
}
};
// top-left corner = minimal sum
result[0] = Collections.min(srcPoints, sumComparator);
// bottom-right corner = maximal sum
result[2] = Collections.max(srcPoints, sumComparator);
// top-right corner = minimal diference
result[1] = Collections.min(srcPoints, diffComparator);
// bottom-left corner = maximal diference
result[3] = Collections.max(srcPoints, diffComparator);
return result;
}
The answer suggests that I should use Quadrilateral Object and call it with Imgproc.drawContours(), but this function takes in ArrayList as argument where as Quadrilateral object contains MatofPoint and Point[]. Can someone help me through this..I am using OpenCV(3.3) and Android (1.5.1)?
Here is the sample what it should look like
This is the exception im getting
CvException [org.opencv.core.CvException: cv::Exception: /hdd2/buildbot/slaves/slave_ardbeg1/50-SDK/opencv/modules/imgproc/src/hough.cpp:712: error: (-5) The source image must be 8-bit, single-channel in function CvSeq* cvHoughLines2(CvArr*, void*, int, double, double, int, double, double)
mat = new Mat();
edges = new Mat();
lines = new Mat();
mRgba = new Mat(612, 816, CvType.CV_8UC1);
Utils.bitmapToMat(bitmap, mat);
Imgproc.Canny(mat, edges, 50, 90);
int threshold = 50;
int minLineSize = 20;
int lineGap = 20;
try {
Imgproc.HoughLines(mat, lines, 1, Math.PI / 180, threshold, minLineSize, lineGap);
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;
} catch (Exception e) {
e.printStackTrace();
System.out.println("e = " + e);
}
Your image in function HoughLines isn't right. You're not formatting it right before putting it into the function.
Try prepare image like this:
https://stackoverflow.com/a/7975315/5577679
I can detect largest contour the answer sheet (20 questions, each have 4 alternative)
After the draw largest contour, what shall I do? Divide matris the rectangle by 20x4 cell? Or find countour again but this time inside the rectangle? I dont know what I need. Just I want to get which is marked.
I looked at this documant.
How to codding "image gridding and division"?
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
return findLargestRectangle(inputFrame.rgba());
}
private Mat findLargestRectangle(Mat original_image) {
Mat imgSource = original_image;
hierarchy = new Mat();
//convert the image to black and white
Imgproc.cvtColor(imgSource, imgSource, Imgproc.COLOR_BGR2GRAY);
//convert the image to black and white does (8 bit)
Imgproc.Canny(imgSource, imgSource, 50, 50);
//apply gaussian blur to smoothen lines of dots
Imgproc.GaussianBlur(imgSource, imgSource, new Size(5, 5), 5);
//find the contours
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Imgproc.findContours(imgSource, contours, hierarchy, Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
hierarchy.release();
double maxArea = -1;
int maxAreaIdx = -1;
MatOfPoint temp_contour = contours.get(0); //the largest is at the index 0 for starting point
MatOfPoint2f approxCurve = new MatOfPoint2f();
Mat largest_contour = contours.get(0);
List<MatOfPoint> largest_contours = new ArrayList<MatOfPoint>();
for (int idx = 0; idx < contours.size(); idx++) {
temp_contour = contours.get(idx);
double contourarea = Imgproc.contourArea(temp_contour);
//compare this contour to the previous largest contour found
if (contourarea > maxArea) {
//check if this contour is a square
MatOfPoint2f new_mat = new MatOfPoint2f( temp_contour.toArray() );
int contourSize = (int)temp_contour.total();
Imgproc.approxPolyDP(new_mat, approxCurve, contourSize*0.05, true);
if (approxCurve.total() == 4) {
maxArea = contourarea;
maxAreaIdx = idx;
largest_contours.add(temp_contour);
largest_contour = temp_contour;
}
}
}
MatOfPoint temp_largest = largest_contours.get(largest_contours.size()-1);
largest_contours = new ArrayList<MatOfPoint>();
largest_contours.add(temp_largest);
Imgproc.cvtColor(imgSource, imgSource, Imgproc.COLOR_BayerBG2RGB);
Imgproc.drawContours(imgSource, contours, maxAreaIdx, new Scalar(0, 255, 0), 1);
Log.d(TAG, "Largers Contour:" + contours.get(maxAreaIdx).toString());
return imgSource;
}
UPDATE 1:
I want to thank you #sturkmen for the his answer. I can read and find black regions now. Here the Android codes:
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View _view = inflater.inflate(R.layout.fragment_main, container, false);
// Inflate the layout for this fragment
Button btnTest = (Button) _view.findViewById(R.id.btnTest);
btnTest.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Mat img = Imgcodecs.imread(mediaStorageDir().getPath() + "/" + "test2.jpg");
if (img.empty()) {
Log.d("Fragment", "IMG EMPTY");
}
Mat gray = new Mat();
Mat thresh = new Mat();
//convert the image to black and white
Imgproc.cvtColor(img, gray, Imgproc.COLOR_BGR2GRAY);
//convert the image to black and white does (8 bit)
Imgproc.threshold(gray, thresh, 0, 255, Imgproc.THRESH_BINARY_INV + Imgproc.THRESH_OTSU);
Mat temp = thresh.clone();
//find the contours
Mat hierarchy = new Mat();
Mat corners = new Mat(4,1, CvType.CV_32FC2);
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Imgproc.findContours(temp, contours,hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
hierarchy.release();
for (int idx = 0; idx < contours.size(); idx++)
{
MatOfPoint contour = contours.get(idx);
MatOfPoint2f contour_points = new MatOfPoint2f(contour.toArray());
RotatedRect minRect = Imgproc.minAreaRect( contour_points );
Point[] rect_points = new Point[4];
minRect.points( rect_points );
if(minRect.size.height > img.width() / 2)
{
List<Point> srcPoints = new ArrayList<Point>(4);
srcPoints.add(rect_points[2]);
srcPoints.add(rect_points[3]);
srcPoints.add(rect_points[0]);
srcPoints.add(rect_points[1]);
corners = Converters.vector_Point_to_Mat(
srcPoints, CvType.CV_32F);
}
}
Imgproc.erode(thresh, thresh, new Mat(), new Point(-1,-1), 10);
Imgproc.dilate(thresh, thresh, new Mat(), new Point(-1,-1), 5);
Mat results = new Mat(1000,250,CvType.CV_8UC3);
Mat quad = new Mat(1000,250,CvType.CV_8UC1);
List<Point> dstPoints = new ArrayList<Point>(4);
dstPoints.add(new Point(0, 0));
dstPoints.add(new Point(1000, 0));
dstPoints.add(new Point(1000, 250));
dstPoints.add(new Point(0, 250));
Mat quad_pts = Converters.vector_Point_to_Mat(
dstPoints, CvType.CV_32F);
Mat transmtx = Imgproc.getPerspectiveTransform(corners, quad_pts);
Imgproc.warpPerspective( img, results, transmtx, new Size(1000,250));
Imgproc.warpPerspective( thresh, quad, transmtx, new Size(1000,250));
Imgproc.resize(quad,quad,new Size(20,5));
Imgcodecs.imwrite("results.png",quad);
//show image
showImage(quad);
//store image
storeImage(quad);
}
});
return _view;
}
public void showImage (Mat img) {
ImageView imgView = (ImageView) getActivity().findViewById(R.id.sampleImageView);
//Mat mRgba = new Mat();
//mRgba = Utils.loadResource(MainAct.this, R.drawable.your_image,Highgui.CV_LOAD_IMAGE_COLOR);
Bitmap img2 = Bitmap.createBitmap(img.cols(), img.rows(),Bitmap.Config.ARGB_8888);
Utils.matToBitmap(img, img2);
imgView.setImageBitmap(img2);
}
public File mediaStorageDir () {
File _mediaStorageDir = new File(Environment.getExternalStorageDirectory()
+ "/Android/data/"
+ getActivity().getApplicationContext().getPackageName());
return _mediaStorageDir;
}
public void storeImage(Mat matImg) {
Bitmap bitmapImg = Bitmap.createBitmap(matImg.cols(), matImg.rows(),Bitmap.Config.ARGB_8888);
Utils.matToBitmap(matImg, bitmapImg);
String timeStamp = new SimpleDateFormat("ddMMyyyy_HHmm").format(new Date());
File mediaFile;
String mImageName="IMG_"+ timeStamp +".jpg";
mediaFile = new File(mediaStorageDir().getPath() + File.separator + mImageName);
File pictureFile = mediaFile;
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
bitmapImg.compress(Bitmap.CompressFormat.PNG, 90, fos);
fos.close();
} catch (FileNotFoundException e) {
Log.d("FragmentMain", "File not found: " + e.getMessage());
} catch (IOException e) {
Log.d("FragmentMain", "Error accessing file: " + e.getMessage());
}
}
here is my trial code as a sample.
i hope it will be helpful. ( i will add some explanation about the code later)
Test Image ( edited your image. having an empty and invalid double mark )
(source: opencv.org)
Result Image
(source: opencv.org)
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
using namespace cv;
using namespace std;
int main( int argc, const char** argv )
{
Mat img = imread(argv[1]);
if(img.empty())
{
return -1;
}
Size dims(20,5); // this variable should be changed according input
Mat gray,thresh;
cvtColor(img, gray, COLOR_BGR2GRAY);
threshold(gray, thresh, 0, 255, THRESH_BINARY_INV + THRESH_OTSU);
Mat quad(img.size(), CV_8UC1); // should be improved
Mat results(img.size(), CV_8UC3);
vector<Point2f> quad_pts;
quad_pts.push_back(cv::Point2f(0, 0));
quad_pts.push_back(cv::Point2f(quad.cols, 0));
quad_pts.push_back(cv::Point2f(quad.cols, quad.rows));
quad_pts.push_back(cv::Point2f(0, quad.rows));
vector<Point2f> corners;
vector<vector<Point> > contours;
findContours(thresh.clone(), contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
for( size_t i = 0; i< contours.size(); i++ )
{
RotatedRect minRect = minAreaRect( Mat(contours[i]) );
// rotated rectangle
Point2f rect_points[4];
minRect.points( rect_points );
if(Rect(minRect.boundingRect()).width > img.cols / 2) // should be improved
for( int j = 0; j < 4; j++ )
{
Point2f pt = quad_pts[j];
Point2f nearest_pt = rect_points[0];
float dist = norm( pt - nearest_pt );
for( int k = 1; k < 4; k++ )
{
if( norm( pt - rect_points[k] ) < dist )
{
dist = norm( pt - rect_points[k] );
nearest_pt = rect_points[k];
}
}
corners.push_back( nearest_pt );
}
}
erode(thresh,thresh,Mat(),Point(-1,-1), 10); // should be improved
dilate(thresh,thresh,Mat(),Point(-1,-1), 5); // should be improved
Mat transmtx = getPerspectiveTransform(corners, quad_pts);
warpPerspective( img, results, transmtx, img.size()); // Create a Mat To Show results
warpPerspective( thresh, quad, transmtx, img.size());
resize(quad,quad,dims);
for(int i = 0; i < quad.cols; i++)
{
String answer = "";
answer += quad.at<uchar>(1,i) == 0 ? "" : "A";
answer += quad.at<uchar>(2,i) == 0 ? "" : "B";
answer += quad.at<uchar>(3,i) == 0 ? "" : "C";
answer += quad.at<uchar>(4,i) == 0 ? "" : "D";
if( answer.length() > 1 ) answer = "X"; // Double mark
int y = 0;
if( answer == "A" ) y = results.rows / dims.height;
if( answer == "B" ) y = results.rows / dims.height *2;
if( answer == "C" ) y = results.rows / dims.height *3;
if( answer == "D" ) y = results.rows / dims.height *4;
if( answer == "" ) answer = "[-]";
putText( results, answer, Point( 50* i + 15, 30 + y), FONT_HERSHEY_PLAIN, 2, Scalar(0,0,255),2);
}
imshow( "results", results );
waitKey(0);
return 0;
}
as a challenge to myself i tried to implement main part in JAVA ( a newcomer copy paste code )
Mat img = Imgcodecs.imread("test.jpg");
Mat gray = new Mat();
Mat thresh = new Mat();
//convert the image to black and white
Imgproc.cvtColor(img, gray, Imgproc.COLOR_BGR2GRAY);
//convert the image to black and white does (8 bit)
Imgproc.threshold(gray, thresh, 0, 255, Imgproc.THRESH_BINARY_INV + Imgproc.THRESH_OTSU);
Mat temp = thresh.clone();
//find the contours
Mat hierarchy = new Mat();
Mat corners = new Mat(4,1,CvType.CV_32FC2);
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Imgproc.findContours(temp, contours,hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
hierarchy.release();
for (int idx = 0; idx < contours.size(); idx++)
{
MatOfPoint contour = contours.get(idx);
MatOfPoint2f contour_points = new MatOfPoint2f(contour.toArray());
RotatedRect minRect = Imgproc.minAreaRect( contour_points );
Point[] rect_points = new Point[4];
minRect.points( rect_points );
if(minRect.size.height > img.width() / 2)
{
List<Point> srcPoints = new ArrayList<Point>(4);
srcPoints.add(rect_points[2]);
srcPoints.add(rect_points[3]);
srcPoints.add(rect_points[0]);
srcPoints.add(rect_points[1]);
corners = Converters.vector_Point_to_Mat(
srcPoints, CvType.CV_32F);
}
}
Imgproc.erode(thresh, thresh, new Mat(), new Point(-1,-1), 10);
Imgproc.dilate(thresh, thresh, new Mat(), new Point(-1,-1), 5);
Mat results = new Mat(1000,250,CvType.CV_8UC3);
Mat quad = new Mat(1000,250,CvType.CV_8UC1);
List<Point> dstPoints = new ArrayList<Point>(4);
dstPoints.add(new Point(0, 0));
dstPoints.add(new Point(1000, 0));
dstPoints.add(new Point(1000, 250));
dstPoints.add(new Point(0, 250));
Mat quad_pts = Converters.vector_Point_to_Mat(
dstPoints, CvType.CV_32F);
Mat transmtx = Imgproc.getPerspectiveTransform(corners, quad_pts);
Imgproc.warpPerspective( img, results, transmtx, new Size(1000,250));
Imgproc.warpPerspective( thresh, quad, transmtx, new Size(1000,250));
Imgproc.resize(quad,quad,new Size(20,5));
Imgcodecs.imwrite("results.png",quad);
here is the (20x5px) result image :
I imrove #sturkmen' s code.
fragment_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="{your package name}.FragmentMain">
<!-- TODO: Update blank fragment layout -->
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="#+id/btnTest"
android:layout_width="match_parent"
android:layout_height="80dp"
android:text="Test" />
<ImageView
android:id="#+id/sampleImageView"
android:layout_width="match_parent"
android:layout_height="150dp"
android:layout_centerHorizontal="true"/>
</LinearLayout>
</framelayout>
AndroidManifest.xml
Add this line for write permission.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
FragmentMain.java
IMAGE FILE: Add Internal Storage/Android/Data/Your Package Folder/test.JPG
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View _view = inflater.inflate(R.layout.fragment_main, container, false);
Button btnTest = (Button) _view.findViewById(R.id.btnTest);
btnTest.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Mat img = Imgcodecs.imread(mediaStorageDir().getPath() + "/" + "test.JPG");
if (img.empty()) {
Log.d("FragmentMain", "Empty Image");
}
Size dims = new Size (20,5);
Mat gray = new Mat();
Mat thresh = new Mat();
//convert the image to black and white
Imgproc.cvtColor(img, gray, Imgproc.COLOR_BGR2GRAY);
storeImage(gray);
//convert the image to black and white does (8 bit)
Imgproc.threshold(gray, thresh, 0, 255, Imgproc.THRESH_BINARY_INV + Imgproc.THRESH_OTSU);
storeImage(thresh);
Mat temp = thresh.clone();
//find the contours
Mat hierarchy = new Mat();
Mat corners = new Mat(4,1, CvType.CV_32FC2);
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Imgproc.findContours(temp, contours,hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
hierarchy.release();
for (int idx = 0; idx < contours.size(); idx++)
{
MatOfPoint contour = contours.get(idx);
MatOfPoint2f contour_points = new MatOfPoint2f(contour.toArray());
RotatedRect minRect = Imgproc.minAreaRect( contour_points );
Point[] rect_points = new Point[4];
minRect.points( rect_points );
if(minRect.size.height > img.width() / 2)
{
List<Point> srcPoints = new ArrayList<Point>(4);
srcPoints.add(rect_points[2]);
srcPoints.add(rect_points[3]);
srcPoints.add(rect_points[0]);
srcPoints.add(rect_points[1]);
corners = Converters.vector_Point_to_Mat(
srcPoints, CvType.CV_32F);
}
}
Imgproc.erode(thresh, thresh, new Mat(), new Point(-1,-1), 10);
storeImage(thresh);
Imgproc.dilate(thresh, thresh, new Mat(), new Point(-1,-1), 5);
storeImage(thresh);
Mat results = new Mat(1000,250,CvType.CV_8UC3);
Mat quad = new Mat(1000,250,CvType.CV_8UC1);
List<Point> dstPoints = new ArrayList<Point>(4);
dstPoints.add(new Point(0, 0));
dstPoints.add(new Point(1000, 0));
dstPoints.add(new Point(1000, 250));
dstPoints.add(new Point(0, 250));
Mat quad_pts = Converters.vector_Point_to_Mat(
dstPoints, CvType.CV_32F);
Mat transmtx = Imgproc.getPerspectiveTransform(corners, quad_pts);
Imgproc.warpPerspective( img, results, transmtx, new Size(1000,250));
Imgproc.warpPerspective( thresh, quad, transmtx, new Size(1000,250));
Imgproc.resize(quad, quad, new Size(20,5));
Imgcodecs.imwrite("results.png",quad);
//store image
storeImage(quad);
//show image
showImage(quad);
System.out.println( quad.dump() );
for(int i = 0; i < quad.cols(); i++)
{
int size = (int) (quad.total() * quad.channels());
byte[] tmp = new byte[size];
String answer = "";
double[] d = new double[0];
d = quad.get(1, i);
answer += d[0] == 0 ? "" : "A";
d = quad.get(2, i);
answer += d[0] == 0 ? "" : "B";
d = quad.get(3, i);
answer += d[0] == 0 ? "" : "C";
d = quad.get(4, i);
answer += d[0] == 0 ? "" : "D";
if( answer.length() > 1 ) answer = "X"; // Double mark
int y = 0;
if( answer.equals("A")) y = results.rows() / (int) dims.height;
if( answer.equals("B")) y = results.rows() / (int) dims.height *2;
if( answer.equals("C")) y = results.rows() / (int) dims.height *3;
if( answer.equals("D")) y = results.rows() / (int) dims.height *4;
if( answer == "" ) answer = "[-]";
Imgproc.putText( results, answer, new Point( 50* i + 15, 30 + y), Core.FONT_HERSHEY_PLAIN, 2, new Scalar(0,0,255),2);
}
//store image
storeImage(results);
//show image
showImage(results);
}
});
public void showImage (Mat img) {
ImageView imgView = (ImageView) getActivity().findViewById(R.id.sampleImageView);
//Mat mRgba = new Mat();
//mRgba = Utils.loadResource(MainAct.this, R.drawable.your_image,Highgui.CV_LOAD_IMAGE_COLOR);
Bitmap img2 = Bitmap.createBitmap(img.cols(), img.rows(),Bitmap.Config.ARGB_8888);
Utils.matToBitmap(img, img2);
imgView.setImageBitmap(img2);
}
public File mediaStorageDir () {
File _mediaStorageDir = new File(Environment.getExternalStorageDirectory()
+ "/Android/data/"
+ getActivity().getApplicationContext().getPackageName());
return _mediaStorageDir;
}
public void storeImage(Mat matImg) {
Bitmap bitmapImg = Bitmap.createBitmap(matImg.cols(), matImg.rows(),Bitmap.Config.ARGB_8888);
Utils.matToBitmap(matImg, bitmapImg);
String timeStamp = new SimpleDateFormat("ddMMyyyy_HHmm").format(new Date());
File mediaFile;
String mImageName="IMG_"+ timeStamp +".jpg";
mediaFile = new File(mediaStorageDir().getPath() + File.separator + mImageName);
File pictureFile = mediaFile;
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
bitmapImg.compress(Bitmap.CompressFormat.PNG, 90, fos);
fos.close();
} catch (FileNotFoundException e) {
Log.d("FragmentMain", "File not found: " + e.getMessage());
} catch (IOException e) {
Log.d("FragmentMain", "Error accessing file: " + e.getMessage());
}
}