I'm trying to write a code that masks red and blue channel from the image inputed. I have retrieved R, G, B values but however stuck up in proceeding further. Can anyone help me in this please?
public class Green {
public static void main(String args[]) throws IOException {
BufferedImage bi = ImageIO.read(new File("image.jpg"));
for (int x = 0; x <= bi.getWidth(); x++) {
for (int y = 0; y <= bi.getHeight(); y++) {
int pixelCol = bi.getRGB(x, y);
int r = (pixelCol >> 16) & 0xff;
int b = pixelCol & 0xff;
int g = (pixelCol >> 8) & 0xff;
int px = 0;
px = (px | (g << 8));
bi.setRGB(x, y, px);
}
}
}
}
Some remarks:
Use an IDE (Integrated Development Environment) like Eclipse or NetBeans.
Use < instead of <= as condition in your for loops.
Use code formatting (that is a feature of the IDE)
For clearness, order r, g and b.
int r = (color >> 16) & 0xff;
int g = (color >> 8) & 0xff;
int b = (color >> 0) & 0xff;
Since you said you were stuck, the only thing left to do is save the manipulated image:
ImageIO.write(bi, "JPG", new File("green.jpg"));
A little trick to perform the mask quickly is this:
bi.setRGB(x, y, bi.getRGB(x, y) & 0xff00ff00);
So, the clean working code should be this:
public class Green
{
public static void main(String args[]) throws IOException
{
/* Read the image */
BufferedImage bi= ImageIO.read(new File("image.jpg"));
/* Loop through all the pixels */
for (int x=0; x < bi.getWidth(); x++)
{
for (int y = 0; y < bi.getHeight(); y++)
{
/* Apply the green mask */
bi.setRGB(x, y, bi.getRGB(x, y) & 0xff00ff00);
}
}
/* Save the image */
ImageIO.write(bi, "JPG", new File("green_mask.jpg"));
}
}
In addition to copying and scaling images, the Java 2D API also filter an image. Filtering is drawing or producing a new image by applying an algorithm to the pixels of the source image.
Image filters can be applied by using the following method:
void Graphics2D.drawImage(BufferedImage img,
BufferedImageOp op,
int x, int y)
The BufferedImageOp parameter implements the filter.
See this document for Image filter examples: http://ptgmedia.pearsoncmg.com/images/9780132413930/samplechapter/0132413930_CH08.pdf
Related
For my project, I'm able to print textures on objects. As soon I use nicer textures that use a color palette higher than 256 it will turn black or invisible...
Is anyone able to help me with this issue? Right now this is my code to transfer the .png into a useable texture:
public static Background getIndexedImage(int id, File file) throws IOException {
BufferedImage image = ImageIO.read(file);
List<Integer> paletteList = new LinkedList<>();
paletteList.add(0);
int width = image.getWidth();
int height = image.getHeight();
byte[] pixels = new byte[width * height];
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int rgb = image.getRGB(x, y);
int red = rgb >> 16 & 0xff;
int green = rgb >> 8 & 0xff;
int blue = rgb & 0xff;
int alpha = rgb & 0xff;
rgb = red << 16 | green << 8 | blue;
if (alpha == 255) {
rgb = 0;
}
int index = paletteList.indexOf(rgb);
if (index == -1) {
if (paletteList.size() < 256) {
index = paletteList.size();
paletteList.add(rgb);
} else {
throw new IllegalArgumentException("The target image has more than 255 color in the palette "+id);
}
}
pixels[x + y * width] = (byte) index;
}
}
int[] palette = new int[paletteList.size()];
final AtomicInteger index = new AtomicInteger(0);
for (int pallet = 0; pallet < paletteList.size(); pallet++) {
palette[index.getAndIncrement()] = paletteList.get(pallet);
}
return new Background(width, height, palette, pixels);
}
I'm trying to create a program that, when selecting an image, reverses the colors of the image.
But when I run the code, my BufferedImage changes the RGB I assigned earlier.
I leave you the code that reverses the image.
image is a static BufferedImage.
public static void saveImage(File input, File output) throws IOException{
image = ImageIO.read(input);
for (int x = 0; x < image.getWidth(); x++) {
for (int y = 0; y < image.getHeight(); y++) {
boolean isTransparent = isTransparent(x, y);
if (!isTransparent) {
Color color = new Color(image.getRGB(x, y));
int r = 255 - color.getRed();
int g = 255 - color.getGreen();
int b = 255 - color.getBlue();
color = new Color(r, g, b);
int rgb = color.getRGB();
image.setRGB(x, y, rgb);
System.out.println(rgb+" --> "+image.getRGB(x, y));
}
}
}
ImageIO.write(image, "png", output);
}
public static boolean isTransparent(int x, int y) {
int pixel = image.getRGB(x, y);
return (pixel >> 24) == 0x00;
}
This is how I am doing to convert from RGB to CMYK using the more "correct" way - i.e using an ICC color profile.
// Convert RGB to CMYK with level shift (minus 128)
private void RGB2CMYK(int[] rgb, float[][] C, float[][] M, float[][] Y, float[][] K, int imageWidth, int imageHeight) throws Exception {
ColorSpace instance = new ICC_ColorSpace(ICC_Profile.getInstance(JPEGWriter.class.getResourceAsStream(pathToCMYKProfile)));
float red, green, blue, cmyk[];
//
for(int i = 0, index = 0; i < imageHeight; i++) {
for(int j = 0; j < imageWidth; j++, index++) {
red = ((rgb[index] >> 16) & 0xff)/255.0f;
green = ((rgb[index] >> 8) & 0xff)/255.0f;
blue = (rgb[index] & 0xff)/255.0f;
cmyk = instance.fromRGB(new float[] {red, green, blue});
C[i][j] = cmyk[0]*255.0f - 128.0f;
M[i][j] = cmyk[1]*255.0f - 128.0f;
Y[i][j] = cmyk[2]*255.0f - 128.0f;
K[i][j] = cmyk[3]*255.0f - 128.0f;
}
}
}
My problem is: it's prohibitively slow given a large image. In one case, it took about 104s instead of the usual 2s for me to write the data as a JPEG image. It turns out the above transform is the most time-consuming part.
I am wondering if there is any way to make it faster. Note: I am not going to use the cheap conversion algorithm one can find form the web.
Update: following haraldK's suggestion, here is the revised version:
private void RGB2CMYK(int[] rgb, float[][] C, float[][] M, float[][] Y, float[][] K, int imageWidth, int imageHeight) throws Exception {
if(cmykColorSpace == null)
cmykColorSpace = new ICC_ColorSpace(ICC_Profile.getInstance(JPEGWriter.class.getResourceAsStream(pathToCMYKProfile)));
DataBuffer db = new DataBufferInt(rgb, rgb.length);
WritableRaster raster = Raster.createPackedRaster(db, imageWidth, imageHeight, imageWidth, new int[] {0x00ff0000, 0x0000ff00, 0x000000ff}, null);
ColorSpace sRGB = ColorSpace.getInstance(ColorSpace.CS_sRGB);
ColorConvertOp cco = new ColorConvertOp(sRGB, cmykColorSpace, null);
WritableRaster cmykRaster = cco.filter(raster, null);
byte[] o = (byte[])cmykRaster.getDataElements(0, 0, imageWidth, imageHeight, null);
for(int i = 0, index = 0; i < imageHeight; i++) {
for(int j = 0; j < imageWidth; j++) {
C[i][j] = (o[index++]&0xff) - 128.0f;
M[i][j] = (o[index++]&0xff) - 128.0f;
Y[i][j] = (o[index++]&0xff) - 128.0f;
K[i][j] = (o[index++]&0xff) - 128.0f;
}
}
}
Update: I also found out it's much faster to do filter on a BufferedImage instead of a Raster. See this post: ARGB int array to CMYKA byte array convertion
You should get rid of the memory allocation within the innermost loop. new is a prohibitively expensive operation. Also it might kick the garbage collector into action, which adds a further penality.
If you can affort the memory consumption, you could create a lookup table:
private void RGB2CMYK(int[] rgb, float[][] C, float[][] M, float[][] Y, float[][] K, int imageWidth, int imageHeight) throws Exception {
ColorSpace cs = new ICC_ColorSpace(...);
int[] lookup = createRGB2CMYKLookup(cs);
for(int y = 0, index = 0; y < imageHeight; y++) {
for(int x = 0; x < imageWidth; x++, index++) {
int cmyk = lookup[rgb[index]];
C[y][x] = ((cmyk >> 24) & 255) - 128F;
M[y][x] = ((cmyk >> 16) & 255) - 128F;
Y[y][x] = ((cmyk >> 8) & 255) - 128F;
K[y][x] = ((cmyk ) & 255) - 128F;
}
}
}
static int[] createRGB2CMYKLookup(ColorSpace cs) {
int[] lookup = new int[16 << 20]; // eats 16m times 4 bytes = 64mb
float[] frgb = new float[3];
float fcmyk[];
for (int rgb=0; rgb<lookup.length; ++rgb) {
frgb[0] = ((rgb >> 16) & 255) / 255F;
frgb[1] = ((rgb >> 8) & 255) / 255F;
frgb[2] = ((rgb ) & 255) / 255F;
fcmyk = cs.fromRGB(frgb);
int c = (int) (fcmyk[0] * 255F);
int m = (int) (fcmyk[1] * 255F);
int y = (int) (fcmyk[2] * 255F);
int k = (int) (fcmyk[3] * 255F);
int icmyk = (c << 24) | (m << 16) | (y << 8) | k;
}
return lookup;
}
Now this may actually worsen performance for small images as it is. It will only help if you can re-use the lookup table for multiple images, but as your example looks you're using actually the same ICC profile over and over. Thus you could cache the lookup table and pay its initialization cost only once:
static int[] lookup;
static {
ColorSpace cs = new ICC_ColorSpace(...);
lookup = createRGB2CMYKLookup(cs);
}
// convert always using (the same) lookup table
private void RGB2CMYK(int[] rgb, float[][] C, float[][] M, float[][] Y, float[][] K, int imageWidth, int imageHeight) throws Exception {
for(int y = 0, index = 0; y < imageHeight; y++) {
for(int x = 0; x < imageWidth; x++, index++) {
int cmyk = lookup[rgb[index]];
C[y][x] = ((cmyk >> 24) & 255) - 128F;
M[y][x] = ((cmyk >> 16) & 255) - 128F;
Y[y][x] = ((cmyk >> 8) & 255) - 128F;
K[y][x] = ((cmyk ) & 255) - 128F;
}
}
}
You should probably use ColorConvertOp. It uses optimized native code on most platforms, and supports ICC profile transforms.
Not sure how fast it will work when using float based Rasters, but it does the job.
Something like:
ICC_Profile cmyk = ...;
ICC_Profile sRGB = ...;
ColorConvertOp cco = new ColorConvertOp(sRGB, cmyk);
Raster rgbRaster = ...;
WritableRaster cmykRaster = cco.filter(rgbRaster, null);
// Or alternatively, if you have a BufferedImage input
BufferedImage rgbImage = ...;
BufferedImage cmykImage = cco.filter(rgbImage, null);
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)}%")
}
This question already has an answer here:
How to scan the screen for a specific color/image in java?
(1 answer)
Closed 9 years ago.
I'm not sure where to start on this one, but is there a way I can use Java to scan an image row by row for a specific color, and pass all of the positions into and ArrayList?
Can you? yes. Here's how:
ArrayList<Point> list = new ArrayList<Point>();
BufferedImage bi= ImageIO.read(img); //Reads in the image
//Color you are searching for
int color= 0xFF00FF00; //Green in this example
for (int x=0;x<width;x++)
for (int y=0;y<height;y++)
if(bi.getRGB(x,y)==color)
list.add(new Point(x,y));
Try using a PixelGrabber. It accepts an Image or ImageProducer.
Here's an example adapted from the documentation:
public void handleSinglePixel(int x, int y, int pixel) {
int alpha = (pixel >> 24) & 0xff;
int red = (pixel >> 16) & 0xff;
int green = (pixel >> 8) & 0xff;
int blue = (pixel ) & 0xff;
// Deal with the pixel as necessary...
}
public void handlePixels(Image img, int x, int y, int w, int h) {
int[] pixels = new int[w * h];
PixelGrabber pg = new PixelGrabber(img, x, y, w, h, pixels, 0, w);
try {
pg.grabPixels();
} catch (InterruptedException e) {
System.err.println("interrupted waiting for pixels!");
return;
}
if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
System.err.println("image fetch aborted or errored");
return;
}
for (int j = 0; j < h; j++) {
for (int i = 0; i < w; i++) {
handleSinglePixel(x+i, y+j, pixels[j * w + i]);
}
}
}
In your case, you would have:
public void handleSinglePixel(int x, int y, int pixel) {
int target = 0xFFABCDEF; // or whatever
if (pixel == target) {
myArrayList.add(new java.awt.Point(x, y));
}
}