Mask image in opencv java - 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);

Related

Difference between SampleModels in Java?

So It took me quite some time solving one problem I had in my code and I'm very interested in some details. I've written a part what exactly I was doing down at the end.
So i was reading an image which I wanted to use with
static BufferedImage img = null;
img = ImageIO.read(new File("/home/user/doggo.jpg"));
Then created a BufferedImage object to store the changes which I would have done.
BufferedImage newimg = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB);
So because some parts of the image would not be changed, I figured i'd "copy" the BufferedImage to the other one (so there wouldn't be empty places) by doing:
newimg = img;
So when I was running my code, sometimes the image would be distorted, pixelated or not what i hoped for. I knew my algorithm was correct 100% and that there should be no way why it would not work.
It turned out, at least what i lea that I was "copying" one type of BufferedImage to a different type.
img.getType() returned
1
img.getColorModel() returned:
DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=0
and img.getSampleModel() returned:
java.awt.image.SinglePixelPackedSampleModel#8080b20
For the newly created BufferedImage I got:
newimg.getType() returned
5
newimg.getColorModel() returned:
ColorModel: #pixelBits = 24 numComponents = 3 color space = java.awt.color.ICC_ColorSpace#76fb509a transparency = 1 has alpha = false isAlphaPre = false
and newimg.getSampleModel() returned:
java.awt.image.PixelInterleavedSampleModel#3086002
I'm mostly interested in how does ImageIO read the image into a BufferedImage? How does it define what type of BufferedImage it will be. The image i was reading was a normal jpeg file which has RGB values, so I presumed it would not do much harm copying the BufferedImage objects like that. By now I realized that it is not as simple as I imagined but I'm still in the dark about what happened in the background. I tried reading the oracle docs but they seem maybe either too lacking or too abstract for me to comprehend.
As for the stuff I was coding, I was doing kernel convolution with images, like blur, edge detection etc. and I just wanted to copy the source image to the destination one because I didn't do any edge wrapping/clipping and I did not want the edges of the newly created image to be empty.

OpenCV thresholding unlike expected

I would like to preprocess a given picture by thresholding it in order to then hand it over to Tesseract. I first did that using Gimp (2.8.16) and a fixed range of 130 – 255. When I then implemented it in OpenCV (3.1) using Java, I first forgot to call cvtColor resulting in a picture that still had some colors in it (these areas were white in Gimp). Besides that, the picture was as expected. However, when I implemented the corresponding call, I got a picture that was different to the one I would have expected. It seems that the areas that were colored previously are now black while the remaining picture is similar to the one I created with Gimp.
Is there anything that I am missing to create a more similar output?
The reason I am asking this question is that, unfortunately, Tesseract (with psm 6) creates quite different results for the two images:
for the one created in Gimp: "2011 1 L 0006"
for the second one created with OpenCV: "2011ÔÇö] L 0 0006 1"
Here is the code that I used:
Mat thres = new Mat();
Mat tmp = new Mat();
Imgproc.cvtColor(src, tmp, Imgproc.COLOR_BGR2GRAY); // tmp = src.clone(); in my first attempt
Imgproc.threshold(tmp, thres, 130, 255, Imgproc.THRESH_BINARY);
Imgcodecs.imwrite("output.jpg", thres);
Here are the pictures:
Given picture:
Picture created with Gimp:
First result using OpenCV:
Second result using OpenCV:
In the first case, You are doing thresholding on color image (tmp = src.clone() creates another copy of src which is a color image). So you are getting result like that and in the second case, you are first converting to grayscale and then thresholding which gives a better result. Thresholding is good on gray scale images.

How to crop image in opencv with android

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);

Convert 2D array of integers to bitmap in Java

I have a 2D array of integers in Java.
I want to take this and output a bitmap image file where the red value of each pixel is the corresponding value in the array (blue and green values are 0).
Does anyone know how to do this? Thanks.
You can create a BufferedImage and use BufferedImage.setRGB(x, y, rgb), where rgb is your (byteArray[x][y] <<< 4) & 0xFF0000. Then save it using ImageIO.write(image, "bmp", file).
There nothing to do but to do it, unfortunately. That's not supposed to be a flip answer - but the file format for a BMP image, which is the easiest, is well defined.
Here's the BMP wiki.

How to work with BufferedImage and YCbCr colorspace?

I need to translate colors in bitmap loaded to BufferedImage from RGB to YCbCr (luminance and 2 channels chrominance) and back after process.
I made it with functions used like rgb2ycbcr() in main method for each pixel, but it isn't so smart solution. I should use ColorSpace and ColorModel classes to get BufferedImage with correct color space. It would be more flexible method, but I don't know how to do that.
I'm lost and I need some tips. Can somebody help me?
As I understood your question, you want to do the following:
Load RGB image -> process YCbCr image -> Use RGB image again
And you want us to help you, to make this process as seamless as possible. First and foremost you want us to give you a simple way to avoid the -> (converting) parts.
Well I looked into the BufferedImage documentation. It seems, as if there doesn't exist a way to change the ColorSpace of an once created BufferedImage.
You could create a new BufferedImage with an YCbCr color space for that you can use the predefined ICC_ColorSpace. Then you copy the data from your old image possibly via ColorSpace.fromRGB to the YCbCr color space, do the image processing and then convert again via ColorSpace.toRGB. This method requires you to fully convert the image before and after processing via existing methods. Furthermore you have to know, how ICC_ColorSpace converts your image to YCbCr color space. Otherwise you can't know, which array indices corresponds to the same pixel.
If you just want to create a wrapper around the RGB-BufferedImage that lets you manipulate this image, as if it was an YCbCr image, that isn't possible with BufferedImage.
EDIT:
To convert the color space of a BufferedImage use ColorConvertOp. The code would look something like this:
ColorConvertOp cco = new ColorConvertOp(new YCbCrColorSpace(), null);
BufferedImage ycbcrImage = cco.filter( oldRGBImage, null );
This requires you to either write your own ColorSpace class or you could download and use the classes mentioned here. If you just want to load a JPEG image you should use the predefined classes.

Categories