I'm trying to use the RGBImageFilter to produce a simple transparency effect on an image, however during testing I found that all of my filtered images come out as blank with black backgound. I simplified my filter to a simple "No Operation" filter that just returns the RGB value that was passed in. Even this simplified NoOp filter produces a blank image. I've tried this with importing and exporting both JPG and PNG with the same affect. I've tried various example filters off the internet that produce the same all black image problem. Has anyone encountered this before? The "load status" and the "write status" below are both returning true so I know from MediaTracker that the original image is loading fully and that the produced image is writing successfully. Thanks in advance for any help!
Here is the code:
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.awt.image.FilteredImageSource;
import java.awt.image.ImageProducer;
import java.awt.image.RGBImageFilter;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
class NoOpFilter extends RGBImageFilter {
public NoOpFilter() {
canFilterIndexColorModel = true;
}
public int filterRGB(int x, int y, int rgb) {
return rgb;
}
public static void main(String[] args) throws Exception{
String originalImageLocation = args[0];
String baseFileName = args[1];
String directory = args[2];
ImageIcon originalImage = new ImageIcon(Toolkit.getDefaultToolkit().getImage(originalImageLocation));
NoOpFilter filter2 = new NoOpFilter();
ImageProducer ip = new FilteredImageSource(originalImage.getImage().getSource(), filter2);
Image filteredImage = Toolkit.getDefaultToolkit().createImage(ip);
System.out.println("Load Status: " + ( originalImage.getImageLoadStatus() == java.awt.MediaTracker.COMPLETE));
BufferedImage bim = new BufferedImage( originalImage.getIconWidth(),originalImage.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = bim.createGraphics();
g2.drawImage(filteredImage, 0, 0, null);
g2.dispose();
File file = new File(directory + File.separator + baseFileName + ".png");
file.createNewFile();
boolean status = ImageIO.write(bim, "png", file);
System.out.print("write() status: " + status);
}
}
Here is the fix that worked for me. Right after creating the fileredImage above, it was necessary to actually add the filtered image to a JLabel and the JLabel to a JFrame in order to force it to be rendered to screen first before writing it into my buffer and saving to disk:
...
Image filteredImage = Toolkit.getDefaultToolkit().createImage(ip);
ImageIcon swingImage = new ImageIcon(filteredImage);
JFrame frame = new JFrame();
JLabel label = new JLabel(swingImage);
frame.add(label);
frame.setVisible(true);
frame.dispose();
BufferedImage bim = new BufferedImage( originalImage.getIconWidth(),originalImage.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = bim.createGraphics();
g2.drawImage(filteredImage, 0, 0, null);
g2.dispose();
File file = new File(directory + File.separator + baseFileName + ".png");
file.createNewFile();
boolean status = ImageIO.write(bim, "png", file);
System.out.print("write() status: " + status);
}
I don't quite yet understand why this can't be done completely off-screen like I wanted to do, however it was easy enough to dispose the JFrame right after opening it. A better solution would not require using UI components to do this since I don't really need anything displayed on screen in this case and just want the end product.
Thanks to Hovercraft for pointing me in the right direction.
You have to call prepareImage, then you don't have to use the JLabel-workaround.
Image filteredImage = Toolkit.getDefaultToolkit().createImage(ip);
Toolkit.getDefaultToolkit().prepareImage(filteredImage, -1, -1, null);
Related
I've written a program to modify images.
First, I get the image, and get its drawing context like this:
BufferedImage image;
try {
image = ImageIO.read(inputFile);
} catch (IOException ioe) { /* exception handling ... */ }
Graphics g = image.createGraphics();
And then I modify the image like this:
for (int x = 0; x < image.getWidth(); x++) {
for (int y = 0; y < image.getHeight(); y++) {
g.setColor( /* calculate color ... */ );
g.fillRect(x, y, 1, 1);
}
}
After I've finished modifying the image, I save the image like this:
try {
ImageIO.write(image, "PNG", save.getSelectedFile());
} catch (IOException ioe) { /* exception handling ... */ }
Now most of the time this works just fine.
However, when I tried recoloring this texture
to this
I get this instead:
Inside the debugger, though, the Graphics's color is the shade of pink I want it to be.
The comments seem to suggest that the image the user opens might have some color limitations, and since I'm drawing to the same image I'm reading from, my program has to abide by these limitations. The example image seems to be pretty grayscale-y, and apparently its bit depth is 8 bit. So maybe the pink I'm drawing on it is converted to grayscale, because the image has to stay 8-bit?
As suggested in the comments, the main problem here indeed is the wrong color model. When you load the original image, and print some information about it...
BufferedImage image = ImageIO.read(
new URL("https://i.stack.imgur.com/pSUFR.png"));
System.out.println(image);
it will say
BufferedImage#5419f379: type = 13 IndexColorModel: #pixelBits = 8 numComponents = 3 color space = java.awt.color.ICC_ColorSpace#7dc7cbad transparency = 1 transIndex = -1 has alpha = false isAlphaPre = false ByteInterleavedRaster: width = 128 height = 128 #numDataElements 1 dataOff[0] = 0
The IndexColorModel does not necessarily support all the colors, but only a subset of them. (Basically, the image supports only the colors that it "needs", which allows for a more compact storage).
The solution here is to convert the image into one that has the appropriate color model. A generic method for this is shown in the following example:
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
public class ImageColors
{
public static void main(String[] args) throws IOException
{
BufferedImage image = ImageIO.read(
new URL("https://i.stack.imgur.com/pSUFR.png"));
// This will show that the image has an IndexColorModel.
// This does not necessarily support all colors.
System.out.println(image);
// Convert the image to a generic ARGB image
image = convertToARGB(image);
// Now, the image has a DirectColorModel, supporting all colors
System.out.println(image);
Graphics2D g = image.createGraphics();
g.setColor(Color.PINK);
g.fillRect(50, 50, 50, 50);
g.dispose();
ImageIO.write(image, "PNG", new File("RightColors.png"));
}
public static BufferedImage convertToARGB(BufferedImage image)
{
BufferedImage newImage = new BufferedImage(
image.getWidth(), image.getHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics2D g = newImage.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
return newImage;
}
}
The idea is next,
user selects a pdf file, and then this file converted into an image and such an image is displayed in the application.
In the image the user can choose positions that wants to read from a pdf file, and when the finish with selection position in the background program reads the original pdf and text stored in a txt file.
It is important that the resulting image from pdf file is the same size as himself pdf file
The next code convert pdf to image. I use pdfrenderer-0.9.1.jar
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import javax.imageio.ImageIO;
import com.sun.pdfview.PDFFile;
import com.sun.pdfview.PDFPage;
public class Pdf2Image {
public static void main(String[] args) {
File file = new File("E:\\invoice-template-1.pdf");
RandomAccessFile raf;
try {
raf = new RandomAccessFile(file, "r");
FileChannel channel = raf.getChannel();
ByteBuffer buf = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
PDFFile pdffile = new PDFFile(buf);
// draw the first page to an image
int num=pdffile.getNumPages();
for(int i=0;i<num;i++)
{
PDFPage page = pdffile.getPage(i);
//get the width and height for the doc at the default zoom
int width=(int)page.getBBox().getWidth();
int height=(int)page.getBBox().getHeight();
Rectangle rect = new Rectangle(0,0,width,height);
int rotation=page.getRotation();
Rectangle rect1=rect;
if(rotation==90 || rotation==270)
rect1=new Rectangle(0,0,rect.height,rect.width);
//generate the image
BufferedImage img = (BufferedImage)page.getImage(
rect.width, rect.height, //width & height
rect1, // clip rect
null, // null for the ImageObserver
true, // fill background with white
true // block until drawing is done
);
ImageIO.write(img, "png", new File("E:/invoice-template-"+i+".png"));
}
}
catch (FileNotFoundException e1) {
System.err.println(e1.getLocalizedMessage());
} catch (IOException e) {
System.err.println(e.getLocalizedMessage());
}
}
}
Then the image is displayed to the user in JavaFX application in ImageView components.
Can you help me to get the exact position of the mouse, the mouse when the user selects a portion of the image from which you want to read the text in the pdf file?
With this code I read pdf file and get text from the set position, only I must to manually input position:( . I use pdfbox-1.3.1.jar.
I would like to position the client chooses to keep a picture in the list and read the text from the pdf file with all of these positions.
File file = new File("E:/invoice-template-1.pdf");
PDDocument document = PDDocument.load(file);
PDFTextStripperByArea stripper = new PDFTextStripperByArea();
stripper.setSortByPosition(true);
Rectangle rect1 = new Rectangle(38, 275, 15, 100);
Rectangle rect2 = new Rectangle(54, 275, 40, 100);
stripper.addRegion("row1column1", rect1);
stripper.addRegion("row1column2", rect2);
List allPages = document.getDocumentCatalog().getAllPages();
List<PDPage> pages = document.getDocumentCatalog().getAllPages();
int j = 0;
for (PDPage page : pages) {
stripper.extractRegions(page);
stripper.setSortByPosition(true);
List<String> regions = stripper.getRegions();
for (String region : regions) {
String text = stripper.getTextForRegion(region);
System.out.println("Region: " + region + " on Page " + j);
System.out.println("\tText: \n" + text);
}
For example,
in the next invoice, I want to select the 4 positions to export the text, and when you select the picture, the dimensions of keeping in the list, then go through the list and from those positions export text from pdf file.
I'm using something along the lines of this to do a (naive, apparently) check for the color space of JPEG images:
import java.io.*;
import java.awt.color.*;
import java.awt.image.*;
import javax.imageio.*;
class Test
{
public static void main(String[] args) throws java.lang.Exception
{
File f = new File(args[0]);
if (f.exists())
{
BufferedImage bi = ImageIO.read(f);
ColorSpace cs = bi.getColorModel().getColorSpace();
boolean isGrayscale = cs.getType() == ColorSpace.TYPE_GRAY;
System.out.println(isGrayscale);
}
}
}
Unfortunately this reports false for images that (visually) appear gray-only.
What check would do the right thing?
You can use this code:
File input = new File("inputImage.jpg");
BufferedImage image = ImageIO.read(input);
Raster ras = image.getRaster();
int elem = ras.getNumDataElements();
System.out.println("Number of Elems: " + elem);
If the number of elems returns 1, then its a greyscale image. If it returns 3, then its a color image.
the image looks like gray beacuse the r=g=b but actually it is a full color image, it has three channel r g b and the real gray image only have one channel
Why does combining images where BG is a JPEG cause unexpected results?
This is a follow-up to my answer in Overlaying of 2 images doesnt work properly. The source posted there (using a BG image created in memory) looks like this:
The BG image is on the left.
The FG image (a PNG with transparency) is in the middle.
The combined image is on the right.
So far, so good. But then the person who asked the question commented that if the BG was a JPEG, it failed. Thinking they were mistaken, I altered my example to encode the BG image to a JPEG. Now if I use BufferedImage.TYPE_INT_ARGB or BufferedImage.TYPE_INT_RGB for the final image I get what they were referring to:
TYPE_INT_ARGB
TYPE_INT_RGB
I expected the result to be the same as the original for at least one of those (more so the ARGB variant).
import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.net.URL;
import javax.imageio.ImageIO;
class CombineImages {
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
try {
URL urlImage1 =
new URL("http://i.stack.imgur.com/T5uTa.png");
// Load the FG image
Image fgImage = ImageIO.read(urlImage1);
int w = fgImage.getWidth(null);
int h = fgImage.getHeight(null);
// Create a non-trasparent BG image
BufferedImage bgImageTemp =
new BufferedImage(w,h,BufferedImage.TYPE_INT_RGB);
ByteArrayOutputStream baos =
new ByteArrayOutputStream();
ImageIO.write(bgImageTemp, "jpg", baos);
ByteArrayInputStream bais =
new ByteArrayInputStream(baos.toByteArray());
BufferedImage bgImageJpeg = ImageIO.read(bais);
int result = JOptionPane.showConfirmDialog(
null,
"Use a final image with transparency?",
"Transparency",
JOptionPane.YES_NO_OPTION);
int type = (result==JOptionPane.OK_OPTION ?
BufferedImage.TYPE_INT_ARGB :
BufferedImage.TYPE_INT_RGB);
// Create the final image
BufferedImage finalImage =
new BufferedImage(w,h,type);
Graphics2D g = finalImage.createGraphics();
g.drawImage(bgImageJpeg, w, h, null);
g.drawImage(fgImage, w, h, null);
g.dispose();
JPanel gui = new JPanel(new GridLayout(1,0,5,5));
gui.add(new JLabel(new ImageIcon(bgImageJpeg)));
gui.add(new JLabel(new ImageIcon(fgImage)));
gui.add(new JLabel(new ImageIcon(finalImage)));
JOptionPane.showMessageDialog(null, gui);
} catch (Exception e) {
e.printStackTrace();
}
}
};
SwingUtilities.invokeLater(r);
}
}
Looks like this is due to a typo.
In your referenced answer, the code that formed the combined image was
Graphics2D g = finalImage.createGraphics();
g.drawImage(bgImage, 0, 0, null);
g.drawImage(fgImage, 0, 0, null);
But in this question, it's been changed to,
Graphics2D g = finalImage.createGraphics();
g.drawImage(bgImageJpeg, w, h, null);
g.drawImage(fgImage, w, h, null);
The latter begins drawing at the "top-left corner", which happens to be the bottom-right corner of the images, so nothing is really drawn. The former, however, draws the entire images, as expected.
gui.repaint();
Try that after since you effectively painted the panel and components while constructing the joptionpane, but, even then because construction threading matching visibility invocation will not hold truth you should invoke the g.drawImage in a paint override method after joptionpane invocation anyhow.
It cannot draw something that does not yet actually exist onscreen, however there is tolerance for the call because it theoretically exists as a set of objects enough for the method.
I've been having a problem with my Java program. It's for resizing images. You drop it into a folder and run it, and it creates a new folder with the resized images. It works great on color, but it has a problem with grayscale. The images are converted, but they become lighter and more washed out, as if someone has messed with the curves or levels. All the input files and output files are sRGB color space jpegs, saved in RGB color mode. I have thousands of 50 megapixel film scans I'm trying to convert down to 15 megapixels or less. Any help or ideas anyone could offer would be most appreciated. The programs full code is below, it's about 130 lines. I have a feeling the problem may be in the toBufferedImage function but I'm lost as to what it could be.
package jpegresize;
import java.awt.*;
import java.awt.image.*;
import java.util.*;
import java.io.*;
import javax.imageio.*;
import javax.imageio.stream.*;
import javax.swing.*;
public class Main {
public static void main(String[] args) {
System.out.println("JPEGResize running . . .");
int max_side = 4096;
float quality = 0.9f;
if(args.length == 0) System.out.println("No maximum side resolution or compression quality arguments given, using default values.\nUsage: java -jar JPEGResize.jar <maximum side resolution in pixels> <quality 0 to 100 percent>");
if(args.length >= 1) max_side = Integer.parseInt(args[0]);
if(args.length >= 2) quality = Float.parseFloat(args[1]) / 100.0f;
System.out.println("Maximum side resolution: " + max_side);
System.out.println("Compression quality: " + (quality * 100) + "%");
File folder = new File(".");
File[] listOfFiles = folder.listFiles(new JPEGFilter());
for(int i = 0; i < listOfFiles.length; i++) {
System.out.println("Processing " + listOfFiles[i].getName() + " . . .");
resizeFile(listOfFiles[i].getName(), max_side, quality);
System.out.println("Saved /resized/" + listOfFiles[i].getName());
}
System.out.println("Operations complete.");
}
public static void resizeFile(String filename, int max_side, float quality) {
try
{
BufferedImage input_img = ImageIO.read(new File(filename));
double aspect_ratio = ((double)input_img.getWidth()) / ((double)input_img.getHeight());
int width, height;
if(input_img.getWidth() >= input_img.getHeight()) {
width = max_side;
height = (int)(((double)max_side) / aspect_ratio);
}
else {
width = (int)(((double)max_side) * aspect_ratio);
height = max_side;
}
Image scaled_img = input_img.getScaledInstance(width, height, Image.SCALE_SMOOTH);
BufferedImage output_img = toBufferedImage(scaled_img);
Iterator iter = ImageIO.getImageWritersByFormatName("jpeg");
ImageWriter writer = (ImageWriter)iter.next();
ImageWriteParam iwp = writer.getDefaultWriteParam();
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
iwp.setCompressionQuality(quality);
File doesDirExist = new File("resized/");
if(!doesDirExist.exists())
new File("resized").mkdir();
File file = new File("resized/" + filename);
FileImageOutputStream output = new FileImageOutputStream(file);
writer.setOutput(output);
IIOImage image = new IIOImage(output_img, null, null);
writer.write(null, image, iwp);
writer.dispose();
}
catch (IOException e)
{
e.printStackTrace();
}
}
// This method returns a buffered image with the contents of an image
public static BufferedImage toBufferedImage(Image image) {
if (image instanceof BufferedImage) {
return (BufferedImage)image;
}
// This code ensures that all the pixels in the image are loaded
image = new ImageIcon(image).getImage();
// Create a buffered image with a format that's compatible with the screen
BufferedImage bimage = null;
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
try {
// Determine the type of transparency of the new buffered image
int transparency = Transparency.OPAQUE;
// Create the buffered image
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gs.getDefaultConfiguration();
bimage = gc.createCompatibleImage(
image.getWidth(null), image.getHeight(null), transparency);
} catch (HeadlessException e) {
// The system does not have a screen
}
if (bimage == null) {
// Create a buffered image using the default color model
int type = BufferedImage.TYPE_INT_RGB;
bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);
}
// Copy image to buffered image
Graphics g = bimage.createGraphics();
// Paint the image onto the buffered image
g.drawImage(image, 0, 0, null);
g.dispose();
return bimage;
}
}
class JPEGFilter implements FilenameFilter {
public boolean accept(File dir, String name) {
return (name.toLowerCase().endsWith(".jpg")) || (name.toLowerCase().endsWith(".jpeg"));
}
}
If jdk's classes and methods are buggy, report the bug to oracle (oh! I wish I could go on saying to SUN..).
And, while the next release will correct the bug ;), try some work arounds, scaling image by yourself like proposed here.
Regards,
Stéphane
In your code, you assume jpeg are encoded in RGB, but that's not always the case. It's also possible to encode 8 bit gray scaled jpeg. So I suggest that you try this when building your BufferedImage, replace :
BufferedImage.TYPE_INT_RGB;
by
BufferedImage.TYPE_BYTE_GRAY;
and see if it works for those images.
If so, then you still have to find out a way to determine the encoding type to automatically change the type of BufferedImage color encoding to use, but you will be one stop closer.
Regards,
Stéphane