RGB to CIELAB conversion [duplicate] - java

This question already has answers here:
Java: how to convert RGB color to CIE Lab
(6 answers)
Closed 6 years ago.
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.ImageIO;
public class ConvertRGBtoLAB {
public static void main(String[] args) {
//get input image
String fileName = "IMG_7990.jpg";
//read input image
BufferedImage image = null;
try
{
image = ImageIO.read(new File(fileName));
}
catch (IOException e)
{
e.printStackTrace();
}
//setup result image
int sizeX = image.getWidth();
int sizeY = image.getHeight();
float r, g, b, X, Y, Z, fx, fy, fz, xr, yr, zr;
float ls, as, bs;
float eps = 216.f/24389.f;
float k = 24389.f/27.f;
float Xr = 0.964221f; // reference white D50
float Yr = 1.0f;
float Zr = 0.825211f;
for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) {
int c = image.getRGB(x,y);
int R= (c & 0x00ff0000) >> 16;
int G = (c & 0x0000ff00) >> 8;
int B = c & 0x000000ff;
r = R/255.f; //R 0..1
g = G/255.f; //G 0..1
b = B/255.f; //B 0..1
// assuming sRGB (D65)
if (r <= 0.04045)
r = r/12;
else
r = (float) Math.pow((r+0.055)/1.055,2.4);
if (g <= 0.04045)
g = g/12;
else
g = (float) Math.pow((g+0.055)/1.055,2.4);
if (b <= 0.04045)
b = b/12;
else
b = (float) Math.pow((b+0.055)/1.055,2.4);
X = 0.436052025f*r + 0.385081593f*g + 0.143087414f *b;
Y = 0.222491598f*r + 0.71688606f *g + 0.060621486f *b;
Z = 0.013929122f*r + 0.097097002f*g + 0.71418547f *b;
// XYZ to Lab
xr = X/Xr;
yr = Y/Yr;
zr = Z/Zr;
if ( xr > eps )
fx = (float) Math.pow(xr, 1/3.);
else
fx = (float) ((k * xr + 16.) / 116.);
if ( yr > eps )
fy = (float) Math.pow(yr, 1/3.);
else
fy = (float) ((k * yr + 16.) / 116.);
if ( zr > eps )
fz = (float) Math.pow(zr, 1/3.);
else
fz = (float) ((k * zr + 16.) / 116);
ls = ( 116 * fy ) - 16;
as = 500*(fx-fy);
bs = 200*(fy-fz);
int Ls = (int) (2.55* ls + .5);
int As = (int) (as + .5);
int Bs = (int) (bs + .5);
int lab = 0xFF000000 + (Ls << 16) + (As << 8) + Bs; // and reassign
image.setRGB(x, y, lab);
}
}
//write new image
File outputfile = new File("lab.png");
try {
// png is an image format (like gif or jpg)
ImageIO.write(image, "png", outputfile);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
Hello,
I am trying to turn a RGB image into CIELAB colour space(LAB) I get an output but I have no idea what it is supposed to look like.
Can anyone point me to a already existing image converter or confirm that I have done this correctly?
Thanks!

I personally use this site as quick reference on conversion formulas between common color spaces.

OpenCV have functions for conversions between different color spaces. Look at my other answer here. This is in C, bout you can easily check you code.

You will want to ensure that Ls, As, and Bs are clamped to the range 0 to 255. The statement you have to combine them into a single int will allow an out-of-bounds value to affect the other values.

Related

BufferedImage Kernel - Image Processing

I would like to create an image filter and have read the following Wikipedia article. I wanted to test the example from Wikipedia and get an incorrect result.
https://en.wikipedia.org/wiki/Kernel_(image_processing)
(For some reason I cannot upload images)
Result:
https://imgur.com/FiYFuZS
Expected result:
https://upload.wikimedia.org/wikipedia/commons/2/20/Vd-Rige1.png
I've also read the following source and still do not know how to fix it :/
Bluring a Java buffered image
URL url = new URL("https://upload.wikimedia.org/wikipedia/commons/5/50/Vd-Orig.png");
BufferedImage image = ImageIO.read(url);
float[][] kernel = {
{0, -1, 0},
{-1, 4, -1},
{0, -1, 0}
};
int w = image.getWidth();
int h = image.getHeight();
// Center point
int cx = kernel.length / 2;
int cy = kernel[0].length / 2;
BufferedImage cImage = new BufferedImage(w, h, image.getType());
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
float r = 0;
float g = 0;
float b = 0;
for (int dx = -cx; dx <= cx; dx++) {
for (int dy = -cy; dy <= cy; dy++) {
float e = kernel[dx + cx][dy + cy];
int xImage = x + dx;
int yImage = y + dy;
if (xImage < 0 || xImage >= w || yImage < 0 || yImage >= h) {
continue;
}
Color pixel = new Color(image.getRGB(xImage, yImage));
r += pixel.getRed() * e;
g += pixel.getGreen() * e;
b += pixel.getBlue() * e;
}
}
// Boundaries
r = Math.min(255, Math.max(0, r));
g = Math.min(255, Math.max(0, g));
b = Math.min(255, Math.max(0, b));
Color newPixel = new Color((int) r, (int) g, (int) b);
cImage.setRGB(x, y, newPixel.getRGB());
}
}
ImageIO.write(cImage, "png", Files.newOutputStream(Path.of("c.png")));

How to get background color of XSSFSimpleShape object?

Does POI-Framwork support to get background color of a XSSFSimpleShape object? I looked around this class but I couldn't find the way to get its background?
Here is my code:
XSSFSimpleShape simpleObj = ...
simpleObj.getCTShape().getSpPr()... get some things named color here
simpleObj.getCTShape().getStyle().getFillRef()... get some things named colors here
This is the opposite of a trivial task. You are on the right way with CTShapeProperties. The next step is CTSolidColorFillProperties and as long we will find a CTSRgbColor all things will be easy because this is simply RGB. But there are much more possible kinds of color types as you see.
One possible color type which Excel is often using is CTSchemeColor. This color is a theme color from ThemesTable but possibly additional determined by given luminescence changing from lumMod and lumOff.
Example XML:
<a:solidFill>
<a:schemeClr val="accent4">
<a:lumMod val="60000"/>
<a:lumOff val="40000"/>
</a:schemeClr>
</a:solidFill>
Problem with this is that those luminescence changings are made in HSL. Javas java.awt.Color is only supporting HSB (aka HSV) but not HSL. So we need additional code for supporting HSL. In my example I have used the code from Rob Camick.
So knowing that all we can do the following:
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFSimpleShape;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFColor;
import org.apache.poi.xssf.model.ThemesTable;
import java.io.InputStream;
import java.io.FileInputStream;
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTShape;
import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.STSchemeColorVal;
import java.awt.Color;
class ReadExcelShapeFillColors {
//two methods for dealing with the HSL color model
//helper method HueToRGB, see below
private static float HueToRGB(float p, float q, float h) {
if (h < 0) h += 1;
if (h > 1 ) h -= 1;
if (6 * h < 1) {
return p + ((q - p) * 6 * h);
}
if (2 * h < 1 ) {
return q;
}
if (3 * h < 2) {
return p + ( (q - p) * 6 * ((2.0f / 3.0f) - h) );
}
return p;
}
//get a new Color changed by given luminescence from lumMod and lumOff
private static Color getColorLumModandOff(Color color, int lumMod, int lumOff) {
float[] rgb = color.getRGBColorComponents( null );
float r = rgb[0];
float g = rgb[1];
float b = rgb[2];
float min = Math.min(r, Math.min(g, b));
float max = Math.max(r, Math.max(g, b));
float h = 0;
if (max == min) h = 0;
else if (max == r) h = ((60 * (g - b) / (max - min)) + 360) % 360;
else if (max == g) h = (60 * (b - r) / (max - min)) + 120;
else if (max == b) h = (60 * (r - g) / (max - min)) + 240;
float l = (max + min) / 2;
l = l * (float)lumMod/100000f + (float)lumOff/100000f;
float s = 0;
if (max == min) s = 0;
else if (l <= .5f) s = (max - min) / (max + min);
else s = (max - min) / (2 - max - min);
h = h % 360.0f;
h /= 360f;
float q = 0;
if (l < 0.5) q = l * (1 + s);
else q = (l + s) - (s * l);
float p = 2 * l - q;
r = Math.max(0, HueToRGB(p, q, h + (1.0f / 3.0f)));
g = Math.max(0, HueToRGB(p, q, h));
b = Math.max(0, HueToRGB(p, q, h - (1.0f / 3.0f)));
r = Math.min(r, 1.0f);
g = Math.min(g, 1.0f);
b = Math.min(b, 1.0f);
return new Color(r, g, b, 1.0f);
}
public static void main(String[] args) throws Exception {
InputStream inp = new FileInputStream("ExcelWithSimpleShape.xlsx");
Workbook workbook = WorkbookFactory.create(inp);
Sheet sheet = workbook.getSheetAt(0);
Drawing<? extends Shape> drawing = sheet.getDrawingPatriarch();
for (Shape shape : drawing) {
System.out.println(shape.getClass());
System.out.println(shape.getShapeName() + "_________________");
if (shape instanceof XSSFSimpleShape) { //we have a XSSFSimpleShape
XSSFWorkbook xssfworkbook = (XSSFWorkbook)workbook;
ThemesTable themesTable = xssfworkbook.getTheme();
XSSFSimpleShape xssfSimpleShape = (XSSFSimpleShape)shape;
CTShape ctShape = xssfSimpleShape.getCTShape();
CTShapeProperties ctShapeProperties = ctShape.getSpPr();
byte[] bRGB;
if (ctShapeProperties.isSetSolidFill()) { //we have a solid fill defined
CTSolidColorFillProperties ctSolidColorFillProperties = ctShapeProperties.getSolidFill();
if (ctSolidColorFillProperties.isSetSrgbClr()) { //we have a explicit given RGB color
bRGB = ctSolidColorFillProperties.getSrgbClr().getVal();
System.out.println((bRGB[0]&0xFF)+", "+(bRGB[1]&0xFF)+", "+(bRGB[2]&0xFF));
Color color = new Color(bRGB[0]&0xFF, bRGB[1]&0xFF, bRGB[2]&0xFF);
System.out.println("explicit given RGB color: " + color);
} else if (ctSolidColorFillProperties.isSetSchemeClr()) { //we have a scheme color defined in ThemesTable
int iThemeColorIdx = ctSolidColorFillProperties.getSchemeClr().getVal().intValue()-1;
System.out.println("theme color index: " + iThemeColorIdx);
//get luminescence definition
int lumMod = 100000;
int lumOff = 0;
if (ctSolidColorFillProperties.getSchemeClr().getLumModList().size() > 0)
lumMod = ctSolidColorFillProperties.getSchemeClr().getLumModList().get(0).getVal();
if (ctSolidColorFillProperties.getSchemeClr().getLumOffList().size() > 0)
lumOff = ctSolidColorFillProperties.getSchemeClr().getLumOffList().get(0).getVal();
System.out.println("lumMod: " + lumMod);
System.out.println("lumOff: " + lumOff);
XSSFColor xssfColor = themesTable.getThemeColor(iThemeColorIdx);
bRGB = xssfColor.getRGB(); //RGB color from ThemesTable
System.out.println((bRGB[0]&0xFF)+", "+(bRGB[1]&0xFF)+", "+(bRGB[2]&0xFF));
Color color = new Color(bRGB[0]&0xFF, bRGB[1]&0xFF, bRGB[2]&0xFF);
color = getColorLumModandOff(color, lumMod, lumOff); //Color changed by given lumMod and lumOff
System.out.println("scheme color: " + color);
}
} else { //we have accent1 scheme color as fill color
XSSFColor xssfColor = themesTable.getThemeColor(STSchemeColorVal.INT_ACCENT_1-1);
bRGB = xssfColor.getRGB();
System.out.println((bRGB[0]&0xFF)+", "+(bRGB[1]&0xFF)+", "+(bRGB[2]&0xFF));
Color color = new Color(bRGB[0]&0xFF, bRGB[1]&0xFF, bRGB[2]&0xFF);
System.out.println("accent1 scheme color: " + color);
}
}
}
workbook.close();
}
}
This code will get the fill colors of all shapes from first sheet of the XSSFWorkbook file which are instanceof XSSFSimpleShape as long as they are given by solid fill and are either CTSRgbColor or CTSchemeColor.

How to extract Y, Cb and Cr color components?

I need to decompose a given colored picture in three separate pictures, so that each color component (Y, Cb, Cr) is stored in one picture like here.
Maybe has an idea how I could get these three pictures with
separately Y, Cb or Cr color components? With following peace of code I can just read out the file and convert the color model from RGB to YCbCr.
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class SpaceConverter {
static int [] colorComponentsYCbCr = new int[3];
static int [] colorComponentsRGB = new int[3];
public static void getRGBComponents (int color)
{
colorComponentsRGB [0] = (color & 0xff);
colorComponentsRGB [1] = (color & 0x00ff) >> 8;
colorComponentsRGB [2] = (color & 0x0000ff) >> 16;
}
public static void convertYCbCr2RGB(int [] componentsYCbCrToConvert)
{
int Y = componentsYCbCrToConvert [0];
int Cb = componentsYCbCrToConvert [1];
int Cr = componentsYCbCrToConvert [2];
colorComponentsRGB = new int [3];
colorComponentsRGB [0] = (int) (Y + 1.402 * (Cr - 128));
colorComponentsRGB [1] = (int) (Y - 0.34414 * (Cb - 128) - 0.71414 * (Cr - 128));
colorComponentsRGB [2] = (int) (Y + 1.772 * (Cb - 128));
}
public static void convertRGB2YCbCr(int [] componentsRGB)
{
int blue = componentsRGB [0];
int green = componentsRGB [1];
int red = componentsRGB [2];
colorComponentsYCbCr [0] = (int) (0.299 * red + 0.587 * green + 0.114 * blue);
colorComponentsYCbCr [1] = (int) (128-0.169 * red-0.331 * green + 0.500 * blue);
colorComponentsYCbCr [2] = (int) (128+0.500 * red - 0.419 * green - 0.081 * blue);
}
public static void getColoredCrPicture(BufferedImage image)
{
File f = null;
// get width and height
int width = image.getWidth();
int height = image.getHeight();
for (int y = 0; y<height; y++)
{
for (int x = 0; x<width; x++)
{
int color = image.getRGB(x, y);
getRGBComponents(color);
convertRGB2YCbCr(colorComponentsRGB);
int Y = colorComponentsYCbCr[0];
int Cb = colorComponentsYCbCr[1];
int Cr = colorComponentsYCbCr[2];
Y = 0;
Cb = 0;
int p = (Y << 24) | (Cb << 16) | (Cr<<8);
image.setRGB(x, y, p);
}
}
try
{
f = new File("/Users/MAC/Documents/workspace/ColorConverter/src/outputX.jpg");
ImageIO.write(image, "jpg", f);
}
catch(IOException e)
{
System.out.println(e);
}
}
public static void getColoredCbPicture(BufferedImage image)
{
File f = null;
// get width and height
int width = image.getWidth();
int height = image.getHeight();
for (int y = 0; y<height; y++)
{
for (int x = 0; x<width; x++)
{
int color = image.getRGB(x, y);
getRGBComponents(color);
convertRGB2YCbCr(colorComponentsRGB);
int Y = colorComponentsYCbCr[0];
int Cb = colorComponentsYCbCr[1];
int Cr = colorComponentsYCbCr[2];
Y = 0;
Cr = 0;
int p = (Y << 24) | (Cb<< 16) | (Cr <<8);
image.setRGB(x, y, p);
}
}
try
{
f = new File("/Users/MAC/Documents/workspace/ColorConverter/src/outputCb.jpg");
ImageIO.write(image, "jpg", f);
System.out.println("WRITE Status: OK");
}
catch(IOException e)
{
System.out.println(e);
}
}
public static BufferedImage loadPicture()
{
File f = null;
BufferedImage img = null;
// read Image
try
{
f = new File("/Users/MAC/Documents/workspace/ColorConverter/src/VILLA.JPG");
img = ImageIO.read(f);
System.out.println("READ Status: OK");
getColoredCbPicture(img);
}
catch(IOException e)
{
System.out.println(e);
}
return img;
}
public static void main(String[] args)
{
BufferedImage image = null;
loadPicture();
getColoredCbPicture(image);
}
}
It sounds like what you are looking to do is take an RGB image, convert it to YCbCr and display each of the three channels in YCbCr as a separate RGB image.
You already have code that converts from RGB to YCbCr . You will also need code that will do the reverse conversion so you can go from YCbCr to RGB.
You will want to use this same logic, but actually create three Y'CrCb images: (Y, 0, 0), (0, Cb, 0) and (0, 0, Cr). Then convert each of these three images to RGB. These three images will be an RGB representation of each of the three YCbCr channels.

applying window level functionality on CT images in DIOCM

I am trying to implement window level functionality( To apply bone, brain, lung etc on CT) for DICOM images in my application and implemented formula as per the DICOM specification.
I am changing pixel values based on below formula and creating a new image, but images are becoming blank. What am doing wrong and is this correct way to do this. Please help :(:( Thanks
BufferedImage image = input image;
double w = 2500; // Window width
double c = 500; // window Center
double ymin = 0;
double ymax = 255;
double x = 0;
double y = 0;
double slope = dicomObject.get(Tag.RescaleSlope).getFloat(true);
double intercept = dicomObject.get(Tag.RescaleIntercept).getFloat(true);
int width = image.getWidth();
int height = image.getHeight();
double val = c - 0.5 - (w - 1) / 2;
double val2 = c - 0.5 + (w - 1) / 2;
for (int m = 0; m < height; m++) {
for (int n = 0; n < width; n++) {
int rgb = image.getRGB(n, m);
int valrgb = image.getRGB(n, m);
int a = (0xff000000 & valrgb) >>> 24;
int r = (0x00ff0000 & valrgb) >> 16;
int g = (0x0000ff00 & valrgb) >> 8;
int b = (0x000000ff & valrgb);
x = a + r + g + b;
if (x <= val)
y = ymin;
else if (x > val2)
y = ymax;
else {
y = ((x - (c - 0.5)) / (w - 1) + 0.5) * (ymax - ymin)+ ymin;
}
y = y * slope + intercept;
rgb = (int) y;
image.setRGB(n, m, rgb);
}
}
String filePath = "out put fileName";
ImageIO.write(image, "jpeg", new File(filePath));
First of all whats in your BufferedImage image ?
There are three steps you want to take from raw (decopressed) pixel data:
Get stored values - apply BitsAllocated, BitsStored, HighBit transformation. (I guess you image already passed that level)
Get modality values - thats your Slope, Intercept transformation. Ofter this transformation, your data will be in Hounsfield Units for CT.
Then you apply WW/WL (Value Of Interest) transformation, which will transform this window of walues into grayscale color space.
EDIT:
You've got to tell me where did you get "input image" from? After decompression pixel data should be in a byte array of size byte[width*height*2] (for CT Image BitsAllocated is always 16, thus *2). You can get stored values like this:
ushort code = (ushort)((pixel[0] + (pixel[1] << 8)) & (ushort)((1<<bitsStored) - 1));
int value = TwosComplementDecode(code);

Comparing images to find duplicates

I have a few (38000) picture/video files in a folder. Approximately 40% of these are duplicates which I'm trying to get rid of. My question is, how can I tell if 2 files are identical? So far I tried to use a SHA1 of the files but it turns out that many duplicates files had different hashes. This is the code I was using:
public static String getHash(File doc) {
MessageDigest md = null;
try {
md = MessageDigest.getInstance("SHA1");
FileInputStream inStream = new FileInputStream(doc);
DigestInputStream dis = new DigestInputStream(inStream, md);
BufferedInputStream bis = new BufferedInputStream(dis);
while (true) {
int b = bis.read();
if (b == -1)
break;
}
inStream.close();
dis.close();
bis.close();
} catch (NoSuchAlgorithmException | IOException e) {
e.printStackTrace();
}
BigInteger bi = new BigInteger(md.digest());
return bi.toString(16);
}
Can I modify this in any way? Or will I have to use a different method?
As outlined above duplicate detection can be based on a hash. However, if you want to have near duplicate detection, which means that you are searching for images that basically show the same things, but have been scaled, rotated, etc. you might need a content based image retrieval approach. There's LIRE (https://code.google.com/p/lire/), a Java library for that, and you'll find the "SimpleApplication" in the Download section. What you then can do is to
Index the first image
go to the next image I
Search for I in the index
If there are results with a score below a threshold, then mark them as duplicate
Index I
Go to (2)
Students of mine did it, it worked well, but I don't have the source code at hand. But rest assured, it's just a few lines and the simple application will get you started.
Besides using hash, if your duplicates have different sizes (because they were resized), you could compare pixel by pixel (maybe not the entire image but a sub-section of the image).
This may depend on the image format but you could compare by comparing the height and width and then go pixel by pixel using the RGB code. To make it more efficient you can decide a threshold of comparison. For example:
public class Main {
public static void main(String[] args) throws IOException {
ImageChecker i = new ImageChecker();
BufferedImage one = ImageIO.read(new File("D:/Images/460249177.jpg"));
BufferedImage two = ImageIO.read(new File("D:/Images/460249177a.jpg"));
if(one.getWidth() + one.getHeight() >= two.getWidth() + two.getHeight()) {
i.setOne(one);
i.setTwo(two);
} else {
i.setOne(two);
i.setTwo(one);
}
System.out.println(i.compareImages());
}
}
public class ImageChecker {
private BufferedImage one;
private BufferedImage two;
private double difference = 0;
private int x = 0;
private int y = 0;
public ImageChecker() {
}
public boolean compareImages() {
int f = 20;
int w1 = Math.min(50, one.getWidth() - two.getWidth());
int h1 = Math.min(50, one.getHeight() - two.getHeight());
int w2 = Math.min(5, one.getWidth() - two.getWidth());
int h2 = Math.min(5, one.getHeight() - two.getHeight());
for (int i = 0; i <= one.getWidth() - two.getWidth(); i += f) {
for (int j = 0; j <= one.getHeight() - two.getHeight(); j += f) {
compareSubset(i, j, f);
}
}
one = one.getSubimage(Math.max(0, x - w1), Math.max(0, y - h1),
Math.min(two.getWidth() + w1, one.getWidth() - x + w1),
Math.min(two.getHeight() + h1, one.getHeight() - y + h1));
x = 0;
y = 0;
difference = 0;
f = 5;
for (int i = 0; i <= one.getWidth() - two.getWidth(); i += f) {
for (int j = 0; j <= one.getHeight() - two.getHeight(); j += f) {
compareSubset(i, j, f);
}
}
one = one.getSubimage(Math.max(0, x - w2), Math.max(0, y - h2),
Math.min(two.getWidth() + w2, one.getWidth() - x + w2),
Math.min(two.getHeight() + h2, one.getHeight() - y + h2));
f = 1;
for (int i = 0; i <= one.getWidth() - two.getWidth(); i += f) {
for (int j = 0; j <= one.getHeight() - two.getHeight(); j += f) {
compareSubset(i, j, f);
}
}
System.out.println(difference);
return difference < 0.1;
}
public void compareSubset(int a, int b, int f) {
double diff = 0;
for (int i = 0; i < two.getWidth(); i += f) {
for (int j = 0; j < two.getHeight(); j += f) {
int onepx = one.getRGB(i + a, j + b);
int twopx = two.getRGB(i, j);
int r1 = (onepx >> 16);
int g1 = (onepx >> 8) & 0xff;
int b1 = (onepx) & 0xff;
int r2 = (twopx >> 16);
int g2 = (twopx >> 8) & 0xff;
int b2 = (twopx) & 0xff;
diff += (Math.abs(r1 - r2) + Math.abs(g1 - g2) + Math.abs(b1
- b2)) / 3.0 / 255.0;
}
}
double percentDiff = diff * f * f / (two.getWidth() * two.getHeight());
if (percentDiff < difference || difference == 0) {
difference = percentDiff;
x = a;
y = b;
}
}
public BufferedImage getOne() {
return one;
}
public void setOne(BufferedImage one) {
this.one = one;
}
public BufferedImage getTwo() {
return two;
}
public void setTwo(BufferedImage two) {
this.two = two;
}
}
You need to use aHash, pHash and best of both dHash algorithm for this.
I wrote a pure java library just for this few days back. You can feed it with directory path(includes sub-directory), and it will list the duplicate images in list with absolute path which you want to delete. Alternatively, you can use it to find all unique images in a directory too.
It used awt api internally, so can't be used for Android though. Since, imageIO has problem reading alot of new types of images, i am using twelve monkeys jar which is internally used.
https://github.com/srch07/Duplicate-Image-Finder-API
Jar with dependencies bundled internally can be downloaded from, https://github.com/srch07/Duplicate-Image-Finder-API/blob/master/archives/duplicate_image_finder_1.0.jar
The api can find duplicates among images of different sizes too.
You could convert your files with e.g. imagemagick convert to a format which has a canonical representation and as little metadata as possible. I guess I'd use PNM. So try something like this:
convert input.png pnm:- | md5sum -
If this does yield the same result for two files which compared different before, then metadata is in fact the source of your problem, and you can either use some command line approach like this, or update your code to read the image and compute the hash from the raw uncompressed data.
If, on the other hand, different files still compare different, then you have some changes to the actual image data. One possible cause might be the addition or removal of an alpha channel, particularly if you are dealing with PNG here. With JPEG, on the other hand, you'll likely have images uncompressed and then recompressed again, which will lead to slight modifications and data loss. JPEG is an inherently lossy codec, and any two images will likely differ unless they were created using the same application (or library), with the same settings and from the same input data. In that case you'll need to perform a fuzzy image matching. Tools like Geeqie can perform such things. If you want to do this yourself, you'll have a lot of work ahead of you, and should do some research up front.
It's been a long time so I should probably explain how I finally solved my problem. The real trick was to not use hashes to begin with and instead just compare the timestamps in the exif data. Given that these pictures were taken either by me of my wife it would have been quite unlikely for different files to have the same timestamp, hence this simpler solution was actually much more reliable.
You can check different percentage of two images through below method and if different percentage os below 10 then you can call it identical image:
private static 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 = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
diff += pixelDiff(img1.getRGB(x, y), img2.getRGB(x, y));
}
}
long maxDiff = 3L * 255 * 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);
}
// covert image to Buffered image through this method
public static BufferedImage toBufferedImage(Image img)
{
if (img instanceof BufferedImage)
{
return (BufferedImage) img;
}
// Create a buffered image with transparency
BufferedImage bimage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB);
// Draw the image on to the buffered image
Graphics2D bGr = bimage.createGraphics();
bGr.drawImage(img, 0, 0, null);
bGr.dispose();
// Return the buffered image
return bimage;
}
Get insight idea from this site : https://rosettacode.org/wiki/Percentage_difference_between_images#Kotlin
The question was asked long time ago. I have found the following link very useful, it has codes for all languages. https://rosettacode.org/wiki/Percentage_difference_between_images#Kotlin
Here is the code for Kotlin taken from the link
import java.awt.image.BufferedImage
import java.io.File
import javax.imageio.ImageIO
import kotlin.math.abs
fun getDifferencePercent(img1: BufferedImage, img2: BufferedImage): Double {
val width = img1.width
val height = img1.height
val width2 = img2.width
val height2 = img2.height
if (width != width2 || height != height2) {
val f = "(%d,%d) vs. (%d,%d)".format(width, height, width2, height2)
throw IllegalArgumentException("Images must have the same dimensions: $f")
}
var diff = 0L
for (y in 0 until height) {
for (x in 0 until width) {
diff += pixelDiff(img1.getRGB(x, y), img2.getRGB(x, y))
}
}
val maxDiff = 3L * 255 * width * height
return 100.0 * diff / maxDiff
}
fun pixelDiff(rgb1: Int, rgb2: Int): Int {
val r1 = (rgb1 shr 16) and 0xff
val g1 = (rgb1 shr 8) and 0xff
val b1 = rgb1 and 0xff
val r2 = (rgb2 shr 16) and 0xff
val g2 = (rgb2 shr 8) and 0xff
val b2 = rgb2 and 0xff
return abs(r1 - r2) + abs(g1 - g2) + abs(b1 - b2)
}
fun main(args: Array<String>) {
val img1 = ImageIO.read(File("Lenna50.jpg"))
val img2 = ImageIO.read(File("Lenna100.jpg"))
val p = getDifferencePercent(img1, img2)
println("The percentage difference is ${"%.6f".format(p)}%")
}

Categories