How can I write 16 bit grayscale image as jpeg? - java

I have 16-bit per pixel grayscale BufferedImage created from an array of shorts:
private BufferedImage get16bitImage(short[] pixels) {
ColorModel colorModel = new ComponentColorModel(
ColorSpace.getInstance(ColorSpace.CS_GRAY),
new int[]{16},
false,
false,
Transparency.OPAQUE,
DataBuffer.TYPE_USHORT);
DataBufferUShort db = new DataBufferUShort(pixels, pixels.length);
WritableRaster raster = Raster.createInterleavedRaster(
db,
imgD.width,
imgD.height,
imgD.width,
1,
new int[1],
null);
return new BufferedImage(colorModel, raster, false, null);
}
When trying to save it:
ImageIO.write(img, "PNG", new File(resultImgNamePNG)); // works fine
ImageIO.write(img, "BMP", new File(resultImgNameBMP)); // doesn't work, returns false
ImageIO.write(img, "JPEG", new File(resultImgNameJPEG)); // doesnt work, returns false
I tried using JAI:
public void writeImageToJPEG(File out, BufferedImage image, float quality) throws IOException {
JPEGEncodeParam param = new JPEGEncodeParam();
param.setQuality(quality);
ImageEncoder encoder = ImageCodec.createImageEncoder("JPEG", new FileOutputStream(out), param);
encoder.encode(image);
}
encoder.encode(image) throws java.lang.RuntimeException: Only 1, or 3-band byte data may be written.

I think you have to convert it to 8-bit first. If this is used for display purposes, java converts it to 8-bit bit before display anyways.
You can do something that I've seen sometimes actually improve the displayed image which is doing non-linear scaling of the values (using a log scale for example) such detail depends on the image you are generating ofcourse.
More on such effect here: http://www.java.net/external?url=http://www.cs.unm.edu/~brayer/vision/perception.html

Related

Byte array into the greyscale. Android take photo

When I am making photo in android I receive a byte array from camera. I would like to convert this array(image) into the 2 colors - white and black. Any ideas? Changing camera mode to mono/negative is not a sollution for me.
Greetings.
EDIT1: Code is in Java
First of all, you need to create bitmap from your array:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inMutable = true;
// data is your byte array
Bitmap bmpOriginal = BitmapFactory.decodeByteArray(data, 0, data.length, options);
Canvas canvas = new Canvas(bmpOriginal);
Bitmap bmpGrayscale = toGrayscale(bmpOriginal);
bmpOriginal.recycle(); // free memory immediately, as your bitmap is not garbage collected by now.
Then, you can pass your bmpOriginal, which is colored image, to converting method
public Bitmap toGrayscale(Bitmap bmpOriginal) {
int width, height;
height = bmpOriginal.getHeight();
width = bmpOriginal.getWidth();
Bitmap bmpGrayscale = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bmpGrayscale);
Paint paint = new Paint();
ColorMatrix cm = new ColorMatrix();
cm.setSaturation(0);
ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
paint.setColorFilter(f);
c.drawBitmap(bmpOriginal, 0, 0, paint);
return bmpGrayscale;
}
Above code will give you grayscale bitmap. It simply applies zero saturation filter to image. As the saturation is the purity of a color, the less purity - the less color difference. After applying above filter the only difference you get is brightness. The brightest is white, the darkest - black. If you want to convert it back to byte array you can try something like this:
Bitmap bmpGrayscale = intent.getExtras().get("data");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmpGrayscale.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
bmpGrayscale.recycle(); // free memory immediately, as your bitmap is not garbage collected by now.

Saving custom BufferedImage

I have raw grayscale image pixels represented by short[]. I would like to create BufferedImage from it and save it as PNG.
Since there is no TYPE_SHORT_GRAY defined for BufferedImage I'm creating one myself this way:
short[] myRawImageData;
// Create signed 16 bit data buffer, and compatible sample model
DataBuffer dataBuffer = new DataBufferShort(myRawImageData, w * h);
SampleModel sampleModel = new ComponentSampleModel(DataBuffer.TYPE_SHORT, w, h, 1, w, new int[] {0});
// Create a raster from sample model and data buffer
WritableRaster raster = Raster.createWritableRaster(sampleModel, dataBuffer, null);
// Create a 16 bit signed gray color model
ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_GRAY);
ColorModel colorModel = new ComponentColorModel(colorSpace, false, false, Transparency.OPAQUE, DataBuffer.TYPE_SHORT);
// Finally create the signed 16 bit image
BufferedImage image = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
try (FileOutputStream fos = new FileOutputStream("/tmp/tst.png")) {
ImageIO.write(image, "png", fos);// <--- Here goes the exception
} catch (Exception ex) {
ex.printStackTrace();
}
So far so good but when I'm trying to use ImageIO.write to save it as PNG I'm getting ArrayIndexOutOfBoundsException.
Your code works fine for me, the only way I got your error was when I changed the bandOffsets. Could you give us more of your code?
EDIT
If you have negative data in your dataset, you should probably be using ushort instead of short.
int h = 64, w = 64;
short[] myRawImageData = new short[4096];
for (int i = 0; i < 4096; i++){
//this rolls over into negative numbers
myRawImageData[i] = (short) (i * 14);
}
// Create signed 16 bit data buffer, and compatible sample model
DataBuffer dataBuffer = new DataBufferUShort(myRawImageData, w * h);
SampleModel sampleModel = new ComponentSampleModel(DataBuffer.TYPE_USHORT, w, h, 1, w, new int[] {0});
// Create a raster from sample model and data buffer
WritableRaster raster = Raster.createWritableRaster(sampleModel, dataBuffer, null);
// Create a 16 bit signed gray color model
ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_GRAY);
ColorModel colorModel = new ComponentColorModel(colorSpace, false, false, Transparency.OPAQUE, DataBuffer.TYPE_USHORT);
// Finally create the signed 16 bit image
BufferedImage image = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
try (FileOutputStream fos = new FileOutputStream("/tmp/tst.png")) {
ImageIO.write(image, "png", fos);// <--- Here goes the exception
} catch (Exception ex) {
ex.printStackTrace();
}
This is assuming you expect negative values to be the brighter half of the gamut.

how to compress image with PNG extension [duplicate]

How can I save BufferedImage with TYPE_INT_ARGB to jpg?
Program generates me that image:
And it's OK, but when I save it in that way:
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(byteStream);
try {
ImageIO.write(buffImg, "jpg", bos);
// argb
byteStream.flush();
byte[] newImage = byteStream.toByteArray();
OutputStream out = new BufferedOutputStream(new FileOutputStream("D:\\test.jpg"));
out.write(newImage);
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
The result is:
Understand that this is due to the alpha layer, but don't know how to fix it. Png format does not suit me, need jpg.
OK!
I've solved it.
Everything was pretty easy. Don't know is it a good decision and how fast it is. I have not found any other.
So.. everything we need is define new BufferedImage.
BufferedImage buffImg = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = buffImg.createGraphics();
// ... other code we need
BufferedImage img= new BufferedImage(buffImg.getWidth(), buffImg.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = img.createGraphics();
g2d.drawImage(buffImg, 0, 0, null);
g2d.dispose();
If there any ideas to improve this method, please, your welcome.
Images having 4 color channels should not be written to a jpeg file. We can convert between ARGB and RGB images without duplicating pixel values. This comes in handy for large images. An example:
int a = 10_000;
BufferedImage im = new BufferedImage(a, a, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = im.createGraphics();
g.setColor(Color.RED);
g.fillRect(a/10, a/10, a/5, a*8/10);
g.setColor(Color.GREEN);
g.fillRect(a*4/10, a/10, a/5, a*8/10);
g.setColor(Color.BLUE);
g.fillRect(a*7/10, a/10, a/5, a*8/10);
//preserve transparency in a png file
ImageIO.write(im, "png", new File("d:/rgba.png"));
//pitfall: in a jpeg file 4 channels will be interpreted as CMYK... this is no good
ImageIO.write(im, "jpg", new File("d:/nonsense.jpg"));
//we need a 3-channel BufferedImage to write an RGB-colored jpeg file
//we can make up a new image referencing part of the existing raster
WritableRaster ras = im.getRaster().createWritableChild(0, 0, a, a, 0, 0, new int[] {0, 1, 2}); //0=r, 1=g, 2=b, 3=alpha
ColorModel cm = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB).getColorModel(); //we need a proper ColorModel
BufferedImage imRGB = new BufferedImage(cm, ras, cm.isAlphaPremultiplied(), null);
//this image we can encode to jpeg format
ImageIO.write(imRGB, "jpg", new File("d:/rgb1.jpg"));

Java - Convert RAW to JPG/PNG

I have an image captured by a camera, in RAW BGRA format (byte array).
How can I save it to disk, as a JPG/PNG file?
I've tried with ImageIO.write from Java API, but I got error IllegalArgumentException (image = null)
CODE:
try
{
InputStream input = new ByteArrayInputStream(img);
BufferedImage bImageFromConvert = ImageIO.read(input);
String path = "D:/image.jpg";
ImageIO.write(bImageFromConvert, "jpg", new File(path));
}
catch(Exception ex)
{
System.out.println(ex.toString());
}
Note that "img" is the RAW byte array, and that is NOT null.
The problem is that ImageIO.read does not support raw RGB (or BGRA in your case) pixels. It expects a file format, like BMP, PNG or JPEG, etc.
In your code above, this causes bImageFromConvert to become null, and this is the reason for the error you see.
If you have a byte array in BGRA format, try this:
// You need to know width/height of the image
int width = ...;
int height = ...;
int samplesPerPixel = 4;
int[] bandOffsets = {2, 1, 0, 3}; // BGRA order
byte[] bgraPixelData = new byte[width * height * samplesPerPixel];
DataBuffer buffer = new DataBufferByte(bgraPixelData, bgraPixelData.length);
WritableRaster raster = Raster.createInterleavedRaster(buffer, width, height, samplesPerPixel * width, samplesPerPixel, bandOffsets, null);
ColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
BufferedImage image = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
System.out.println("image: " + image); // Should print: image: BufferedImage#<hash>: type = 0 ...
ImageIO.write(image, "PNG", new File(path));
Note that JPEG is not a good format for storing images with alpha channel. While it is possible, most software will not display it properly. So I suggest using PNG instead.
Alternatively, you could remove the alpha channel, and use JPEG.
With Matlab you can convert all types of images with 2 lines of code:
img=imread('example.CR2');
imwrite(img,'example.JPG');

How to read image with IIOImage and Get Raster

I am trying to read an image in Java and access the pixels via the raster. However I get an NPE from the Raster, how do I access it?
Here is what I am doing:
public static void main(String[] args) throws Exception
{
IIOImage iioImage = Image.readImage(Main.class.getResourceAsStream("/annalisa-licciardi.png"));
System.out.println(iioImage.getRaster().getHeight());
}
readImage is implemented as follows:
public static IIOImage readImage(ImageInputStream stream) throws IOException
{
if (stream == null)
throw new IllegalArgumentException("stream == null!");
Iterator iterator = ImageIO.getImageReaders(stream);
if (!iterator.hasNext())
return null;
ImageReader imageReader = (ImageReader) iterator.next();
ImageReadParam param = imageReader.getDefaultReadParam();
imageReader.setInput(stream,true,true);
IIOImage iioImage = imageReader.readAll(0,param);
stream.close();
imageReader.dispose();
return iioImage;
}
public static IIOImage readImage(InputStream inputStream) throws IOException
{
return readImage(ImageIO.createImageInputStream(inputStream));
}
How do I get the raster?
ImageReader.readAll(...) doesn't work that way.
Form the JavaDoc:
Reads the image indexed by imageIndex and returns an IIOImage containing the image, thumbnails, and associated image metadata, using a supplied ImageReadParam.
The actual BufferedImage referenced by the returned IIOImage will be chosen using the algorithm defined by the getDestination method.
Also note that an IIOImage can only hold either a BufferedImage or a Raster. Not both. readAll(...) will return an IIOImage that holds a BufferedImage. So, basically, what you are trying to achieve won't work.
But as #Marco13 says in the comments, it's trivial to get the Raster from the BufferedImage once you have loaded it.
BufferedImage image = ImageIO.read(input);
WritableRaster raster = image.getRaster();
To get the pixels as int ARGB values, you don't need the Raster, you can always get it from the BufferedImage directly:
int[] pixels = new int[w * h];
image.getRGB(0, 0, w, h, pixels, 0, w);
These values will be normalized and in the sRGB color space. And it will work, regardless of the actual sample format in the image.
However, if (and only if) your raster (or its backing DataBuffer, really) is already containing pixels in int ARGB (pixel packed) format, you can access them in this way, which is faster (as it requires no conversion):
int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
In many cases though, images will be in 3 or 4 byte RGB/BGR or RGBA/ABGR (pixel interleaved) form.
You can then get the pixels directly like this:
byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
You need to loop through the values and convert to int packed form, if that is what you prefer.

Categories

Resources