Java Compressing an Image using Arrays 2D to 1D? - java

I am having trouble with an assignment of mine. I have a class which reads in PGM image files.
I need to create a few classes, the main one being a compression class. I need to compress the PGM (represented by 2D arrays) into a 1D array.
Here are the instructions:
public static short[] compress(short[][])
Is passed a 2D array of shorts which represents an image.
Returns the compressed image as a 1D array of shorts.
That method is my main concern.
Compression idea:
look for horizontal or vertical runs of like pixel values and record the number of times the
pixel value repeated itself.
note by Spektre: this is called RLE run length encoding used by PCX for example
algorithm:
1.Compute the compressed image array using horizontal runs
2.Compute the compressed image array using vertical runs
3.Choose to compress the image uses the best technique for this particular image.
4.Set the Header of image
Set the first and second values of your short [ ] result array to the width and height values.
Set the third value in your short[ ] result array to 1 for horizontal or 2 for vertical compression.
5.Set the image body
The rest of the short [ ] result array is filled with the RLE quantity and value
There are also a few other methods
if anyone wants to help with them:
public static void write(short[], String)
Is passed a 1D array of shorts which represents the compressed image and a desired filename.
Simply write the values to a text file with a .compressed extension added to the filename.
public static short[] read(String)
Is passed a compressed image filename.
Simply reads the values and returns them in a 1D array of shorts.
public static short[][] decompress(short[])
Is passed a 1D array of shorts which represents a compressed image.
Simply decompress the file so that we can display it on the screen
using our showGrayImage( ) code that we already have.
Thanks in advance!

Related

No get method providing array access - error when assigning new values ​to the matrix

I am working with opencv on android for the development of an image segmentation application, but specifically with the watershed algorithm. I'm opening the image and creating a mask with the same size as the image and passing 0 for all the rows and columns of that mask. However, in the following step, which is to add new values ​​for certain rows and columns of this matrix, I have the error: No get method providing array access
With that, I am not able to pass the new values ​​to the matrix, can someone help me with this?
COde:
// Load the image
val srcOriginal = Imgcodecs.imread(currentPhotoPath)
// Create a blank image of zeros (same dimension as img)
val markers = Mat.zeros(srcOriginal.rows(), srcOriginal.cols(), CvType.CV_32F)
// Example assigning a new value to a matrix index
markers.put(0,0,255)
Error:
OpenCV library in java doesn't take input Int as a parameter. You can see in the method declaration https://docs.opencv.org/3.4/javadoc/org/opencv/core/Mat.html#get(int,int,int%5B%5D).
Pass an IntArray for RGB values along with row and col values:
// Replace row and col with your values
markers.put(row, col, intArrayOf(0,0,255))
try this:
markers.put(0,0,X);
where x is array containing pixel vales (because it may be RGB,GRAY SCALE,etc)
In Java you can use Mat::get() and Mat::put() methods.
Read this for more details.

Working with images and file handling in 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.

Tensorflow in Java: inferenceInterface.fetch convert to multi-dimension array

I am using a trained Tensorflow model in Java on Android. I am trying to extract the output of an intermediary op.
The Tensor I am extracting has shape (150, 150, 256).
I have declared the output target to be
private float[] hybridValues;
hybridValues = new float[150 * 150 * 256];
I am then getting the output using.
inferenceInterface.fetch(OUTPUT_NODE, hybridValues);
The values are fine but they are stored as a 1D array. Is there a way to get inferenceinterface.fetch to return a multi-dimentional array?
I tried declaring hybridValue to be a three dimensional float array but that doesn't work as the fetch method expects a 1D array.
The end goal is to pass my output to a Python program that will feed the values to a Tensor of the same shape (150, 150, 256).
For comparison the Python a_output = graph.get_tensor_by_name('a2b_generator/Conv_7/Relu:0')
returns an ndarray with values in the same shape as the target tensor.
I'm using tensorflow with java on the desktop (which might be different) and all I do is create a multi-dimensional array with the correct size, then copy the values over, e.g. Tensor.copyTo(Object dst)

Image median threshold in java

i am doing the median threshold. The concept is i will insert an color image, then i will use the array.sort to get the median value of each rgb value. then write the new image based on the median value.
Here is the code.
public class medianthreshold{
public static void main(String[] a)throws Throwable{
File f=new File("C:\\Users\\asd\\Documents\\NetBeansProjects\\JavaApplication34\\images.jpg"); //Input Photo File
Color[] pixel=new Color[9];
int[] R=new int[9];
int[] B=new int[9];
int[] G=new int[9];
File output=new File("C:\\Users\\asd\\Documents\\NetBeansProjects\\JavaApplication34\\outputmedian.jpg");
img.setRGB(i,j,new Color(R[4],B[4],G[4]).getRGB());
}
ImageIO.write(img,"jpg",output);
}
}
And i would like to enhance it,make it become black and white image,by putting a condition, if the pixel value of each rgb is less than median, then the value should be 0 (represent white color), else the value become 0xFFFFFF (represent black color). 225x225 is the image width and height.
The problem im facing now is i duno which part i should put to make sure every pixel value will change to 0 or 0xFFFFF in each R[4],G[4],B[4] which represent the median value of rgb. The output image should have 0 and 1 of pixel value which also is a white and black image.
You said median, but you computed the mean. It's ok, it's just an other ways to do an adaptive global thresholding.
In order to make it faster, you should simply use the histogram of the image. It will be faster and simpler to compute the mean or the median value. You can also use the DataBuffer instead of getRGB. Moreover, I strongly advise to use TYPE_3BYTE_BGR images instead of TYPE_INT_RGB.
For the mean, you have code source example in ImageJ.
0/1, 0/0xFF or 0/0xFFFF just depends on the image encoding, so respectively TYPE_BINARY, TYPE_BYTE_GRAY or TYPE_USHORT_GRAY when you work with the BufferedImage.

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());

Categories

Resources