Here's what I'm trying to do:
Get an image that's the difference between two images.
Context:
I have 2 images (identical) but one is bigger than the other.
I superimpose the smaller one in the center of the bigger one.
I would like to retrieve an image that is the difference between the 2 images.
Expected outcome:
What code would you propose to do that in an efficient way?
Edit :
I didn't specify the way.
I work in a Graphics2D context with BufferedImage
Thank a lot Camickr !
It was very simple so far.
Here is the result :
...
BufferedImage finalCut = extractEdge(source, dest);
...
g2d.drawImage(buff, 100, 100, null);
...
public static BufferedImage extractEdge(BufferedImage imgBottom, BufferedImage imgTop)
{
BufferedImage buffer = new BufferedImage(imgBottom.getWidth(), imgBottom.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2dBuff = buffer.createGraphics();
g2dBuff.setComposite(AlphaComposite.getInstance(AlphaComposite.XOR));
g2dBuff.drawImage(imgBottom, 0, 0, null);
g2dBuff.drawImage(imgTop, (int) getCenter(0, imgBottom.getWidth(), imgTop.getWidth(), true), (int) getCenter(0, imgBottom.getHeight(), imgTop.getHeight(), true), null);
return buffer;
}
public static float getCenter(float startZone, float zoneLenght, float elementLenght, boolean round)
{
float s = startZone + (zoneLenght - elementLenght) / 2f;
return (round) ? Math.round(s) : s;
}
Final cut
Related
This question already has an answer here:
Rotate BufferedImage with transparent background
(1 answer)
Closed 2 years ago.
I want to rotate a BufferedImage by an angle in radians. I used the following code. matrixImage is a matrix of integers where foreground pixels have 1 as value while background pixels have 0 as value. The new BufferedImage is correctly rotated but the extra borders are black. The new image is bigger than the original one and the new parts are black. I want that all the background pixels of the new image are white. I tried the solution proposed at Rotate BufferedImage and remove black bound, but I noticed that during the rotation the image changes.
bufferedImage = matrix2BufferedImage(matrixImage);
AffineTransform transform = new AffineTransform();
transform.rotate(radians, bufferedImage.getWidth() / 2, bufferedImage.getHeight() / 2);
AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR);
bufferedImage = op.filter(bufferedImage, null);
I solved using the following code for rotating
private BufferedImage rotateImage(BufferedImage sourceImage, double angle) {
AffineTransform transform = new AffineTransform();
transform.rotate(angle, sourceImage.getWidth() / 2, sourceImage.getHeight() / 2);
AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR);
BufferedImage destImage = op.filter(sourceImage, null);
Graphics2D g2d = destImage.createGraphics();
g2d.drawRenderedImage(sourceImage, transform);
g2d.dispose();
return destImage;
}
Then, I binarised the buffered image with the following code
int value=binarized.getRGB(x,y);
if(value==0)
value=-1;
output[y][x] = ((0xFFFFFF & value) == 0xFFFFFF) ? (byte) 0 : 1;
Thanks for suggesting me the right post!
i have a java.util.List filled with java.io.File and i wan't to list them in a gui. To display them i wan't to show their name which is accessable by the method getName() of java.io.File. And i want to display their icon/image with which they are displayed for example on the desktop. To build this i am using the newest javaversion (Java8/1.8). So far i found one way to do get the image of any file which looks like this:
Icon icon = FileSystemView.getFileSystemView().getSystemIcon(pFile);
The problem about that is, that the returned icon is in a very small resolution(16x16) and i d like to display it in a bigger size. 80x80 would be perfect but doesn't have to be exactly it. 64x64 or smth like that would be fine, too. So i managed to resize the icon and stretch it but streatching 16x16 to 80x80 is not cool as you can imagine. There are to less pixels to get a good result.
I also found this tutorial but the first method shown in this tutorial doesn't work with Java8: http://www.rgagnon.com/javadetails/java-0439.html The method i am using right now is copied from there.
So is there any way to get a bigger sized icon like the one which is shown on the desktop of a specific file?
Thanks
Baschdi
private BufferedImage getBufferedImage(final File pFile)
throws FileNotFoundException {
Image icon = ShellFolder.getShellFolder(pFile).getIcon(true);
BufferedImage im = new BufferedImage(icon.getWidth(null),
icon.getHeight(null), BufferedImage.TYPE_INT_ARGB);
Graphics2D g = im.createGraphics();
g.drawImage(icon, 0, 0, null);
g.dispose();
int width = im.getWidth();
int height = im.getHeight();
System.out.println(width);
System.out.println(height);
final int maxHeigh = 79;
double scaleValue = 0;
if (height > width)
scaleValue = maxHeigh / height;
else
scaleValue = maxHeigh / width;
final int scaledWidth = (int) (im.getWidth() * scaleValue);
final int scaledHeigh = (int) (im.getHeight() * scaleValue);
BufferedImage resized = new BufferedImage(scaledWidth, scaledHeigh,
im.getType());
g = resized.createGraphics();
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.drawImage(im, 0, 0, scaledWidth, scaledHeigh, 0, 0, im.getWidth(),
im.getHeight(), null);
g.dispose();
return resized;
}
I imported the jdk7 and used the older function with ShellFolder. Works fine, even when running on java8. Thanks for the help :)
I have created a library called JIconExtractReloaded which can extract all icons sizes not only 32x32. Here is the link https://github.com/MrMarnic/JIconExtractReloaded.
Just write:
BufferedImage image = JIconExtract.getIconForFile(128,128,"C:\\Windows\\explorer.exe");
And you have the icon.
I am tryimg to set an image as background in JPanel and resize it to desired size.
This is MyPanel where i choose image and set it as backgorund:
public class MyPanel extends JPanel {
Image img;
public MyPanel(LayoutManager l) {
super(l);
JFileChooser fc = new JFileChooser();
int result = fc.showOpenDialog(null);
if (result == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
String sname = file.getAbsolutePath();
img = new ImageIcon(sname).getImage();
double xRatio = img.getWidth(null) / 400;
double yRatio = img.getHeight(null) / 400;
double ratio = (xRatio + yRatio) / 2;
img = img.getScaledInstance((int)(img.getWidth(null) / ratio), (int)(img.getHeight(null) / ratio), Image.SCALE_SMOOTH);
}
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(img, 0, 0, Color.WHITE, null);
}
}
And this is my frame:
public class MyFrame extends JFrame {
public MyFrame () {
initUI();
}
private void initUI() {
MyPanel pnl = new MyPanel(null);
add(pnl);
setSize(600, 600);
setTitle("My component");
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
MyFrame ex = new MyFrame ();
ex.setVisible(true);
}
});
}
}
The problem is the image doesn't show at first. It shows when i for example change frame size a little.
Like that:
You are loading your image asynchronously.
The ImageIcon(String) constructor uses Toolkit.getImage internally, which is a hold-over from the 1990s, when many home Internet connections were so slow that it made sense to always load images in a background thread.
Since the image is loading in the background, img.getWidth(null) might return the image's size, or it might return -1. The documentation explains this.
So, how do you wait until the image has been loaded in that background thread?
Normally, you would observe the image's progress, using an ImageObserver. It just so happens that all AWT Components, and by extension, all Swing JComponents, implement ImageObserver. So you have an ImageObserver object: your MyPanel instance.
So, instead of passing null to all those methods, you would pass your MyPanel instance—that is, this:
double xRatio = img.getWidth(this) / 400;
double yRatio = img.getHeight(this) / 400;
And:
g.drawImage(img, 0, 0, Color.WHITE, this);
However… this will properly track the image's loading progress, but still doesn't guarantee that img.getWidth will return a positive value at the time that you call it.
To guarantee that an image is fully loaded immediately, you can replace the use of ImageIcon with ImageIO.read:
File file = fc.getSelectedFile();
try {
img = ImageIO.read(file);
} catch (IOException e) {
throw new RuntimeException("Could not load \"" + file + "\"", e);
}
This is different from using ImageIcon and Toolkit, because it doesn't just return an Image, it returns a BufferedImage, which is a type of Image that is guaranteed to be fully loaded and present in memory—no background loading to worry about.
Since a BufferedImage is already loaded, it has some additional methods which don't need an ImageObserver, in particular getWidth and getHeight methods which don't require an argument:
BufferedImage bufferedImg = (BufferedImage) img;
double xRatio = bufferedImg.getWidth() / 400.0;
double yRatio = bufferedImg.getHeight() / 400.0;
Notice I changed 400 to 400.0. This is because dividing one int by another int results in integer arithmetic in Java. For instance, 200 / 400 returns zero, because 200 and 400 are both int values. 5 / 2 produces the int value 2.
However, if either or both numbers are doubles, Java treats the entire thing as a double expression. So, (double) 200 / 400 returns 0.5. The presence of a decimal point in a sequence of digits indicates a double value, so 200.0 / 400 and 200 / 400.0 (and, of course, 200.0 / 400.0) will also return 0.5.
Finally, there is the issue of scaling. I recommend reading MadProgrammer's answer, and in particular, the java.net article to which his answer links, The Perils of Image.getScaledInstance().
The short version is that Image.getScaledInstance is another hold-over from the 1990s, and doesn't do a very good job of scaling. There are two better options:
Draw your image into a new image, and let the drawImage method handle the scaling, using the Graphics2D object's RenderingHints.
Use an AffineTransformOp on your image to create a new, scaled image.
Method 1:
BufferedImage scaledImage = new BufferedImage(
img.getColorModel(),
img.getRaster().createCompatibleWritableRaster(newWidth, newHeight),
false, new Properties());
Graphics g = scaledImage.createGraphics();
g.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_SPEED);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
g.drawImage(img, 0, 0, newWidth, newHeight, null);
g.dispose();
Method 2:
RenderingHints hints = new RenderingHints();
hints.put(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_SPEED);
hints.put(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
AffineTransform transform = AffineTransform.getScaleInstance(
(double) newWidth / img.getWidth(),
(double) newHeight / img.getHeight());
BufferedImageOp op = new AffineTransformOp(transform, hints);
BufferedImage scaledImage = op.filter(img, null);
You may want to alter the RenderingHints values, based on your own preferred trade-off of speed versus quality. They're all documented in the RenderingHints class.
You have to invoke setVisible(true):
public MyFrame () {
initUI();
setVisible(true);
}
Java ImageIO correctly displays this black & white image http://www.jthink.net/jaikoz/scratch/black.gif but when I try and resize it using this code
public static BufferedImage resize2D(Image srcImage, int size)
{
int w = srcImage.getWidth(null);
int h = srcImage.getHeight(null);
// Determine the scaling required to get desired result.
float scaleW = (float) size / (float) w;
float scaleH = (float) size / (float) h;
MainWindow.logger.finest("Image Resizing to size:" + size + " w:" + w + ":h:" + h + ":scaleW:" + scaleW + ":scaleH" + scaleH);
//Create an image buffer in which to paint on, create as an opaque Rgb type image, it doesn't matter what type
//the original image is we want to convert to the best type for displaying on screen regardless
BufferedImage bi = new BufferedImage(size, size, BufferedImage.TYPE_INT_RGB);
// Set the scale.
AffineTransform tx = new AffineTransform();
tx.scale(scaleW, scaleH);
// Paint image.
Graphics2D g2d = bi.createGraphics();
g2d.setComposite(AlphaComposite.Src);
g2d.drawImage(srcImage, tx, null);
g2d.dispose();
return bi;
}
I just end up with a black image. Im trying to make the image smaller (a thumbnail) but even if I resize it larger for test purposes it still ends up as a black square.
Other images resize okay, anyone know what is the problem with the gif/and or Java Bug
Here is the string representation of the ColorModel of the linked image when loaded through ImageIO:
IndexColorModel: #pixelBits = 1 numComponents = 4 color space = java.awt.color.ICC_ColorSpace#1572e449 transparency = 2 transIndex = 1 has alpha = true isAlphaPre = false
If I understand this correctly, you have one bit per pixel, where a 0 bit is opaque black and a 1 bit is transparent. Your BufferedImage is initially all black, so drawing a mixture of black and transparent pixels onto it will have no effect.
Although you are using AlphaComposite.Src this will not help as the R/G/B values for the transparent palette entry read as zero (I am not sure whether this is encoded in the GIF or just the default in the JDK.)
You can work around it by:
Initializing the BufferedImage with all-white pixels
Using AlphaComposite.SrcOver
So the last part of your resize2D implementation would become:
// Paint image.
Graphics2D g2d = bi.createGraphics();
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, size, size);
g2d.setComposite(AlphaComposite.SrcOver);
g2d.drawImage(srcImage, tx, null);
Try this:
BufferedImage bi = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
That makes it work. Of course, the question is why..?
I am using Java AWT for scaling a JPEG image, to create thumbnails. The code works fine when the image has a normal sampling factor ( 2x2,1x1,1x1 )
However, an image which has this sampling factor ( 1x1, 1x1, 1x1 ) creates problem when scaled. The colors get corrupted though the features are recognizable.
The original and the thumbnail:
alt text http://otherplace.in/thumb1.jpg
The code I am using is roughly equivalent to:
static BufferedImage awtScaleImage(BufferedImage image,
int maxSize, int hint) {
// We use AWT Image scaling because it has far superior quality
// compared to JAI scaling. It also performs better (speed)!
System.out.println("AWT Scaling image to: " + maxSize);
int w = image.getWidth();
int h = image.getHeight();
float scaleFactor = 1.0f;
if (w > h)
scaleFactor = ((float) maxSize / (float) w);
else
scaleFactor = ((float) maxSize / (float) h);
w = (int)(w * scaleFactor);
h = (int)(h * scaleFactor);
// since this code can run both headless and in a graphics context
// we will just create a standard rgb image here and take the
// performance hit in a non-compatible image format if any
Image i = image.getScaledInstance(w, h, hint);
image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
g.drawImage(i, null, null);
g.dispose();
i.flush();
return image;
}
(Code courtesy of this page )
Is there a better way to do this?
Here's a test image with sampling factor of [ 1x1, 1x1, 1x1 ].
I believe the problem is not the scaling, but your use of an incompatible color model ("image type") when constructing your BufferedImage.
Creating decent thumbnails in Java is surprisingly hard. Here's a detailed discussion.