java opencv png image with alpha channel over video issue - java

everybody. I'm trying to place ".png" image over the video capture using java and opencv.
I performed the tutorial from the official opencv java website http://opencv-java-tutorials.readthedocs.io/en/latest/04-opencv-basics.html
In this tutorial everything works fine with png image from the tutorial. But when i try to load my own png image with alpha channel, i get the wrong result. Png image displays with white spaces.
Here is the print screens of video capture with both images:
correct output
incorrect output
my png image
Here is the code of loading image:
#FXML
protected void loadLogo() {
if(logoCheckBox.isSelected()) {
this.logo = Imgcodecs.imread("resources/Poli.png", Imgcodecs.IMREAD_COLOR);
}
}
Here is the code of video capture and placing png image over it:
Mat frame = new Mat();
// read the current frame
this.capture.read(frame);
Rect roi = new Rect(frame.cols() - logo.cols(), frame.rows() - logo.rows(), logo.cols(), logo.rows());
Mat imageROI = frame.submat(roi);
Core.addWeighted(imageROI, 1.0, logo, 1.0f, 0.0, imageROI);
And here is how output image place inside ImageView:
MatOfByte buffer = new MatOfByte();
Imgcodecs.imencode(".png", frame, buffer);
Image imageToShow = new Image(new ByteArrayInputStream(buffer.toArray()));
Image imageToShow = Utils.mat2Image(frame);
currentFrame.setImage(imageToShow);
I know that parameter Imgcodecs.IMREAD_COLOR load image with 3 channels, without alpha, but when i change this parameter to IMREAD_UNCHANGED i got this error:
OpenCV Error: Sizes of input arguments do not match (The operation is neither 'array op array' (where arrays have the same size and the same number of channels)
This is appear becouse the input Mat with video capture contains only 3 channels.
My question is: how can i load and place png image over video capture correctly ?
P.S When i fill the background of png image with black, its displayd correct, but image is still stay transparent.

The answer is that RGBA image cannot be placed over RGB video capture. Placing only RBG components of png image gives "wrong result".
For now, the best solution i found is to load png image in RBGA mode COLOR_UNCHANCHED wiht 4 channels, take the 4-th alpha channel and use it as mask for displaying image with function "copyTo" instead of "addWeighted".
But this method doesnt work with translucent parts of PNG image like shadows, and objects with low opacity.
Here is the code of loading image with separate RGB and alpha channel:
Mat logo = new Mat();
Mat logo_alpha = new Mat();
Mat logo_with_alpha = Imgcodecs.imread("resources/TREE_1.png", Imgcodecs.IMREAD_UNCHANGED);
Vector<Mat> rgba = new Vector<Mat>();
// split RBGA image for separate channels
Core.split(logo_with_alpha, rgba);
// get alpha channel
logo_alpha = rgba.get(3);
// remove alpha channel to get RGB image
rgba.remove(rgba.size()-1);
// get image with only RGB components
Core.merge(rgba, this.logo);
Here is the code of video capturing and placing png image over it:
Mat frame = new Mat();
// read the current frame
this.capture.read(frame);
Rect roi = new Rect(frame.cols() - logo.cols(), frame.rows() - logo.rows(), logo.cols(), logo.rows());
Mat imageROI = frame.submat(roi);
// place logo with alpha on the frame
logo.copyTo(imageROI, logo_alpha);

Related

Image Capture Specific part of screen

I'm trying to capture a specific part of my screen and save it as a JPG image.
This is what I've tried thus far:
Robot r = new Robot();
// It saves screenshot to desired path
String path = "Health_Bar.JPG";
// Used to get ScreenSize and capture image
//Dimension dim = new Dimension(376, 54, 58, 18);
Rectangle capture =
new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
BufferedImage Image = r.createScreenCapture( new Rectangle(191, 229, 473, 150) );//r.createScreenCapture(capture);
ImageIO.write(Image, "jpg", new File(path));
System.out.println("Screenshot saved");
This code is not working as expected. I expect the x and y coordinate to be the top left corner coordinate of the image captured. And even though this is true in some cases i.e. if x,y=0; it is not in most other cases i.e x=191, y=229, as in the example code above.
For the example code above this is the out I get output image from code. However this is the image I expect to get Image of expected output. The coordinates in image 2 (expected output image) is the coordinate for the top left corner of the "Mofiki's Coordinate Finder" window.
Can anyone explain whats going on and how to fix it?

Trying to fill color on square-like object using OpenCV and Java

I am trying to fill color to that square objects. It does not look like a perfect square. I have attached 4 sample images. Please have a look at it. Please help me with your ideas.
By using this code I can change the background and line colors. But I want to fill color that unshaped squares (squares only not triangle).
System.loadLibrary( Core.NATIVE_LIBRARY_NAME );
String img_url1 = "C:\\\\Users\\\\me\\\\Desktop\\\\cpt\\\\1.png";
Mat img = Imgcodecs.imread(img_url1, -1);
List<Mat> channels = new ArrayList<>();
Core.split(img, channels);
Mat black = new Mat();
Core.bitwise_not(channels.get(3), black);
String file2 = "C:\\\\\\\\Users\\\\\\\\me\\\\\\\\Desktop\\\\\\\\cpt\\\\\\\\1-cpy.png";
Imgcodecs.imwrite(file2, black);
Input Images :

Grayscale image becomes monochrome when using BufferedImage

i need to write a program which takes an image, resizes and rotates it and then saves it out. The first 2 point are done, but now I have a problem. Every time I convert a grayscale image it becomes a monochrome image.
I load the target image with the following command:
BufferedImage sourceimg = ImageIO.read(input);
And after I scaled and rotated it I save it out with the following command:
BufferedImage newimg = new BufferedImage(sourceimg.getHeight(), sourceimg.getWidth(), sourceimg.getType());
op.filter(sourceimg, newimg);
sourceimg = newimg;
ImageIO.write(sourceimg, "png", outputFile);
This works fine for every image except grayscale images. I have already tried a workaround by setting the type of every image to ARGB but there has to be another way. Is there a way to get the IndexColorModel of an given image?
The problem ist now solved, i just had to change:
BufferedImage newimg = new BufferedImage(sourceimg.getHeight(), sourceimg.getWidth(), sourceimg.getType());
to:
BufferedImage newimg = new BufferedImage(sourceimg.getWidth(), sourceimg.getHeight(), BufferedImage.TYPE_INT_ARGB);
There are other solutions, especially if you like to keep the original image type (ie. keep palette of IndexColorModel images etc.).
In fact, the easiest is to just do (assuming op is a standard BufferedImageOp):
BufferedImage newimg = op.filter(sourceimg, null);
Here a new, compatible image will be created for you, and will be of correct size to keep the result of the operation.
Another option, which will preserve the image type and color model, is slightly more verbose:
ColorModel sourceCM = sourceimg.getColorModel(); // Will be the IndexColorModel in your case
// I'm assuming you are deliberately switching width/height to rotate 90 deg
WritableRaster raster = sourceCM.createCompatibleWritableRaster(sourceimg.getHeight(), sourceimg.getWidth());
BufferedImage newimg = new BufferedImage(sourceCM, raster, sourceCM.isAlphaPremultiplied(), null);
op.filter(sourceimg, newimg);

How to set background color when rotating image using thumbnailator

I'm trying to rotate an image using thumbnailator library. It works fine except I don't see any way to set background color of the new image.
Thumbnails.of(image).rotate(45).toByteArray()
If I rotate an image 45 degree it makes corners black. If I need to set some other color, how do I do this?
Example of a rotated image with black background:
You can transform to an "intermediate" image and then apply your background color.
This is mostly untested.
Color newBackgroundColor = java.awt.Color.WHITE;
// apply your transformation, save as new BufferedImage
BufferedImage transImage = Thumbnails.of(image)
.size(100, 60)
.rotate(45)
.outputQuality(0.8D)
.outputFormat("jpeg")
.asBufferedImage();
// Converts image to plain RGB format
BufferedImage newCompleteImage = new BufferedImage(transImage.getWidth(), transImage.getHeight(),
BufferedImage.TYPE_INT_ARGB);
newCompleteImage.createGraphics().drawImage(transImage, 0, 0, newBackgroundColor, null);
// write the transformed image with the desired background color
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(newCompleteImage, "jpeg", baos);

BufferedImage red mask

I have to take screenshot of some area on the desktop.
I am doing it this way:
BufferedImage image = new Robot().createScreenCapture(area.areaRect);
ImageIO.write(image, "jpg", new File(current));
//then I paint in on JPanel
and every screenshot saved to .jpg looks like this one:
It doesn't happen for pngs and gifs.
I googled about this problem and found a solution, creating image by the Toolkit class:
Image toolkitImage = Toolkit.getDefaultToolkit().createImage(imageUrl);
but I have no idea how can I take screenshot with it.
The JPEG writer in ImageIO doesn't take the ColorModel into account when writing the file, as JPEG does not have an alpha channel. The Alpha channel is the A part of ARGB that specifies the transparency value for each pixel. Most image clients will assume that JPEG don't have alpha values as that is the standard behavior.
The writer in ImageIO will write a file with the colors shifted, like the lovely shade of salmon as background in the picture. This is because the writer will write down the values wrong when it really should be writing them in RBG.
The workaround is to paint the image to a BufferedImage with RBG as ColorModel. Here is some sample code:
// argbBuffer is the ARGB Image that should be written as JPEG:
WritableRaster raster = argbBuffer.getRaster();
WritableRaster newRaster = raster.createWritableChild(0, 0, width, height, 0, 0, new int[] {0, 1, 2});
// create a ColorModel that represents the one of the ARGB except the alpha channel:
DirectColorModel cm = (DirectColorModel)argbBuffer.getColorModel();
DirectColorModel newCM = new DirectColorModel(cm.getPixelSize(),
cm.getRedMask(), cm.getGreenMask(), cm.getBlueMask());
// now create the new buffer that is used ot write the image:
BufferedImage rgbBuffer = new BufferedImage(newCM, newRaster, false, null);

Categories