How to crop image in opencv with android - java

i Have a rectangle drawn by opencv+java+android. now i need to crop and display in my imagviwe how to crop it. submat methord is allow only int values. but rect.tl().x values are in double. casting is not a good solution. it make errors.
Core.rectangle(ImageMatin, rect.tl(), rect.br(), new Scalar(255, 0, 0),1); // works
Mat cropped = ImageMatin.submat(rect.tl().x, rect.height, rect.tl().x, rect.width); //error

i recommend this library from github: https://github.com/edmodo/cropper , it even has a sample so if you get stuck you can always refer to it. Also this library is great for cropping purposes and lightweight as well, i myself have used it in one or two apps

You can crop Mat images by appling this command:
Mat croppedImg = new Mat (sourceMat, ROI);

Related

What happens to submats if the original mat is released

In OpenCV, I am getting an image and crop some ROIs out (using mat.submat(...)). I would like to avoid cloning the submats to save time and memory, but I am afraid that the image might be released before the rois are. Given that submat returns a Mat that uses the original Mat as its backing storage, my question is: What happens to sub-mats after their parent mat has been released? Is it safe to use the submats afterwards?
Here's the code to explain the question:
// Some big image that I get from somewhere
Mat image = Mat.zeros(1080, 1920, CvType.CV_8UC3);
Mat roi = image.submat(10, 20, 10, 20);
image.release();
// Still safe to use roi?
Mat blurredRoi = new Mat();
Imgproc.blur(roi, blurredRoi, new Size(5, 5));
OpenCV uses reference counting.
submat adds another reference to the data memory.
.release() does not deallocate the memory, unless the last reference was removed/decremented.

Preprocessing images for OCR: local Otsu thresholding or another binarization algorithm using OpenCV?

I am using Otsu, which is a global thresholding technique, using OpenCV in java.
But I read that local thresholding techniques (e.g. Local Otsu, Sauvola, Niblack, etc...) are more effective in leaving out text from images (I am preprocessing images for OCR).
This is what I am doing:
Mat src = Imgcodecs.imread(imageFilePath, Imgcodecs.IMREAD_GRAYSCALE);
//Creating an empty matrices to store the destination image.
Mat dst = new Mat(src.rows(), src.cols(), src.type());
//Applying simple threshold
Imgproc.threshold(src, dst, 50, 255, Imgproc.THRESH_OTSU);
Bitmap bitmap = BitmapFactory.decodeFile(imageFilePath);
Utils.matToBitmap(dst, bitmap);
Should I use otsu? If so, how can be applied locally?
Or should I use another binarization algorithm?
A global method cannot work everywhere on an image if the illumination is uneven, or if the background color changes.
A local method copes with that but requires a scale parameter, which depends on the size of the features to detect. (This is a problem for general-purpose solutions where this size is unknown.)
Applying the filter in a sliding window is best because it results in a continuous threshold function, but is more time-consuming than with tiled windows.

Mask image in opencv java

I need to convert near white pixels to white and near black pixels to black.
I found a code snippet in python on how to do it.
hsv=cv.cvtColor(image,cv.COLOR_BGR2HSV)
# Define lower and upper limits of what we call "brown"
brown_lo=np.array([10,0,0])
brown_hi=np.array([20,255,255])
# Mask image to only select browns
mask=cv.inRange(hsv,brown_lo,brown_hi)
# Change image to red where we found brown
image[mask>0]=(0,0,255)
I have converted it java as below.
Mat temp= new Mat();
Imgproc.cvtColor(src,temp,COLOR_BGR2HSV);
Scalar low= new Scalar(10,0,0);
Scalar high= new Scalar(20,255,255);
Mat mask = new Mat();
inRange(temp,low,high,mask);
But I am facing problem converting below statement to java and there is no good opencv documentation in java with samples.
image[mask>0]=(0,0,255)
Could somebody help on how to convert above statement to java...?
I have tried setTo but it is not giving desired behaviour(attached screenshot below). Refer https://stackoverflow.com/a/50215020/12643143 for the expected result.
src.setTo(new Scalar(0,0,255),mask);
I recommend to use setTo(). This method can set all he pixels in a Mat. If an optionally mask argument is specified, then all the pixels who have a corresponding pixel with a non-zero value in the mask will be set.
Thus the python statement
image[mask>0]=(0,0,255)
can be substituted in Java by:
image.setTo(new Scalar(0, 0, 255), mask);
where image has to be a Mat object.
Answer to the question
As #Rabbid76 mentioned setTo is the correct way to do this. However if you want specific logic like image[mask>127]=(0,0,255), then do threshold (Imgproc.threshold(grey,grey, 127, 255, THRESH_BINARY);) and then use setTo.
Solution to my problem
Actually my problem was not due to setTo. Its the logic mismatch between how I read/write the Mat in my code Vs the post I referred.
I am posting the solution to the problem that I have faced so that it might help new bees like me.
Problem in reading Image
The post use Imgcodecs.imread() to read image to Mat in BGR format.
Whereas I am loading bitmap using bitmapToMat in CV_8UC4 type as below which reads the image to Mat in RGBA format.
Mat src = new Mat(bitmap.getHeight(), bitmap.getWidth(), CV_8UC4);
org.opencv.android.Utils.bitmapToMat(bitmap, src);
Fix is to convert the format properly.
Mat src = new Mat(bitmap.getHeight(), bitmap.getWidth(), CV_8UC3); //notice 3 channel
org.opencv.android.Utils.bitmapToMat(bitmap, src);
Imgproc.cvtColor(src,hsv,COLOR_RGB2HSV); //Convert RGB to HSV. COLOR_RGBA2HSV not exist, hence we load it in CV_8UC3(3 channel R,G,B).
Problem in writing the Image
Similarly as we have differences in reading between bitmapToMat and imread, the same are applicable for writing. Imgcodecs.imwrite() will write the BGR image to bitmap, where as I have to convert it back to RGB format for matToBitmap to work like Imgproc.cvtColor(rgb, rgb, Imgproc.COLOR_BGR2RGB);

How to detect upside down text in image / upside down image using OpenCV and Java

I face this problem for a while and still cannot figure it out.
I'm using OpenCV and Java to detect the card from image and crop it then transfrom, every thing goes fine if the original image is in correct orientation. The task for now is: How to rotate image in 180 degrees? Or how to detect the text is upside down then we can rotate it to right orientation.
The result i get from OpenCV in Java is the Mat object contains the cropped cards.
Does anyone faced and solved this before or have any idea please direct me to the right solution.
Thanks in advance.
Here is sample images i cropped from originals
Sample 1
Sample 2
Just run your code, and if it finds nothing on the first pass then you can just rotate the image and try again? Or you could try scanning a small portion of the card only, and then you can determine if the card is the right way up or not.
It will pay to sit down and think it through for yourself on how this might be done best.
As for rotating images, have you tried looking it up? Because if you are using a buffered image then rotating can be done with a simple AffineTransform:
AffineTransform at = AffineTransform.getRotateInstance(Math.PI, image.getWidth()/2, image.getHeight()/2.0);
BufferedImage myRotatedImage = createTransformed(image, at);
Edit: For rotating a Mat object you can use:
Imgproc.warpAffine(Mat source, Mat dest, Mat M, Size size)
Taken from: http://docs.opencv.org/java/3.0.0/

Merge two Image.class items into 1 Image.class

I have been researching this for a while on this site and have not came up with exactly what I am looking for. I am working with software in which I do not have the source for must pass that code an Image.class image. I have 2 images that I need to overlay the first one over the second and then pass that combined image to the software I am interfacing with. I must use the Java Language for all code.
Everything I have found is writing the 2 images directly. Is what I am trying to do possible and if so please can you help me get these results? A code stub would be wonderful. I surely appreciate all of the time you take in assisting me.
Basically, you can "paint" one image on to the other...The trick is getting one of them into the correct format...
Image image1 = ...;
Image image2 = ...;
BufferedImage buffer1 = new BufferedImage(image1.getWidth(this), image1.getHeight(this), BufferedImage.TYPE_ARGB);
Graphics2D g = buffer.createGraphics();
g.drawImage(image1, 0, 0, this);
g.drawImage(image2, 0, 0, this);
g.dispose();
This will overlay image2 over image1. You can either assign buffer1 to image1 and pass it to you other class or simple pass buffer1 as BufferedImage extends from java.awt.Image

Categories