Working with images and file handling in Java - java

I have a full red image I made using MS Paint (red = 255, blue = 0, green = 0)
I read that image into a File object file
Then I extracted the bytes using Files.readAllBytes(file.toPath()) into a byte array byteArray
Now my expectation is that :
a) byteArray[0], when converted to bitstream, should be all 1
b) byteArray[1], when converted to bitstream, should be all 0
c) byteArray[2], when converted to bitstream, should be all 0
because, as I understand, the pixels values are stored in the order RGB with 8 bits for each color.
When I run my code, I don't get expected outcome. byteArray[0] is all 1 alright, but the other 2 aren't 0s.
Where am I going wrong?
Edit
As requested, I'm including image size, saved format and code used to read it.
Size = 1920p x 1080p
Format = JPG
Code:
File file = new File("image_path.jpg");
byte byteArray[]= new byte[(int) file.length()];
try {
byteArray = Files.readAllBytes(file.toPath());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int bits[] = new int[8];
for(int j=0; j<8; j++)
{
bits[j] = (b[0] & (1 << j))==0 ? 0:1 ;
//System.out.println("bitsb :"+bitsb[j]);
}
Update
Unfortunately I am unable to make use of other questions containing ImageIO library functions. I'm here partly trying to understand how the image itself is stored, and how I can write my own logic for retrieving and manipulating the image files.

JPEG is a complex image format.
It does not hold the raw image pixel data, but instead has a header, optional metadata and compressed image data.
The algorithm to decompress it to raw pixel values is quite complex, but there are libraries that will do the work for you.
Here is a short tutorial:
https://docs.oracle.com/javase/tutorial/2d/images/loadimage.html
Here is the documentation of the BufferedImage class which will hold the image data:
https://docs.oracle.com/javase/7/docs/api/java/awt/image/BufferedImage.html
You will need to use one of the getRGB functions to access the raw pixel data.
Make sure to check that your image is in 24 bit color format, if you want each color component to take 1 byte exactly!
JPEG supports other formats such as 32 and 16 bits!
Alternatively, save your image as 24 bit uncompressed BMP.
The file will be much larger, but reading it is much simpler so you don't have to use a library.
Just skip the header, then read raw bytes.
An even simpler image format to work with would be PBM/PPM.

Related

How can the sound level at specific timestamps of a .wav file be measured using the Sound API in Java?

After searching for over 12 hours, I was unable to find anything regarding this. ALl I could find is how to use functions from the Sound API to measure and change the volume of the device, not the .wav file. It would be great if someone could advise us/tell us how to get and/or change the volume from specific timestamps of a .wav file itself, thank you very much!
Even if it is not possible to change the audio of the .wav file itself, we need to know at least how to measure the volume level at the specific timestamps.
To deal with the amplitude of the sound signal, you will have to inspect the PCM data held in the .wav file. Unfortunately, the Java Clip does not expose the PCM values. Java makes the individual PCM data values available through the AudioInputStream class, but you have to read the data points sequentially. A code example is available at The Java Tutorials: Using Files and Format Converters.
Here's a block quote of the relevant portion of the page:
Suppose you're writing a sound-editing application that allows the
user to load sound data from a file, display a corresponding waveform
or spectrogram, edit the sound, play back the edited data, and save
the result in a new file. Or perhaps your program will read the data
stored in a file, apply some kind of signal processing (such as an
algorithm that slows the sound down without changing its pitch), and
then play the processed audio. In either case, you need to get access
to the data contained in the audio file. Assuming that your program
provides some means for the user to select or specify an input sound
file, reading that file's audio data involves three steps:
Get an AudioInputStream object from the file.
Create a byte array in which you'll store successive chunks of data from the file.
Repeatedly read bytes from the audio input stream into the array. On each iteration, do something useful with the bytes in the array
(for example, you might play them, filter them, analyze them, display
them, or write them to another file).
The following code snippet outlines these steps:
int totalFramesRead = 0;
File fileIn = new File(somePathName);
// somePathName is a pre-existing string whose value was
// based on a user selection.
try {
AudioInputStream audioInputStream =
AudioSystem.getAudioInputStream(fileIn);
int bytesPerFrame =
audioInputStream.getFormat().getFrameSize();
if (bytesPerFrame == AudioSystem.NOT_SPECIFIED) {
// some audio formats may have unspecified frame size
// in that case we may read any amount of bytes
bytesPerFrame = 1;
}
// Set an arbitrary buffer size of 1024 frames.
int numBytes = 1024 * bytesPerFrame;
byte[] audioBytes = new byte[numBytes];
try {
int numBytesRead = 0;
int numFramesRead = 0;
// Try to read numBytes bytes from the file.
while ((numBytesRead =
audioInputStream.read(audioBytes)) != -1) {
// Calculate the number of frames actually read.
numFramesRead = numBytesRead / bytesPerFrame;
totalFramesRead += numFramesRead;
// Here, do something useful with the audio data that's
// now in the audioBytes array...
}
} catch (Exception ex) {
// Handle the error...
}
} catch (Exception e) {
// Handle the error...
}
END OF QUOTE
The values themselves will need another conversion step before they are PCM. If the file uses 16-bit encoding (most common), you will have to concatenate two bytes to make a single PCM value. With two bytes, the range of values is from -32778 to 32767 (a range of 2^16).
It is very common to normalize these values to floats that range from -1 to 1. This is done by float division using 32767 or 32768 in the denominator. I'm not really sure which is more correct (or how much getting this exactly right matters). I just use 32768 to avoid getting a result less than -1 if the signal has any data points that hit the minimum possible value.
I'm not entirely clear on how to convert the PCM values to decibels. I think the formulas are out there for relative adjustments, such as, if you want to lower your volume by 6 dBs. Changing volumes is a matter of multiplying each PCM value by the desired factor that matches the volume change you wish to make.
As far as measuring the volume at a given point, since PCM signal values can range pretty widely as they zig zag back and forth across the 0, the usual operation is to take an average of the absolute value of many PCM values. The process is referred to as getting a root mean square. The number of values to include in a RMS calculation can vary. I think the main consideration is to make the number of values in the rolling average to be large enough such that they are greater than the period of the lowest frequency included in the signal.
There are some good tutorials at the HackAudio site. This link is for the RMS calculation.

TIFFPackBitsCompressor - NPE?

I'm using com.sun.media.imageioimpl.plugins.tiff.TIFFPackBitsCompressor to try and encode an array of tiff bytes I have using PackBits. I'm unfamiliar with this class and haven't been finding many examples on how to use it. But, when following the javadoc, I've been getting an NPE every time I try to encode my data. So far as I can see, none of my values are null. I've tried these tests with multiple values at this point, but below is my most recent iteration:
TIFFPackBitsCompressor pack = new TIFFPackBitsCompressor();
//bImageFromConvert is a 16-bit BufferedImage with all desired data.
short[] bufferHolder = ((DataBufferUShort) bImageFromConvert.getRaster().getDataBuffer()).getData();
//Since bImageFromConvert is 16-bits, the short array isn't the right length.
//The below conversion handles tihs issue
byte[] byteBuffer = convertShortToByte(bufferHolder);
//I'm not entirely sure what this int[] in the parameters should be.
//For now, it is a test int[] array containing all 1s
int[] testint = new int[byteBuffer.length];
Arrays.fill(testint, 1);
//0 offset. dimWidth = 1760, dimHeight = 2140. Not sure what that last param is supposed to be in layman's terms.
//npe thrown at this line.
int testOut = pack.encode(byteBuffer, 0, dimWidth, dimHeight, testint, 1);
Does anyone have any insight as to what's happening? Also, if available, does anyone know a better way to encode my TIFF files using PackBits in a java program?
Let me know if there's anything to make my question clearer.
Thank you!
As said in the comment, you are not supposed to use the TIFFPackBitsCompressor directly, instead it's used internally by the JAI ImageIO TIFF plugin (the TIFFImageWriter) when you specify "PackBits" as compression type in the ImageWriteParam. You may also pass a compressor instance in the param, if you cast it to TIFFImageWriteParam first, but this is more useful for custom compressions not known by the plugin.
Also note that the compressor will only write PackBits compressed pixel data, it will not create a full TIFF file.
The normal way of writing a PackBits compressed TIFF file is:
BufferedImage image = ...; // Your input image
ImageWriter writer = ImageIO.getImageWritersByFormatName("TIFF").next(); // Assuming a TIFF plugin is installed
try (ImageOutputStream out = ImageIO.createImageOutputStream(...)) { // Your output file or stream
writer.setOutput(out);
ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionType("PackBits");
writer.write(null, new IIOImage(image, null, null), param);
}
writer.dispose();
The above code should work fine using both JAI ImageIO and the TwelveMonkeys ImageIO TIFF plugins.
PS: PackBits is a very simple compression algorithm based on run-length encoding of byte data. As 16 bit data may vary wildly between the high and low byte of a single sample, PackBits is generally not a good choice for compression of such data.
As stated in my comments, using completely random values I got the following results:
Compression | File size
-----------------|-----------------
None | 7 533 680 bytes
PackBits | 7 593 551 bytes
LZW w/predictor | 10 318 091 bytes
ZLib w/predictor | 10 318 444 bytes
This is not very surprising, as completely random data isn't generally compressible (without data loss). For a linear gradient, which may be more similar to "photographic" image data I got completely different results:
Compression | File size
-----------------|-----------------
None | 7 533 680 bytes
PackBits | 7 588 779 bytes
LZW w/predictor | 200 716 bytes
ZLib w/predictor | 144 136 bytes
As you see, here the LZW and Deflate/Zlib algorithms (with predictor step) performs MUCH better. For "real" data, there's likely more noise, so your results is likely somewhere in between these extremes.

Efficient access to image pixels in Java

I need to write a resampling function that takes an input image and generates an output image in Java.
The image type is TYPE_BYTE_GRAY.
As all pixels will be read and written, I need an efficient method to access the image buffer(s).
I don't trust that methods like getRGB/setRGB will be appropriate as they will perform conversions. I am after functions that will allow me the most direct access to the stored buffer, with efficient address computation, no image copy and minimum overhead.
Can you help me ? I have found examples of many kinds, for instance using a WritableRaster, but nothing sufficiently complete.
Update:
As suggested by #FiReTiTi, the trick is to get a WritableRaster from the image and get its associated buffer as a DataBufferByte object.
DataBufferByte SrcBuffer= (DataBufferByte)Src.getRaster().getDataBuffer();
Then you have the option to directly access the buffer using its getElem/setElem methods
SrcBuffer.setElem(i, getElem(i) + 1);
or to extract an array of bytes
byte [] SrcBytes= SrcBuffer.getData();
SrcBytes[i]= SrcBytes[i] + 1;
Both methods work. I don't know yet it there's a difference in performance...
The easiest way (but not the fastest) is to use the Raster myimage.getRaster(), and then use the methods getSample(x,y,c) and setSample(x,y,c,v) to access and modify the pixels values.
The fastest way to do it is to access the DataBuffer (direct access to the array representing the image), so for a TYPE_BYTE_GRAY BufferedImage, it would be byte[] buffer = ((DataBufferByte)myimage.getRaster().getDataBuffer()).getData(). Just be careful that the pixels are encoded on byte and not unsigned byte, so every time you want to read a pixel value, you have to do buffer[x] & 0xFF.
Here is a simple test:
BufferedImage image = new BufferedImage(256, 256, BufferedImage.TYPE_BYTE_GRAY) ;
byte[] buffer = ((DataBufferByte)image.getRaster().getDataBuffer()).getData() ;
System.out.println("buffer[0] = " + (buffer[0] & 0xFF)) ;
buffer[0] = 1 ;
System.out.println("buffer[0] = " + (buffer[0] & 0xFF)) ;
And here is the outputs:
buffer[0] = 0
buffer[0] = 1
It is possible to get the underlying buffer yourimage.getData().getDataBuffer() but it will require some conversion since this is one long array. You could find order of pixels by setting some elements to a extreme value and render the picture to see how the pixels are affected.

Having different values between Java and Matlab when Reading a .pgm image

I have to perpare a Trainging set for my Machine Learning Course, in which for a given face image it gives you an answer representing the side of the head ( straight , left , right , up )
For this purpose i need to read a .pgm image file in java and store its pixels in one row of matrix X, and then store the appropriate right answer of this image in a y vector. finally i will save these two arrays in a .mat file.
The problem is when trying to read the pixel values from a (P2 .pgm) image and printing them to console , they don't give identical values with the matlab matrix viewer. what would be the problem?
This is my code:
try{
InputStream f = Main.class.getResourceAsStream("an2i_left_angry_open.pgm");
BufferedReader d = new BufferedReader(new InputStreamReader(f));
String magic = d.readLine(); // first line contains P2 or P5
String line = d.readLine(); // second line contains height and width
while (line.startsWith("#")) { // ignoring comment lines
line = d.readLine();
}
Scanner s = new Scanner(line);
int width = s.nextInt();
int height = s.nextInt();
line = d.readLine();// third line contains maxVal
s = new Scanner(line);
int maxVal = s.nextInt();
for(int i=0;i<30;i++) /* printing first 30 values from the image including spaces*/
System.out.println((byte)d.read());
} catch (EOFException eof) {
eof.printStackTrace(System.out) ;
}
these are the values i get:
50
49
32
50
32
49
32
48
32
50
32
49
56
32
53
57
while this photo is what is indeed in the image from MATLAB Viewer:
(sorry i can't post images because of lack of reputationS)
and this is what you find when you open the .pgm file via notepad++
Take a look at this post in particular. I've experienced similar issues with imread and with Java's ImageIO class and for the longest time, I could not find this link as proof that other people have experienced the same thing... until now. Similarly, someone experienced related issues in this post but it isn't quite the same at what you're experiencing.
Essentially, the reason why images loaded in both Java and MATLAB are different is due to enhancement purposes. MATLAB scales the intensities so the image isn't mostly black. Essentially, the maximum intensity in your PGM gets scaled to 255 while the other intensities are linearly scaled to suit the dynamic range of [0,255]. So for example, if your image had a dynamic range from [0-100] in your PGM file before loading it in with imread, this would get scaled to [0-255] and not be the original scale of [0-100]. As such, you would have to know the maximum intensity value of the image before you loaded it in (by scanning through the file yourself). That is very easily done by reading the third line of the file. In your case, this would be 156. Once you find this, you would need to scale every value in your image so that it is rescaled to what it originally was before you read it in.
To confirm that this is the case, take a look at the first pixel in your image, which has intensity 21 in the original PGM file. MATLAB would thus scale the intensities such that:
scaled = round(val*(255/156));
val would be the input intensity and scaled is the output intensity. As such, if val = 21, then scaled would be:
scaled = round(21*(255/156)) = 34
This matches up with the first pixel when reading it out in MATLAB. Similarly, the sixth pixel in the first row, the original value is 18. MATLAB would scale it such that:
scaled = round(18*(255/156)) = 29
This again matches up with what you see in MATLAB. Starting to see the pattern now? Basically, to undo the scaling, you would need to multiply by the reciprocal of the scaling factor. As such, given that A is the image you loaded in, you need to do:
A_scaled = uint8(double(A)*(max_value/255));
A_scaled is the output image and max_value is the maximum intensity found in your PGM file before you loaded it in with imread. This undoes the scaling, as MATLAB scales the images from [0-255]. Note that I need to cast the image to double first, do the multiplication with the scaling factor as this will most likely produce floating point values, then re-cast back to uint8. Therefore, to bring it back to [0-max_value], you would have to scale in the opposite way.
Specifically in your case, you would need to do:
A_scaled = uint8(double(A)*(156/255));
The disadvantage here is that you need to know what the maximum value is prior to working with your image, which can get annoying. One possibility is to use MATLAB and actually open up the file with file pointers and get the value of the third line yourself. This is also an annoying step, but I have an alternative for you.
Alternative... probably better for you
Alternatively, here are two links to functions written in MATLAB that read and write PGM files without doing that unnecessary scaling, and it'll provide the results that you are expecting (unscaled).
Reading: http://people.sc.fsu.edu/~jburkardt/m_src/pgma_io/pgma_read.m.
Writing: http://people.sc.fsu.edu/~jburkardt/m_src/pgma_io/pgma_write.m
How the read function works is that it opens up the image using file pointers and manually parses in the data and stores the values into a matrix. You probably want to use this function instead of relying on imread. To save the images, file pointers are again used and the values are written such that the PGM standard is maintained and again, your intensities are unscaled.
Your java implementation is printing the ASCII values of the text bytes "21 2 1" etc.
50->2
51->1
32->SPACE
50->2
32->SPACE
51->1
etc.
Some PGM files use a text header, but binary representation for the pixels themselves. These are marked with a different magic string at the beginning. It looks like the java code is reading the file as if it had binary pixels.
Instead, your PGM file has ASCII-coded pixels, where you want to scan a whitespace-separated value for each pixel. You do this the same way you read the width and height.
The debug code might look like this:
line = d.readLine(); // first image line
s = new Scanner(line);
for(int i=0;i<30;i++) /* printing first 30 values from the image including spaces*/
System.out.println((byte)s.nextInt());

Convert 8 bit ( 16 Color Palette ) PNG to proper 4 bit ( 16 color Palette ) with Java or Python?

I have a bunch of images, to many to do by hand that are 16 color 8 bit PNG format that I need in 16 4 bit format, they all have the same palette.
I am scouring Google for the best library to use, but I am not finding much on this specific problem so I am coming here for hopefully some more targeted solutions.
I am trying to use PIL based on other answers I have found here, but not having any luck.
img = Image.open('DownArrow_focused.png')
img = img.point(lambda i: i * 16, "L")
img.save('DownArrow_focused.png', 'PNG')
but this gives me a grayscale image, not what I want.
PIL won't work, trying PyPNG. GIMP does this, but I have hundreds of these things I need to batch process them. And get batches of these to convert, so it isn't a one time thing.
A Java based solution would be acceptable as well, pretty much anything I can run from the command line on a Linux/OSX machine will be acceptable.
In PNG the palette is always stored in RGB8 (3 bytes for each index=color), with an arbitrary (up to 256) number of entries. If you currently have a 8 bit image with a 16-colors palette (16 total entries), you dont need to alter the pallete, only to repack the pixel bytes (two indexes per byte). If so, I think you could do it with PNGJ with this code (untested):
public static void reencode(String orig, String dest) {
PngReader png1 = FileHelper.createPngReader(new File(orig));
ImageInfo pnginfo1 = png1.imgInfo;
ImageInfo pnginfo2 = new ImageInfo(pnginfo1.cols, pnginfo1.rows, 4, false,false,true);
PngWriter png2 = FileHelper.createPngWriter(new File(dest), pnginfo2, false);
png2.copyChunksFirst(png1, ChunksToWrite.COPY_ALL);
ImageLine l2 = new ImageLine(pnginfo2);
for (int row = 0; row < pnginfo1.rows; row++) {
ImageLine l1 = png1.readRow(row);
l2.tf_pack(l1.scanline, false);
l2.setRown(row);
png2.writeRow(l2);
}
png1.end();
png2.copyChunksLast(png1, ChunksToWrite.COPY_ALL);
png2.end();
System.out.println("Done");
}
Elsewhere, if your current pallette has 16 "used" colors (but its length is greater because it includes unused colors), you need to do some work, modifying the palette chunk (but it also can be done).
Call Netpbm programs
http://netpbm.sourceforge.net/
from a Python script using the following commands:
$ pngtopnm test.png | pnmquant 16 | pnmtopng > test16.png
$ file test16.png
test16.png: PNG image data, 700 x 303, 4-bit colormap, non-interlaced
And GIMP reports test16.png as having Color space: Indexed color (16 colors),
which I guess is what you want.
This is not a pure Python solution but PIL is also not pure Python and has dependencies on shared libraries too. I think you cannot avoid a dependency on some external image software.

Categories

Resources