I am using Java with swing in FSE mode. I want to load a completely black-and-white image into binary format (a 2d array preferably) and use it for mask-based per-pixel collision detection. I don't even know where to start here, I've been researching for the past hour and haven't found anything relevant.
Just read it into a BufferedImage using ImageIO#read() and get the individual pixels by BufferedImage#getRGB(). A value of 0xFFFFFFFF is white and the remnant is color. Assuming that you want to represent white as byte 0 and color (black) as byte 1, here's a kickoff example:
BufferedImage image = ImageIO.read(new File("/some.jpg"));
byte[][] pixels = new byte[image.getWidth()][];
for (int x = 0; x < image.getWidth(); x++) {
pixels[x] = new byte[image.getHeight()];
for (int y = 0; y < image.getHeight(); y++) {
pixels[x][y] = (byte) (image.getRGB(x, y) == 0xFFFFFFFF ? 0 : 1);
}
}
See also:
The Java Tutorials - 2D Graphics - Working with images
If you're reading the image from a URL, it will already be in a binary format. Just download the data and ignore the fact that it's an image. The code which is involved in download it won't care, after all. Assuming you want to write it to a file or something similar, just open the URLConnection and open the FileOutputStream, and repeatedly read from the input stream from the web, writing the data you've read to the output stream.
You can also use ImageIO if you are not downloading it from some resource.
Related
Nearest neighbor scaling works: The entire picture stays intact when I use TYPE_NEAREST_NEIGHBOR.
Even though it is Scala code, all used libraries are standard Java libraries.
Functions:
def getBufferedImage(imageFile: java.io.File): BufferedImage = {
ImageIO.read(imageFile)
}
def scaleImage(image: BufferedImage, minSize: Double): BufferedImage = {
val before: BufferedImage = image
val w = before.getWidth()
val h = before.getHeight()
val affit = new AffineTransform()
var scale = 1.0
if(h < w) {
if(h > 0) {
scale = minSize / h
}
} else {
if(w > 0) {
scale = minSize / w
}
}
affit.scale(scale, scale)
val affitop = new AffineTransformOp(affit, AffineTransformOp.TYPE_BICUBIC)
affitop.filter(before, null)
}
def getImageJpegByteArray(image: BufferedImage): Array[Byte] = {
val baos = new java.io.ByteArrayOutputStream()
val mcios = new MemoryCacheImageOutputStream(baos)
ImageIO.write(image, "jpeg", mcios)
mcios.close()
baos.toByteArray
}
Calling code snippet:
val img = getBufferedImage(imageFile)
val scaledImg = scaleImage(img, 512)
val result = getImageJpegByteArray(scaledImg)
// result is written to SQLite database
result is written to an SQLite database. If I download it from the database and save it as JPEG file, the resulting JPEG is
as expected if I use AffineTransformOp.TYPE_NEAREST_NEIGHBOR
completely black if I use AffineTransformOp.TYPE_BILINEAR
completely black if I use AffineTransformOp.TYPE_BICUBIC
Consequently, I accuse AffineTransformOp of being buggy...
How can I solve this problem?
File magic number of result is always ff d8 ff as expected for JPEG.
Details
Java version: Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_71
Operating System: Apple, OS X 10.9.5
Test image: http://www.photos-public-domain.com/wp-content/uploads/2012/05/thundercloud-plum-blossoms.jpg
I was able to reproduce your issue on Java 1.7.0_71 on OS X 10.10.4 (I rewrote your code in Java, I can post the full code if you are interested).
In any case, the problem is not that AffineTransformOp is buggy in itself. In my test program I displayed the image using a minimal Swing JFrame and the scaled image looked all good there. This is likely why most people in the comments did not understand the problem.
Part of the issue is that the BufferedImage returned by AffineTransformOp when you don't provide a destination to the filter method (the second parameter, null in your case), it will create one for you. This image will get type BufferedImage.TYPE_INT_ARGB. Here is the relevant code from AffineTransformOp.createCompatibleDestImage() (lines 456-468, I kept the formatting, to make it easier to spot):
ColorModel cm = src.getColorModel();
if (interpolationType != TYPE_NEAREST_NEIGHBOR &&
(cm instanceof IndexColorModel ||
cm.getTransparency() == Transparency.OPAQUE)
{
image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
}
else {
image = new BufferedImage(cm,
src.getRaster().createCompatibleWritableRaster(w,h),
cm.isAlphaPremultiplied(), null);
}
Notice the special case for TYPE_NEAREST_NEIGHBOR, which explains why you'll get different behavior when using nearest neighbor algorithm.
Normally this is all good, however (as I said, the image displays just fine in a Swing component).
The problem arises when you try to store this image as a JPEG. During the years, there's been a lot of confusion and issues related to the ImageIO JPEG plugin and whether it will allow you to write images with alpha channel (like your TYPE_INT_ARGB image). It does allow that. But, most often ARGB JPEGs will get misinterpreted as CMYK JPEGs (as they are 4 channels, and storing ARGB data in JPEG is very exotic) and will be displayed in all funky colors. In your case though, it seems to be all black...
So, there are two possible solutions:
Either write your image in a file format that supports alpha channel, like PNG or TIFF (TIFF requires an extra plugin, so it might not be the best choice). Like this:
ImageIO.write(image, "PNG", mcios);
Or, make sure your BufferedImage is in a pixel format without alpha channel before storing as JPEG. You can do this after the scaling, but the easiest (and fastest) is to just provide the AffineTransformOp with an explicit destination image, like this:
Rectangle newSize = affitop.getBounds2D(before).getBounds();
return affitop.filter(before,
new BufferedImage(newSize.width, newSize.height, BufferedImage.TYPE_3BYTE_BGR));
Here is your image, scaled by the program, using JPEG format and the TYPE_3BYTE_BGR:
I'm sure you can rewrite my Java code back to Scala. :-)
I can sucessfully read and write values to an image file which accurately shows an image created.
I simply read the values using getRGB(), and then bit shift them into red, green and blue arrays respectively. Then I simply set them back into another BufferedImage object using the setRGB() method.
Now, I am trying to alter the pixel values, say the very first pixel of the red array. I then print out the first 5 pixels of the red array and the first value is changed as expected before invoking the setRGB() method, but when I read in that image again the first value is now back to its original value?
Does anybody know that using the setRGB() only changes the values in memory, but does not actually write that altered values?
EDITS - THIS IS A SAMPLE REPRESENTATION OF MY CODE (this works perfectly due to getting back an image)
//READ IN IMAGE
BufferedImage imgBuf =null;
imgBuf = ImageIO.read(new File("test.jpg"));
int w = imgBuf.getWidth();
int h = imgBuf.getHeight();
int[] RGBarray = imgBuf.getRGB(0,0,w,h,null,0,w);
//BIT SHIFT VALUES INTO ARRAY
for(int row=0; row<h; row++)
{
for(int col=0; col<w; col++)
{
alphaPixels[row][col] = ((RGBarray[g]>>24)&0xff);
redPixels[row][col] = ((RGBarray[g]>>16)&0xff);
greenPixels[row][col] = ((RGBarray[g]>>8)&0xff);
bluePixels[row][col] = (RGBarray[g]&0xff);
g++;
}
}
//BIT SHIFT VALUES BACK TO INTEGERS
for(int row=0; row<h; row++)
{
for(int col=0; col<w; col++)
{
int rgb = (alphaPixels[row][col] & 0xff) << 24 | (redPixels[row][col] & 0xff) << 16 | (greenPixels[row][col] & 0xff) << 8 | (bluePixels[row][col] & 0xff);
imgBuf.setRGB(col, row, rgb);
}
}
//WRITE IMAGE BACK OUT
ImageIO.write(imgBuf, "jpeg", new File("new-test.jpg"));
Write where? If you change the RGB value of the BufferedImage's raster, then yes the memory value is written to and changed. If you mean does it change it to disk? No, not unless you write the image yourself to disk, often with ImageIO.write(...). Changes to the memory representation of disk data will not mathemagically change the disk representation on it's own; instead you have to explicitly do this with your code. I think that you may be missing this last important step.
Edit
You state in comment:
Currently I can write to an image created on disk with a new name. So if that works, then surely changing a few values should be the same effect? (Using setRBG() )
I'm still not clear on this. Say for instance:
If you have an image on disk, say imageA.jpg,
and say you read this into a BufferedImage via ImageIO.read(...), say into the bufferedImageA variable,
and then you change the data raster via setRGB(...)
and then write your changed BufferedImage to disk with ImageIO.write(...), say to a new file, imageB.jpg,
Then if you read in imageB.jpg, it should show the changes made.
But if you re-read in the unchanged imageA.jpg file, it will remain unchanged.
for my final year project I am developing an android app that can capture the image of a leaf and identify what type of tree it came from. I have a nearly completed PC version (developed in java) and i am starting the process of porting it to android. Although BufferedImage and Raster make up a key part of my program, this is a problem because java.awt is missing in android, so this means i have to alter the library.
I am using a library that was developed by my lecturer, the method below converts a BufferedImage to an IntImage(a type that is used throughout the package) using Raster.
I'm basically asking are there any android alternatives to java.awt that i can use?
Here's the code:
public static int[][] readAsInts(String fileName) throws IOException {
int[][] pixels;
System.out.println("File: " + fileName);
String [] types = ImageIO.getReaderFileSuffixes();
for (int l = 0; l < types.length; l++) {
System.out.println("Type " + l + ": " + types[l]);
}
File f = new File(fileName);
BufferedImage bi;
bi = ImageIO.read(f);
int cols = bi.getWidth();
int rows = bi.getHeight();
System.err.println("Number of bands: " + bi.getRaster().getNumBands());
Raster rast = bi.getRaster();
pixels = new int[rows][cols];
for (int r = 0; r < rows; r++) {
rast.getSamples(0, r, cols, 1, 0, pixels[r]);
}
return pixels;
} // readAsInts
Thanks guys!
EDIT: For anyone who doesn't know the raster stores the pixel values for the BufferedImage, along with the number of bands (red, green, blue, alpha), so this algorithm takes the pixel values ad stores them in a 2d array (pixels).
Sounds like you just want to work with bitmaps and be able to manipulate the pixels themselves in an array. The Bitmap class comes to mind along with its getPixels method. Remember though that you have much more limited memory on a mobile device. It's easy to run into out of memory problems when working with large bitmaps.
Another route would be to use the NDK. I don't have much experience there, but AndroidBitmap_lockPixels and AndroidBitmap_unlockPixels are probably a good place to start.
Lastly, yet another option that should perform pretty well is to use RenderScript.
If all you're looking to do is load an image into an int array though, Bitmap.getPixels should be quite sufficient.
You can convert a BufferedImage to a PNG or JPEG, then send it via HTTP to Android which can then convert the PNG or JPEG back to a Bitmap and use it.
When Android needs to send any image, send it as PNG or JPEG via network, and then convert the PNG to BufferedImage and use it in your program.
can any one suggest me the method to get actual pixel value of each pixel of a gray scale image?
i used this code to get pixel. But it just gives me red value of an pixel? I don't know that black-white or Gray Scale image contains RGB pixels or not, So please tell me that, the method i used is right or wrong? And also when i plotted histogram for my image it was exactly opposite to the imajeJ histogram for same image. So i guess my method to grab pixel of Gray Scale image is somewhere wrong
So If it is wrong what is the right way to get pixel.
here is my code
PlanarImage image = JAI.create("fileload", "C:\\16bit images\\alpXray.tiff");
BufferedImage bi = image.getAsBufferedImage();
int[] bins = new int[256];
int b=0;
for (int x = 0; x < bi.getWidth(); x++) {
for (int y = 0; y < bi.getHeight(); y++) {
int p= bi.getRaster().getSample(x, y,b); // (1) b=0 for red (2)b=1 for green(3) b=2 for bule
p=p/256;
bins[p]++;
}
}
every channel of a RGB image is in gray scale. in fact if you visualize a single channel (GIMP, photoshop) you see only gray shades.
usually to convert a rgb image to gray you take the three values of every channel and do a arithmetic mean.
or simply you take a single channel.
tell me if i misunderstood the question..
EDIT:
ok. if you have 3-channel gray scaled image probably you have 3 channel with same value on each channel. so simply take the value of single channel for each pixel.
if you have only one channel (of 16bit) take that pixel.
there...
I have some question about my homework on image processing using java. My question :
how to get gray level value each pixel of the rgb image in java programming???
I just know a little about how to get rgb value each pixel by syntax image.getRGB(x,y) for return rgb value. I have no idea for to get gray level value each pixel of the image....
Thanks for advance
First you'll need to extract the red, green and blue values from each pixel that you get from image.getRGB(x, y). See this answer about that. Then read about converting color to grayscale.
I agree with the previous answer. Create the BufferedImage like BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY). From what I understand a raster is for reading pixel data and a writable raster for writing pixel data or updating pixels. I always use writable raster although this may not be the best way to do it, because you can read pixel data and set pixel values. You can get the raster by WritableRaster raster = image.getRaster(); you can then get the value of a pixel by using raster.getSample(x, y, 0); The 0 in the arguments is for the band you want to get which for gray scale images should be 0.
You could also set up a BufferedImage of type TYPE_BYTE_GRAY, and draw the picture into it, then get the raster data and find the values.
Complementing Daniel's answer:
WritableRaster wr = myBufferedImage.getRaster();
for(int i = 0; i < myBufferedImage.getWidth(); i++){
for(int j = 0; j < myBufferedImage.getHeight(); j++){
int grayLevelPixel = wr.getSample(i, j, 0);
wr.setSample(i, j, 0, grayLevelPixel); // Setting same gray level, will do nothing on the image, just to show how.
}
}