GrayScale (8bit per pixel) Image Pixel Manipulation in Java - java

I've heard that the data in gray-scale images with 8-bits color depth is stored in the first 7 bits of a byte of each pixel and the last bit keep intact! So we can store some information using the last bit of all pixels, is it true?
If so, how the data could be interpreted in individual pixels? I mean there is no Red, Blue and Green! so what do those bits mean?
And How can I calculate the average value of all pixels of an image?
I prefer to use pure java classes not JAI or other third parties.
Update 1
BufferedImage image = ...; // loading image
image.getRGB(i, j);
getRGB method always return an int which is bigger than one byte!!!
What should I do?

My understanding is that 8-bits colour depth means there is 8-bits per pixel (i.e. one byte) and that Red, Gren and Blue are all this value. e.g. greyscale=192 means Red=192, Green=192, Blue=192. There is no 7 bits plus another 1 bit.
AFAIK, you can just use a normal average. However I would use long for the sum and make sure each byte is unsigned i.e. `b & 0xff
EDIT: If the grey scale is say 128 (or 0x80), I would expect the RGB to be 128,128,128 or 0x808080.

Related

Improve Histogram

Ive made this method for getting me the pixel values of an image, im using it to compare 1 image against 50 other images. However it takes forever to produce outputs. Does anyone know of a way l can speed this method up? Would converting the images to Grayscale be a quicker way? If anyone could help with code, that would be great!
public static double[] GetHistogram (BufferedImage img) {
double[] myHistogram = new double [16777216];
for (int y = 0; y < img.getHeight(); y += 1)
{
for (int x = 0; x < img.getWidth(); x += 1)
{
int clr = img.getRGB(x,y);
Color c = new Color(img.getRGB(x, y));
int pixelIntValue = (int) c.getBlue() * 65536 + c.getGreen() * 256 + c.getRed();
myHistogram[pixelIntValue]++;
}
}
return myHistogram;
}
TLDR: use a smaller image and read this paper.
You should try to eliminate any unnecessary function calls as #Piglet mentioned, but you should definitely keep the colors in one histogram instead of a separate histogram for R, G, and B. Aside from getting rid of the extra function calls, I think there are four things you can do to speed up your algorithm—both creating and comparing the histograms—and reduce the memory usage (because less page caching means less disk thrashing and more speed).
Use a smaller image
One of the advantages of color histogram indexing is that it is relatively independent of resolution. The color of an object does not change with the size of the image. Obviously, there are limits to this—imagine trying to match objects using a 1×1 image. However, if your images have millions of pixels (like the images from most smart phones these days), you should definitely resize it. These authors found that an image resolution of only 16×11 still produced very good results [see page 17], but even resizing down to ~100×100 pixels should still provide a significant speed-up.
BufferedImage inherits the method getScaledInstance from Image, which you can use to get a smaller image.
double scalingFactor = 0.25; //You need to choose this value to work with your images
int aSmallHeight = myBigImage.getHeight() * scalingFactor;
int aSmallWidth = myBigImage.getWidth() * scalingFactor;
Image smallerImage = myBigImage.getScaledInstance(aSmallWidth, aSmallHeight, SCALE_FAST);
Reducing your image size is the single most effective thing you can do to speed up your algorithm. If you do nothing else, at least do this.
Use less information from each color channel
This won't make as much difference for generating your histograms because it will actually require a little more computation, but it will dramatically speed up comparing the histograms. The general idea is called quantization. Basically, if you have red values in the range 0..255, they can be represented as one byte. Within that byte, some bits are more important than others.
Consider this color sample image. I placed a mostly arbitrary shade of red in the top left, and in each of the other corners, I ignored one or more bits in the red channel (indicated by the underscores in the color byte). I intentionally chose a color with lots of one bits in it so that I could show the "worst" case of ignoring a bit. (The "best" case, when we ignore a zero bit, has no effect on the color.)
There's not much difference the upper right and upper left corners, even though we ignored one bit. The upper left and lower left have a visible, but minimal difference even though we ignored 3 bits. The Upper left and lower right corners are very different even though we ignored only one bit because it was the most significant bit. By strategically ignoring less significant bits, you can reduce the size of your histogram, which means there's less for the JVM to move around and fewer bins when it comes time to compare them.
Here are some solid numbers. Currently, you have 28×28×28 = 16777216 bins. If you ignore the 3 least significant bits from each color channel, you will get
25×25×25 = 32768 bins, which is 1/512 of the number of bins you are currently using. You may need to experiment with your set of images to see what level of quantization still produces acceptable results.
Quantization is very simple to implement. You can just ignore the rightmost bits by performing the bit shift operations.
int numBits = 3;
int quantizedRed = pixelColor.getRed() >> numBits;
int quantizedGreen = pixelColor.getGreen() >> numBits;
int quantizedBlue = pixelColor.getBlue() >> numBits;
Use a different color space
While grayscale might be quicker, you should not use grayscale because you lose all of your color information that way. When you're matching objects using color histograms, the actual hue or chromaticity is more important than how light or dark something is. (One reason for this is because the lighting intensity can vary across an image or even between images.) There are other representations of color that you could use that don't require you to use 3 color channels.
For example, L*a*b* (see also this) uses one channel (L) to encode the brightness, and two channels (a, b) to encode color. The a and b channels each range from -100 to 100, so if you create a histogram using only a and b, you would only need 40000 bins. The disadvantage of a histogram of only a and b is that you lose the ability to record black and white pixels. Other color spaces each have their own advantages and disadvantages for your algorithm.
It is generally not very difficult to convert between color spaces because there are many existing implementations of color space conversion functions that are freely available on the internet. For example, here is a Java conversion from RGB to L*a*b*.
If you do choose to use a different color space, be careful using quantization as well. You should apply any quantization after you do the color space conversion, and you will need to test different quantization levels because the new color space might be more or less sensitive to quantization than RGB. My preference would be to leave the image in RGB because quantization is already so effective at reducing the number of bins.
Use different data types
I did some investigating, and I notices that BufferedImage stores the image as a Raster, which uses a SampleModel to describe how pixels are stored in the data buffer. This means there is a lot of overhead just to retrieve the value of one pixel. You will achieve faster results if your image is stored as byte[] or int[]. You can get the byte array using
byte[] pixels = ((DataBufferByte) bufferedImage.getRaster().getDataBuffer()).getData();
See the answer to this previous question for more information and some sample code to convert it to a 2D array.
This last thing might not make much difference, but I noticed that you are using double for storing your histogram. You should consider whether int would work instead. In Java, int has a maximum value of > 2 billion, so overflow shouldn't be an issue (unless you are making a histogram of an image with more than 2 billion pixels, in which case, see my first point). An int uses only half as much memory as a double (which is a big deal when you have thousands or millions of histogram bins), and for many math operations they can be faster (though this depends on your hardware).
If you want to read more about color histograms for object matching, go straight to the source and read Swain and Ballard's Color Indexing paper from 1991.
Calculating a histogram with 16777216 classes is quite unusual.
Most histograms are calculated for each channel separately resulting in a 256 class histogram each for R,G and B. Or just one if you convert the image to grayscale.
I am no expert in Java. I don't know how clever the compilers optimize code.
But you call img.getHeight() for every row and img.getWidth() for every column of your image.
I don't know how often those expressions are actually evaluated but maybe you can save some processing time if you just use 2 variables that you assign the width and height of your image to befor you start your loops.
You also call img.getRGB(x,y) twice for every pixel. Same story. Maybe it is faster to just do it once. Function calls are usually slower than reading variables from memory.
You should also think about what you are doing here. img.getRGB(x,y) gives you an integer representation for a color.
Then you put that integer into a contrustor to make a Color object out of it. Then you use c.getBlue() and so on to get integer values for red, green and blue out of that Color object. Just to put it together into a integer again?
You could just use the return value of getRGB straight away and at least save 4 function calls, 3 multiplications, 3 summations...
So again given that I programmed Java for the last time like 10 years ago my function would look more like that:
public static double[] GetHistogram (BufferedImage img) {
double[] myHistogram = new double [16777216];
int width = img.getWidth()
int height = img.getHeight()
for (int y = 0; y < height; y += 1)
{
for (int x = 0; x < width; x += 1)
{
int clr = img.getRGB(x,y);
myHistogram[clr]++;
}
}
return myHistogram;
}
Of course the array type and size won't be correct and that whole 16777216 class histogram doesn't make sense but maybe that helps you a bit to speed things up.
I'd just use a bit mask to get the red, green and blue values out of that integer and create three histograms.

How to convert BMP image to 8-bit greyscale

I have to make a program that reads a 24 bit BMP image(without ImageIO or any external library) and make it a 8 bit greyscale BMP image... I read that I must change the header of the image to make it a 8 bit, Source 1 and Source 2. So I read here that the BitCount bytes are at 29 and 30 of the Header and try to change them...
First I read my file and generate the byte vector like this
FileInputStream image= new FileInputStream(path);
byte[] bytesImage = new byte[image.available()];
image.read(bytesImage);
image.close();
Then I get the image header and copy it to a new vector
int width = byteToInt(bytesImage[18], bytesImage[19], bytesImage[20], bytesImage[21]);
int height = byteToInt(bytesImage[22], bytesImage[23], bytesImage[24], bytesImage[25]);
int header = byteToInt(bytesImage[14], bytesImage[15], bytesImage[16], bytesImage[17]) + 14; // Add 14 for the header
vecGrey = Arrays.copyOf(bytesImage, bytesImage.length);
Then I change the header info bytes to make it an 8 bit BMP like this:
byte[] values = intToByte(8);
vecGrey[28] = values[0]; // This is the index for the BitCount byte 1
vecGrey[29] = values[1]; // and this one is the index for the second one.
Okay now comes the problem, for some reason I can't write a file with the header in vecGrey if i try to write vecGrey with a diferent header as show here:
FileOutputStream aGrey = new FileOutputStream(name+ "-gray.bmp");
aGrey.write(vecGrey);
aGrey.close();
// This is a method that displays the resulting image in a frame...
makeInterface(name + "-gray.bmp");
I know that I must change values in the vecGrey, but this should work showing incorrect output(probably a non greyscale image or not an image at all). But when I try to read the file that I generate in the makeInterface() method I get a
javax.imageio.iioexception unable to read the image header
So I asume that the program is unable to read correctly the header, but I don't know why! If I change the BitCount value to 16 it still works, but to 1, 4 or 8 it doesn't work with the same error... I didn't upload my hole code because it's in spanish, but if needed I can translate it and edit here.
Thanks!
EDIT1: I'm only using 640x480 24-bit BMP images, so I don't need to check padding.
When changing a BMP from 24bit to 8 bit you have to change several other things in the header, first of all the size of the image changes (bytes 3-6), since you are dealing with an 8bit image there is one byte per pixel, therefore the new size should become
headerSize {Usually 54} +(numberOfColors*4){This is for the color table/pallette, I recommend setting this at 256}+width*height {The actual amount of pixels}
Next you must indicate where is the offset for the pixel data, which is right after the color table/pallete, this value is located in the bytes 11-14 and the new value should be:
headerSize +numberOfColors*4
Next you need to modify the BITMAPINFOHEADER which starts on byte 15, bytes 15-18 should contain the size of this second header which is usually 40, if you just want to convert to grayscale you can ignore and leave some bytes unmodified until you reach byte 29 and 30 where you modify the bitCount (like you already did), then in bytes 35-38 as far as I know you have to input the new image size we have already calculated, bytes 47-50 determines the number of colors in your color palette, since you are doing grayscale I'd recommend using 256 colors and I'll explain why in a bit. Bytes 51-54 contains the number of important colors, set it at 0 to indicate every color is important.
Next you need to add the color table/palette right next to the heeader. The reason why I recommend 256 colors is because the color pallette is written like so: [B,G,R,0] where BGR are Blue, Green and Red color values in RGB format and a constant 0 at the end, with 256 colors you can make a palette that writes RGB values were R=G=B which should yield a shade of gray. So, next to the header you must add this new series of bytes in an ascending order:
[0,0,0,0] [1,1,1,0] [2,2,2,0] [3,3,3,0] ... [255,255,255,0]
Note that 256 is the numberOfColors which you need to calculate the new size of the image because it's the number of "entries" in the color pallette.
Next you'll want to write your new pixel data after the table/pallette. Since you were given an image in 24 bits, you can extract the pixel matrix and obtain RGB values of each pixel, just remember that you have a byte array which has values from -128 to 127, you need to make sure that you are getting the int value, so if the intensity of any channel is < 0 then add 256 to it to get the int value, then you can apply an equation which gives you an intensity of gray:
Y' = 0.299R' + 0.587G' + 0.114B'
Where Y' is the intensity of gray, R G B are the intensities of Red, Green and Blue.
You can round the result of the equation and then write it as a byte to the imange, and do the same with every pixel in the original image.
When you are done, simply add the two reserved 0s at the end of the file and you should have a brand new 8bit grayscale image of a 24bit image.
Hope this helped.
sources: The one you provided and:
https://en.wikipedia.org/wiki/BMP_file_format
https://en.wikipedia.org/wiki/Grayscale
you should first see the hex format of both 24-bit BMP as well as gray scale BMP, then you should go step wise,
-read 24-bit bmp header
-read data after offset.
-write header of 8-bit gray scale image
-write the data into 8-bit gray scale image.
note: you have to convert rgb bits into 8-bit gray scale by adding rgb bits and divide them by 3.

converting colored Image to an Array[i][j]

I would like to compute the Normalized Cross Correlation between two images using Java.
My program works fine but when I tried to verify my results in MATLAB, I unfortunately did not get the same results as I did with my Java implementation.
This is the code that I performed in MATLAB:
Img1 = rgb2gray(imread('image1.png'));
Img2 = rgb2gray(imread('image2.png'));
corr2(Img1, Img2);
This is part of my Java implementation. Some of the classes have been removed for better understanding:
I am not sure what is wrong with my Java implementation.
I also have one other question. In MATLAB, I had to convert the image to grayscale before using corr2. Do I need to do the same in Java?
The reason why it isn't the same is because you didn't account for the headers in the PNG file.
If you take a look at your Java code, you are reading in the image as a byte stream in your readImage method. For PNG, there are headers involved such as the size of the image and how many bits of colour per pixel there are. Not only are you grabbing image data (which by the way is compressed using a version of LZW so you're not even reading in raw image data), but you are also grabbing in extra information which is being collected in your correlation code.
What's confusing is that you are reading in the image fine using the BufferedImage type at the beginning of your correlation code to obtain the rows and columns. Why did you switch to using a byte stream in your readImage method?
As such, you need to change your readImage method to take in the BufferedImage object, or reread the data like you did in the correlation method in your readImage method. Once you do that, use the BufferedImage methods to access the RGB pixels. FWIW, if you are reading in the image as grayscale, then every channel should give you the same intensity so you can operate on one channel alone. Doesn't matter which.... but make sure that you're doing correlation on grayscale images. It is ambiguous when you go to colour, as there is currently no set standard on how to do this.
Using BufferedImage, you can use the getRGB method to obtain the pixel you want at column x and row y. x traverses from left to right, while y traverses from top to bottom. When you call getRGB it returns a single 32-bit integer in ARGB format. Each channel is 8 bits. As such, the first 8 bits (MSB) are the alpha value, the second 8 bits are for red, the third 8 bits are for green, and the final 8 are for blue. Depending on what channel you want, you need to bitshift and mask out the bits you don't need to get the value you want.
As an example:
int rgb = img.getRGB(x, y);
int alpha = rgb >> 24 & 0xFF;
int red = rgb >> 16 & 0xFF;
int green = rgb >> 8 & 0xFF;
int blue = rgb & 0xFF;
For the alpha value, you need to shift to the right by 24 bits to get it down to the LSB positions, then mask with 0xFF to obtain only the 8 bits that represent the alpha value. Similarly you would have do the same for the red, green and blue channels. Because correlation is rather ill-posed for colour images, let's convert the image to grayscale within your readImage method. As such, there is no need to convert the image before you run this method. We will do that within the method itself to save you some hassle.
If you take a look at how MATLAB performs rgb2gray, it performs a weighted sum, weighting the channels differently. The weights are defined by the SMPTE Rec. 601 standard (for those of you that want to figure out how I know this, you can take a look at the source of rgb2gray and read off the first row of their transformation matrix. These coefficients are essentially the definition of the 601 standard).
Previous versions of MATLAB simply added up all of the channels, divided by 3 and took the floor. I don't know which version of MATLAB you are using, but to be safe I'm going to use the most up to date conversion.
public static void readImage(BufferedImage img, int array[][], int nrows, int ncols) {
for (int i = 0; i < nrows; i++)
for (int j = 0; j < ncols; j++) {
int rgb = img.getRGB(j, i);
int red = rgb >> 16 & 0xFF;
int green = rgb >> 8 & 0xFF;
int blue = rgb & 0xFF;
array[i][j] = (int) (0.299*((double)red) + 0.587*((double)green) +
0.114*((double)blue) );
}
}
This should hopefully give you what you want!

To retrieve the bit from LSB insertion

I read about LSB insertion online, but it only introduces about how to insert bits to LSB, but it didn't describe how to extract the bits. This is the article I read about LSB insertion.
I understand the method they use below, but how do you extract the bits?
Here's an algorithm for getting the encrypted message:
Read image.
Iterate over pixels.
Decompose pixel into RGB values (one byte for R, one for G, one for B)
Take the LSB from red. If the LSB is in bit zero, you can AND the red value with a mask of 1 (bits 000000001). So, lsbValue = rvalue & 0x01. Place the lsbValue (it will only be one or zero) in the highest bit
Get the LSB from green. Place this in the next highest bit.
Get the LSB from blue. Place this in the next bit down.
Read the next pixel and decompose into RGB bytes.
Stuff the LSB of the color components into bit positions until you've filled a byte. This is the first byte of your encrypted mesage.
Continue iterating over pixels and their RGB values until you've processed all pixels.
Inspect the bytes you've decrypted. The actual message should be obvious. Anything beyond the encrypted message will just be noise, i.e, the LSB of the actual image pixels.

How to create a disparity map?

Okay so I have implemented a stereo correspondence algorithm which takes a stereo image pair, matches a point on the left image with a point on the right image, and finds the disparity between the points. I need to write this to a disparity map.
The disparity maps I have found are grayscale images, with lighter grays meaning less depth and darker grays meaning more depth. How do I translate my set of disparities into a grayscale image like this? My disparities are very small, ie only a distance of two between pixels, how does this translate into a grayscale pixel value?
There must be a standard way of compiling disparity maps but all my searching has yielded nothing thus far.
A simple solution when creating a disparity map the largest distance becomes black ie rgb(0,0,0) and the smallest distance - which is 0 - becomes white ie rgb(255,255,255). If you divide 255 by the largest distance then you find the increment value. Finally just go through all the disparities and set each rgb value to 255 minus the disparity times the increment value. Viola, you have your disparity map.
So in your example it sounds like your largest distance is only 2 pixals (which unfortunately means your map isn't going to have a lot of detail). Anyways 255 / 2 = 127.5. This means that 127.5 is the increment value. So everywhere that the disparity is 0, the rgb value is 255 - (0 * 127.5) or rgb(255,255,255), anywhere the disparity is 1 the rgb value is 255 - (1 * 127.5), we'll round to 128 so rgb(128,128,128) and anywhere the disparity is 2 the rgb value is 255 - (2 * 127.5) or rgb(0,0,0).
Here are some more resources:
How MathWorks does it
Jay Rambhia has a good blog explaining how to program one
Hope that helps!

Categories

Resources