get average image of a set of images in java - java

I have a set of meteorological RGB type BufferedImages. I want to get average image of them. By that, I mean get average value of each pixel and make a new image out of those values. What I tried is this:
public void getWaveImage(BufferedImage input1, BufferedImage input2){
// images are of same size that's why i'll use first one's width and height
int width = input1.getWidth(), height = input1.getHeight();
BufferedImage output = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
int[] rgb1 = input1.getRGB(0, 0, width, height, new int[width * height], 0, width);
int[] rgb2 = input2.getRGB(0, 0, width, height, new int[width * height], 0, width);
for(int i=0; i<width; i++){
for(int j=0; j<height; j++){
int rgbIndex = i * width + j;
rgb1[rgbIndex] = (rgb1[rgbIndex] + rgb2[rgbIndex]) / 2;
}
}
output.setRGB(0, 0, width, height, rgb1, 0, width);
return output;
}
What am I doing wrong? Thank you in advance.
input1:
input2:
output:

You want the average of each component of the colour, average red, average green, average blue.
Instead you are averaging the whole int.
Color c1 = new Color(rgb1[rgbIndex]);
Color c2 = new Color(rgb2[rgbIndex]);
Color cA = new Color((c1.getRed() + c2.getRed())/2,
(c1.getGreen() + c2.getGreen())/2,
(c1.getBlue() + c2.getBlue())/2);
rgb1[rgbIndex] = cA.getRGB();
This may not be the most efficient due to creating so many objects, so a more direct approach is like so:
public static int average(int argb1, int argb2){
return (((argb1 & 0xFF) + (argb2 & 0xFF)) >> 1) | //b
(((argb1 >> 8 & 0xFF) + (argb2 >> 8 & 0xFF)) >> 1) << 8 | //g
(((argb1 >> 16 & 0xFF) + (argb2 >> 16 & 0xFF)) >> 1) << 16 | //r
(((argb1 >> 24 & 0xFF) + (argb2 >> 24 & 0xFF)) >> 1) << 24; //a
}
Usage:
rgb1[rgbIndex] = average(rgb1[rgbIndex], rgb2[rgbIndex]);

If you have:
int rgb1, rgb2; //the rgb value of a pixel in image 1 and 2 respectively
The "average" color would be:
int r = (r(rgb1) + r(rgb2)) / 2;
int g = (g(rgb1) + g(rgb2)) / 2;
int b = (b(rgb1) + b(rgb2)) / 2;
int rgb = ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | ((b & 0xFF) << 0);
with the following "helper" methods:
private static int r(int rgb) { return (rgb >> 16) & 0xFF; }
private static int g(int rgb) { return (rgb >> 8) & 0xFF; }
private static int b(int rgb) { return (rgb >> 0) & 0xFF; }
Alternatively you can use the Color class if you don't want to deal with bitwise operations.

another solution can be to replace
rgb1[rgbIndex] = (rgb1[rgbIndex] + rgb2[rgbIndex]) / 2;
with
rgb1[rgbIndex] = ((rgb1[rgbIndex]>>1)&0x7f7f7f7f)+((rgb2[rgbIndex]>>1)&0x7f7f7f7f)+(rgb1[rgbIndex]&rgb2[rgbIndex]&0x01010101);
binary right shift to divide by 2, last member of the sum to handle the case of two odd numbers.

Related

Java, Create an ARGB BufferedImage from a an int[] array

I want to make a funktion, that takes every pixel and changes it a bit.
after it is changed, I have an array of all the pixels, i want to translate that array back to an image.
What would be the fastest way to do that?
byte[] argb = ((DataBufferByte) ii.getRaster().getDataBuffer()).getData();
int j = (argb.length / 4);
int[]intrb = new int[j];
for(int k = 0; k < j; k++) {
{
intrb[k] = (((int) argb[4*k]-0x99 & 0xff) << 24); // alpha
intrb[k] += ((int) argb[4*k+1] & 0xff); // blue
intrb[k] += (((int) argb[4*k+2] & 0xff) << 8); // green
intrb[k] += (((int) argb[4*k+3] & 0xff) << 16); // red
}
}
ii.setRGB(); // this is, where i would translate it back. But it has to be an ARGB version.
return ii;

java get difference between two images in percent

I have the following code:
private static int pixelDiff(int rgb1, int rgb2) {
int r1 = (rgb1 >> 16) & 0xff;
int g1 = (rgb1 >> 8) & 0xff;
int b1 = rgb1 & 0xff;
int r2 = (rgb2 >> 16) & 0xff;
int g2 = (rgb2 >> 8) & 0xff;
int b2 = rgb2 & 0xff;
return Math.abs(r1 - r2) + Math.abs(g1 - g2) + Math.abs(b1 - b2);
}
and it works without a problem, but it takes to long and i don't know how it optimize it.
So the basic is, that i want to compare two images and get the percentage of difference.
Therefor I load the RGB of both images and compare them with this code.
My question: Is it possible to optimize this code, or do you have any idea to compare two images(not only that they are equal)
UPDATE:
here is the full code:
private double getDifferencePercent(BufferedImage img1, BufferedImage img2) {
int width = img1.getWidth();
int height = img1.getHeight();
int width2 = img2.getWidth();
int height2 = img2.getHeight();
if (width != width2 || height != height2) {
throw new IllegalArgumentException(String.format("Images must have the same dimensions: (%d,%d) vs. (%d,%d)", width, height, width2, height2));
}
long diff = 0;
for (int y = height - 1; y >= 0; y--) {
for (int x = width - 1; x >= 0; x--) {
diff += pixelDiff(img1.getRGB(x, y), img2.getRGB(x, y));
}
}
long maxDiff = 765L * width * height;
return 100.0 * diff / maxDiff;
}
private static int pixelDiff(int rgb1, int rgb2) {
int r1 = (rgb1 >> 16) & 0xff;
int g1 = (rgb1 >> 8) & 0xff;
int b1 = rgb1 & 0xff;
int r2 = (rgb2 >> 16) & 0xff;
int g2 = (rgb2 >> 8) & 0xff;
int b2 = rgb2 & 0xff;
return Math.abs(r1 - r2) + Math.abs(g1 - g2) + Math.abs(b1 - b2);
}
I checked this with a profiler and it shows that pixelDiff() is very slow.

Java : Mix two colors in a realistic way

Given two colors A and B, I would like to get the resulting color C, that is the most possible realistic natural mix of the A and B.
Example :
Red + Yellow = Orange
Blue + Yellow = Green
Red + Blue = Purple
Blue + White = Light Blue
Blue + Black = Dark Blue
etc...
Can I get it with ARGB representation of the given colors?
We can call a function which returns result array when give two arrays as parameters. But Arrays should be same sizes.
public int getAvgARGB(int[] clr1, int[] clr2){
int[] returnArray = new int[clr1.length];
for(int i=0; i<clr1.length;i++){
int a1[i] = (clr1[i] & 0xFF000000) >>> 24;
int r1[i] = (clr1[i] & 0x00FF0000) >> 16;
int g1[i] = (clr1[i] & 0x0000FF00) >> 8;
int b1[i] = (clr1[i] & 0x000000FF) ;
int a2[i] = (clr2[i] & 0xFF000000) >>> 24;
int r2[i] = (clr2[i] & 0x00FF0000) >> 16;
int g2[i] = (clr2[i] & 0x0000FF00) >> 8;
int b2[i] = (clr2[i] & 0x000000FF) ;
int aAvg = (a1[i] + a2[i]) / 2;
int rAvg = (r1[i] + r2[i]) / 2;
int gAvg = (g1[i] + g2[i]) / 2;
int bAvg = (b1[i] + b2[i]) / 2;
int returnArray[i] = (aAvg << 24) + (rAvg << 16) + (gAvg << 8) + bAvg;
}
return returnArray;
}

Converting 8 bit to 4 bit image

Can anyone see what the issue is when I try to convert my 8 bit image into an 4 bit image?
I am testing using the 8 bit image found here: http://laurashoe.com/2011/08/09/8-versus-16-bit-what-does-it-really-mean/
You can tell how the 4 bit image should look like but mine is almost purely black.
// get color of the image and convert to grayscale
for(int x = 0; x <img.getWidth(); x++) {
for(int y = 0; y < img.getHeight(); y++) {
int rgb = img.getRGB(x, y);
int r = (rgb >> 16) & 0xF;
int g = (rgb >> 8) & 0xF;
int b = (rgb & 0xF);
int grayLevel = (int) (0.299*r+0.587*g+0.114*b);
int gray = (grayLevel << 16) + (grayLevel << 8) + grayLevel;
img.setRGB(x,y,gray);
}
}
You should use 0xFF not 0xF,as 0xF means only last four bits, wchich will tell you almost nothing about the color, since in RGB an color is 8 bit.
try if this work:
// get color of the image and convert to grayscale
for(int x = 0; x <img.getWidth(); x++) {
for(int y = 0; y < img.getHeight(); y++) {
int rgb = img.getRGB(x, y);
int r = (rgb >> 16) & 0xFF;
int g = (rgb >> 8) & 0xFF;
int b = (rgb & 0xFF);
int grayLevel = (int) (0.299*r+0.587*g+0.114*b);
int gray = (grayLevel << 16) + (grayLevel << 8) + grayLevel;
img.setRGB(x,y,gray);
}
}
Since the code has been edited out from the question, here it is with the confirmed solution from the comments:
// get color of the image and convert to grayscale
for(int x = 0; x <img.getWidth(); x++) {
for(int y = 0; y < img.getHeight(); y++) {
int rgb = img.getRGB(x, y);
// get the upper 4 bits from each color component
int r = (rgb >> 20) & 0xF;
int g = (rgb >> 12) & 0xF;
int b = (rgb >> 4) & 0xF;
int grayLevel = (int) (0.299*r+0.587*g+0.114*b);
// use grayLevel value as the upper 4 bits of each color component of the new color
int gray = (grayLevel << 20) + (grayLevel << 12) + (grayLevel << 4);
img.setRGB(x,y,gray);
}
}
Note that the resulting image only looks like 4-bit grayscale, but still uses int as the RGB value.
8 bit image values are in range [0, 255] because pow(2, 8) = 256
To get 4 bit image values which will be in range [0, 15] as pow(2, 4) = 16,
we need to divide each pixel value by 16 -> range [0, 255] / 16 = range [0, 15].
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread("crowd.jpeg")
#Convert the image to grayscale
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
plt.imshow(gray_img, cmap='gray')
Grayscale image
bit_4 = np.divide(gray_img, 16).astype('uint8')
plt.imshow(bit_4, cmap='gray')
Bit4 image

How do I stop java from setting my BufferedImage to fully transparent?

I am trying to create an image editor in java, but when I run the code, the output image is fully transparent.
Here is my code for Main.java:
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Main {
public static void main(String[] args) {
BufferedImage image = null;
try {
image = ImageIO.read(new File("strawberry.png"));
} catch (IOException e) {
System.out.println(e);
}
new Negative(image);
File outputfile = new File("saved.png");
try {
ImageIO.write(image, "png", outputfile);
} catch (IOException e) {
System.out.println(e);
}
}
}
And here is my code for Negative.java:
import java.awt.image.BufferedImage;
public class Negative {
public Negative(BufferedImage img) {
for (int x = 0; x < img.getWidth(); ++x) {
for (int y = 0; y < img.getHeight(); ++y) {
int rgb = img.getRGB(x, y);
int r = (rgb >> 16) & 0xFF;
int g = (rgb >> 8) & 0xFF;
int b = (rgb & 0xFF);
r = 255 - r;
g = 255 - g;
b = 255 - b;
int newColour = (r << 16) + (g << 8) + (b << 4);
img.setRGB(x, y, newColour);
}
}
}
}
If anyone could help I would be very grateful.
Problem
What they call RGB color is in fact ARGB, 8 bits for each.
Alpha is given in the highest 8 bits, 0 for transparent to 255 for fully opaque.
This is what TYPE_INT_ARGB means in the javadoc for BufferedImage.setRGB() :
The pixel is assumed to be in the default RGB color model, TYPE_INT_ARGB, and default sRGB color space.
Solution
For a fully opaque image, add a 255 alpha value:
int newColour = (0xff << 24) + (r << 16) + (g << 8) + (b << 4);
Alternatively, you can take the original alpha of your image if you extract it too:
int rgb = img.getRGB(x, y);
int alpha = (rgb >>> 24);
int r = (rgb >> 16) & 0xFF;
int g = (rgb >> 8) & 0xFF;
int b = (rgb & 0xFF);
int newColour = (alpha << 24) + (r << 16) + (g << 8) + (b << 4);
There is one more component of a color: alpha channel. It is stored in 24-31 bits of a number. If it is set to 0, the image is transparent. So you need to set 24-31 bits of newColor to 1 to make it opaque.

Categories

Resources