I'm attempting to work out the area of a greyscale image, I'm aware that I could use getRGB() if it was a buffered image, but i'm using a toolkit so it is therefore a int image. I just want to ask how I can get the pixel value? I've included my code below
import iptoolkit.*;
public class FindArea {
public static void main(String[] args) {
String imageDir = "C:/Users/John/Dropbox/finalYear/Project/Leaves/";
MainWindow mw = new MainWindow();
int area = 0;
IntImage src = new IntImage(imageDir + "bg7.jpg", 256, 256);
src.displayImage(); //displays the image in a window
for (int row = 0; row <= src.getRows(); row++)
{
for (int col=0; col <= src.getCols(); col++)
{
//if(src.pixels[row][col] >= 0)
area++;
}
}
System.out.print("The area of the leaf is:" +area);
}
int pixel = src.pixels[row][col];
int red = (pixel & 0x00ff0000) >> 16;
int green = (pixel & 0x0000ff00) >> 8;
int blue = pixel & 0x000000ff;
// and the Java Color is ...
Color color = new Color(red,green,blue);
based on this for BufferedImage, but principle is same.
I believe to remember that bits in an RGB value are ordered like that:
8 bit of R | 8 bits of G | 8 bits of B
But it also depends on the type of image that you are using.
Use some bit operators like shift << and >> and mask the value with a and operator &.
Related
This question already has answers here:
Hiding message in JPG image
(2 answers)
Closed 6 years ago.
I have the following problem, I want to create simple steganography "program" by coding message in LSB.
I extract ARGB from picture ( each in it's own array ), encode message in LSB of blue color, and try to create new image using a those new values ( I join ARGB arrays back in int array ).
The obvious problem I have is when I change LSB and try to write them to picture , I can see that ImageWriter is creating picture that is much smaller in kb and I can't extract my message anymore.
This is the code :
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class Steganography {
int [][] alpha;
int [][] red;
int [][] green;
int [][] blue;
public int [][] readPixels (String image) throws IOException {
//load image into img buffer
BufferedImage img = ImageIO.read(new File(image));
//make matrix according to picture height and width
int [][] pixels = new int[img.getHeight()][img.getWidth()];
// load matrix with image pixels
for(int i=0;i<pixels.length;i++) {
for (int j = 0; j < pixels[0].length; j++) {
pixels[i][j]=(img.getRGB(j, i));
}
}
/* reminder to myself
values will be negative because of packing the 4 byte values into a 4-byte
The getRGB method returns an int whose 4 bytes are the alpha, red, green, and blue components in that order.
Assuming that the pixel is not transparent, the alpha is 255 (0xFF).
It's the most significant byte in the int, and the first bit is set in that value.
Because in Java int values are signed according to Two's Complement,
the value is actually negative because that first bit is on.
*/
return pixels ;
}
// extracts colors and alpha into their own matrix so we can reconstruct image later
public void extractColors(int [][] pixel){
this.alpha = new int[pixel.length][pixel[0].length];
this.red = new int[pixel.length][pixel[0].length];
this.green = new int[pixel.length][pixel[0].length];
this.blue = new int[pixel.length][pixel[0].length];
for(int i=0;i<pixel.length;i++) {
for(int j=0;j<pixel[i].length;j++){
int clr = pixel[i][j];
alpha[i][j] = (clr & 0xff000000) >> 24;
red[i][j] = (clr & 0x00ff0000) >> 16;
green[i][j] = (clr & 0x0000ff00) >> 8;
blue [i][j] = clr & 0x000000ff;
}
}
} // closed method
//reconstruct image
// need to make 32 bit integer again in correct order
public void reconstructImage () throws IOException{
int height = alpha.length;
int width= alpha[0].length;
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int y = 0; y < width; y++) {
for (int x = 0; x < height; x++) {
int rgb= red[x][y];
rgb = (rgb << 8) + green[x][y];
rgb = (rgb << 8) + blue[x][y];
image.setRGB(y, x, rgb);
}
}
ImageWriter writer = ImageIO.getImageWritersByFormatName("jpeg").next();
ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); // Needed see javadoc
param.setCompressionQuality(1.0F); // Highest quality
File file = new File("output.jpg");
ImageOutputStream ios = ImageIO.createImageOutputStream(file);
writer.setOutput(ios);
writer.write(image);
}
public void codeMessage (String message){
//first turn string into binary representation
// each character should have 7 bits
// ASCII uses 7 bit
message="START"+message.length()+message+"STOP";
String binaryMessage ="";
for(int i =0;i<message.length();i++){
//adding zeros if string has less than 8 characters
String binaryString= Integer.toBinaryString(message.charAt(i));
while (binaryString.length() !=7)
binaryString = "0"+binaryString;
binaryMessage+=binaryString;
}
//binaryMessage is binary representation of string
// change value of LSB in blue color according to binaryMessage
//actually coding message into LSB is done here
int k=0;
for (int i = 0; i < blue.length; i++) {
for (int j = 0; j < blue[i].length; j++) {
if(k>=binaryMessage.length())
break;
else if (binaryMessage.charAt(k) == '0') {
blue[i][j] = blue[i][j] & 0b1111110;
k++;
}
else {
blue[i][j] = blue[i][j] | 0b0000001;
k++;
}
}
}
} //closed codeMessage
public void readMessage(){
String LSB ="";
char charLSB;
String messageBinary ="";
for(int i=0;i<blue.length;i++){
for(int j=0;j<blue[i].length;j++){
LSB = Integer.toBinaryString(blue[i][j]);
charLSB = LSB.charAt(LSB.length()-1);
messageBinary+=charLSB;
}
}
char ArrayOfChars [] = new char [blue[0].length*blue.length];
int k =0;
for(int i=0;i<messageBinary.length()-7;i+=7){
String letter=(messageBinary.substring(i,i+7));
int valueOfASCIIcharacter = Integer.parseInt(letter,2);
char c = (char)(valueOfASCIIcharacter);
System.out.println(c);
ArrayOfChars[k]=c;
k++;
}
}
}
I have also tried to use ARGB instead of RGB for BufferedImage, without luck (only messes up colors, picture gets kinda pink ).
This is how I call function in main class
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException{
Steganography img = new Steganography();
int pixels [][] =img.readPixels("image.jpg");
img.extractColors(pixels);
img.codeMessage("Some message");
img.reconstructImage();
/*reading message from here on */
int pixels2 [][] = img.readPixels("output.jpg");
img.extractColors(pixels2);
img.readMessage();
}
}
Original picture has 83,3 kb ,and recreated picture has only 24,3 kb.
I have found solution.
For anyone having same problem as me and possible searching for solution in future:
This algorithm can't survive .jpg extension. Changed picture to bmp, takes bit longer but everything works as expected.
If you want to use steganography on jpg you have to use something else than LSB.
Here i'm trying to do a fastest method to save 3 matrix(R, G and B) into a BufferedImage.
I've found this method here at StackExchange, but it doesn't work for me because the image it's being saved in a grayscale color.
If I'm doing something wrong or if there's a way of doing this faster than bufferimage.setRGB(), please help me. Thanks!
public static BufferedImage array_rasterToBuffer(int[][] imgR,
int[][]imgG, int[][] imgB) {
final int width = imgR[0].length;
final int height = imgR.length;
int numBandas = 3;
int[] pixels = new int[width*height*numBandas];
int cont=0;
System.out.println("max: "+width*height*3);
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
for (int band = 0; band < numBandas; band++) {
pixels[(((i*width)+j)*numBandas +band)] =Math.abs(( (imgR[i][j] & 0xff) >> 16 | (imgG[i][j] & 0xff) >> 8 | (imgB[i][j] & 0xff)));
cont+=1;
}
}
}
BufferedImage bufferImg = new BufferedImage(width, height,BufferedImage.TYPE_INT_RGB);
WritableRaster rast = (WritableRaster) bufferImg.getData();
rast.setPixels(0, 0, width, height, pixels);
bufferImg.setData(rast);
return bufferImg;
}
I think you are getting grey because the expression
Math.abs(( (imgR[i][j] & 0xff) >> 16 | (imgG[i][j] & 0xff) >> 8 | (imgB[i][j] & 0xff)));
does not depend on band, so your rgb values are all the same.
The expression looks dodgy anyway because you normally use the left shift operator << when packing rgb values into a single int.
I don't know for sure, as I'm not familiar with the classes you are using, but I'm guessing something like this might work
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
pixels[(((i*width)+j)*numBandas)] = imgR[i][j] & 0xFF;
pixels[(((i*width)+j)*numBandas + 1)] = imgG[i][j] & 0xFF;
pixels[(((i*width)+j)*numBandas + 2)] = imgB[i][j] & 0xFF;
}
}
If you want a faster approach, you need to get the "live" WritableRaster from the BufferedImage and set pixels in the "native" format of the image, which is "pixel packed" for TYPE_INT_RGB. This will save you multiple (at least two) array copies and some data conversion. It will also save you 2/3rds of the memory used for the conversion, as we only need a single array component per pixel.
The below method should be quite a bit faster:
public static BufferedImage array_rasterToBuffer(int[][] imgR, int[][] imgG, int[][] imgB) {
final int width = imgR[0].length;
final int height = imgR.length;
// The bands are "packed" for TYPE_INT_RGB Raster,
// so we need only one array component per pixel
int[] pixels = new int[width * height];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
// "Pack" RGB values to native TYPE_INT_RGB format
// (NOTE: Do not use Math.abs on these values, and without alpha there won't be negative values)
pixels[((y * width) + x)] = ((imgR[y][x] & 0xff) << 16 | (imgG[y][x] & 0xff) << 8 | (imgB[y][x] & 0xff));
}
}
BufferedImage bufferImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// NOTE: getRaster rather than getData for "live" view
WritableRaster rast = bufferImg.getRaster();
// NOTE: setDataElements rather than setPixels to avoid conversion
// This requires pixels to be in "native" packed RGB format (as above)
rast.setDataElements(0, 0, width, height, pixels);
// No need for setData as we were already working on the live data
// thus saving at least two expensive array copies
return bufferImg;
}
// Test method, displaying red/green/blue stripes
public static void main(String[] args) {
int[][] fooR = new int[99][99];
int[][] fooG = new int[99][99];
int[][] fooB = new int[99][99];
for (int i = 0; i < 33; i++) {
Arrays.fill(fooR[i], 0xff);
Arrays.fill(fooG[i + 33], 0xff);
Arrays.fill(fooB[i + 66], 0xff);
}
BufferedImage image = array_rasterToBuffer(fooR, fooG, fooB);
showIt(image);
}
// For demonstration only
private static void showIt(final BufferedImage image) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("JPEGTest");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JScrollPane scroll = new JScrollPane(new JLabel(new ImageIcon(image)));
scroll.setBorder(BorderFactory.createEmptyBorder());
frame.add(scroll);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
It is possible to optimize this further, if you don't need a "managed" (possible hardware accelerated for display) image. The trick is to create the image directly "around" your pixels array, thus saving one more array allocation and array copy in setDataElements. The downside is that in some cases the image will be a little slower to draw onto the screen. This is mainly a concern for games or smooth animations though.
Replace the lines from BufferedImage bufferImg = new BufferedImage... until the return statement, with the following code:
DataBufferInt buffer = new DataBufferInt(pixels, pixels.length);
int[] bandMasks = {0xFF0000, 0xFF00, 0xFF}; // RGB (no alpha)
WritableRaster raster = Raster.createPackedRaster(buffer, width, height, width, bandMasks, null);
ColorModel cm = new DirectColorModel(32,
0x00ff0000, // Red
0x0000ff00, // Green
0x000000ff, // Blue
0x00000000 // No Alpha
);
BufferedImage bufferImg = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
PS: Note that I also changed the shifts inside the x/y loop, from right to left shifts. Might have been just a minor typo. :-)
I wanted to subtract two images pixel by pixel to check how much they are similar. Images have the same size one is little darker and beside brightness they don't differ. But I get those little dots in the result. Did I subtract those two images rigth? Both are bmp files.
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
public class Main2 {
public static void main(String[] args) throws Exception {
int[][][] ch = new int[4][4][4];
BufferedImage image1 = ImageIO.read(new File("1.bmp"));
BufferedImage image2 = ImageIO.read(new File("2.bmp"));
BufferedImage image3 = new BufferedImage(image1.getWidth(), image1.getHeight(), image1.getType());
int color;
for(int x = 0; x < image1.getWidth(); x++)
for(int y = 0; y < image1.getHeight(); y++) {
color = Math.abs(image2.getRGB(x, y) - image1.getRGB(x, y));
image3.setRGB(x, y, color);
}
ImageIO.write(image3, "bmp", new File("image.bmp"));
}
}
Image 1
Image 2
Result
The problem here is that you can't subtract the colors direcly. Each pixel is represented by one int value. This int value consists of 4 bytes. These 4 bytes represent the color components ARGB, where
A = Alpha
R = Red
G = Green
B = Blue
(Alpha is the opacity of the pixel, and always 255 (that is, the maximum value) in BMP images).
Thus, one pixel may be represented by
(255, 0, 254, 0)
When you subtract another pixel from this one, like (255, 0, 255, 0), then the third byte will underflow: It would become -1. But since this is part of ONE integer, the resulting color will be something like
(255, 0, 254, 0) -
(255, 0, 255, 0) =
(255, 255, 255, 0)
and thus, be far from what you would expect in this case.
The key point is that you have to split your color into the A,R,G and B components, and perform the computation on these components. In the most general form, it may be implemented like this:
int argb0 = image0.getRGB(x, y);
int argb1 = image1.getRGB(x, y);
int a0 = (argb0 >> 24) & 0xFF;
int r0 = (argb0 >> 16) & 0xFF;
int g0 = (argb0 >> 8) & 0xFF;
int b0 = (argb0 ) & 0xFF;
int a1 = (argb1 >> 24) & 0xFF;
int r1 = (argb1 >> 16) & 0xFF;
int g1 = (argb1 >> 8) & 0xFF;
int b1 = (argb1 ) & 0xFF;
int aDiff = Math.abs(a1 - a0);
int rDiff = Math.abs(r1 - r0);
int gDiff = Math.abs(g1 - g0);
int bDiff = Math.abs(b1 - b0);
int diff =
(aDiff << 24) | (rDiff << 16) | (gDiff << 8) | bDiff;
result.setRGB(x, y, diff);
Since these are grayscale images, the computations done here are somewhat redundant: For grayscale images, the R, G and B components are always equal. And since the opacity is always 255, it does not have to be treated explicitly here. So for your particular case, it should be sufficient to simplify this to
int argb0 = image0.getRGB(x, y);
int argb1 = image1.getRGB(x, y);
// Here the 'b' stands for 'blue' as well
// as for 'brightness' :-)
int b0 = argb0 & 0xFF;
int b1 = argb1 & 0xFF;
int bDiff = Math.abs(b1 - b0);
int diff =
(255 << 24) | (bDiff << 16) | (bDiff << 8) | bDiff;
result.setRGB(x, y, diff);
You did not "subtract one pixel from the other" correctly. getRGB returns "an integer pixel in the default RGB color model (TYPE_INT_ARGB)". What you are seeing is an "overflow" from one byte into the next, and thus from one color into the next.
Suppose you have colors 804020 - 404120 -- this is 3FFF00; the difference in the G component, 1 gets output as FF.
The correct procedure is to split the return value from getRGB into separate red, green, and blue, subtract each one, make sure they fit into unsigned bytes again (I guess your Math.abs is okay) and then write out a reconstructed new RGB value.
I found this which does what you want. It does seem to do the same thing and it may be more "correct" than your code. I assume it's possible to extract the source code.
http://tutorial.simplecv.org/en/latest/examples/image-math.html
/Fredrik Wahlgren
I'm having a few problems using a package to display an image, it works perfectly well for some images but not others. I've tried changing the image format but this doesn't make any difference, the output looks like this; http://tinypic.com/r/289bn92/5. Is this a problem with the code? or the formatting of the image?
Here's my code:
import java.awt.Color;
import iptoolkit.*;
public class FindArea {
public static void main(String[] args){
int area = 0;
String imageDir = "C:/Users/John/Dropbox/finalYear/Project/Leaves/";
MainWindow mw = new MainWindow();
IntImage src = new IntImage(imageDir + "ashLeafBW.jpg", 256, 256);
src.displayImage(); //displays the image in a window
for (int row = 0; row < src.getRows(); row++)
{
for (int col=0; col < src.getCols(); col++)
{
int pixel = src.pixels[row][col];
int red = (pixel & 0x00ff0000) >> 16;
int green = (pixel & 0x0000ff00) >> 8;
int blue = pixel & 0x000000ff;
// and the Java Color is ...
Color color = new Color(red,green,blue);
if((color.getRed() == 0) & (color.getBlue() == 0) & (color.getGreen() == 0))
area++;
}
}
System.out.print("The area of the leaf is: " +area);
}
}
The program is supposed to display an image of a leaf and calculate the area of that leaf. Any help would be greatly appreciated
I making App in netbeans platform using java Swing and JAI. In this i want to do image processing. I capture .tiff black and white image using X-Ray gun. after that i want to plot histogram of that Black and White image. so, for plot to histogram , first we have to get gray or black and white image pixel value. then we can plot histogram using this pixel value.so, how can i get this pixel value of black and white image?
This should work if you use java.awt.image.BufferedImage.
Since you want to create a histogram, I suppose you will loop through all the pixels. There is the method for returning a single pixel value.
int getRGB(int x, int y)
However, since looping will take place I suppose you'd want to use this one:
int[] getRGB(int startX, int startY, int w, int h, int[] rgbArray, int offset, int scansize)
When you get the array, use:
int alpha = (pixels[i] >> 24) & 0x000000FF;
int red = (pixels[i] >> 16) & 0x000000FF;
int green = (pixels[i] >>8 ) & 0x000000FF;
int blue = pixels[i] & 0x000000FF;
To extract the channel data. Not sure if the variables can be declared as byte (we are using only one byte of the integer in the array, although byte is signed and different arithmetic takes place - two's complement form), but you can declare them as short.
Then preform some maths on these values, for example:
int average = (red + green + blue) / 3;
This will return the average for the pixel, giving you a point you can use in a simple luminosity histogram.
EDIT:
Regarding histogram creation, I have used this class. It takes the image you want the histogram of as an argument to its setImage(BufferedImage image) method. Use updateHistogram() for array populating. The drawing data is in paintComponent(Graphics g). I must admit, it is sloppy, especially when calculating the offsets, but it can be easily simplified.
Here is the whole class:
class HistogramCtrl extends JComponent
{
BufferedImage m_image;
int[] m_histogramArray = new int[256]; //What drives our histogram
int m_maximumPixels;
public HistogramCtrl(){
m_maximumPixels = 0;
for(short i = 0; i<256; i++){
m_histogramArray[i] = 0;
}
}
void setImage(BufferedImage image){
m_image = image;
updateHistogram();
repaint();
}
void updateHistogram(){
if(m_image == null) return;
int[] pixels = m_image.getRGB(0, 0, m_image.getWidth(), m_image.getHeight(), null, 0, m_image.getWidth());
short currentValue = 0;
int red,green,blue;
for(int i = 0; i<pixels.length; i++){
red = (pixels[i] >> 16) & 0x000000FF;
green = (pixels[i] >>8 ) & 0x000000FF;
blue = pixels[i] & 0x000000FF;
currentValue = (short)((red + green + blue) / 3); //Current value gives the average //Disregard the alpha
assert(currentValue >= 0 && currentValue <= 255); //Something is awfully wrong if this goes off...
m_histogramArray[currentValue] += 1; //Increment the specific value of the array
}
m_maximumPixels = 0; //We need to have their number in order to scale the histogram properly
for(int i = 0; i < m_histogramArray.length;i++){ //Loop through the elements
if(m_histogramArray[i] > m_maximumPixels){ //And find the bigges value
m_maximumPixels = m_histogramArray[i];
}
}
}
protected void paintComponent(Graphics g){
assert(m_maximumPixels != 0);
Rectangle rect = g.getClipBounds();
Color oldColor = g.getColor();
g.setColor(new Color(210,210,210));
g.fillRect((int)rect.getX(), (int)rect.getY(), (int)rect.getWidth(), (int)rect.getHeight());
g.setColor(oldColor);
String zero = "0";
String thff = "255";
final short ctrlWidth = (short)rect.getWidth();
final short ctrlHeight = (short)rect.getHeight();
final short activeWidth = 256;
final short activeHeight = 200;
final short widthSpacing = (short)((ctrlWidth - activeWidth)/2);
final short heightSpacing = (short)((ctrlHeight - activeHeight)/2);
Point startingPoint = new Point();
final int substraction = -1;
startingPoint.x = widthSpacing-substraction;
startingPoint.y = heightSpacing+activeHeight-substraction;
g.drawString(zero,widthSpacing-substraction - 2,heightSpacing+activeHeight-substraction + 15);
g.drawString(thff,widthSpacing+activeWidth-substraction-12,heightSpacing+activeHeight-substraction + 15);
g.drawLine(startingPoint.x, startingPoint.y, widthSpacing+activeWidth-substraction, heightSpacing+activeHeight-substraction);
g.drawLine(startingPoint.x,startingPoint.y,startingPoint.x,heightSpacing-substraction);
double factorHeight = (double)activeHeight / m_maximumPixels; //The height divided by the number of pixels is the factor of multiplication for the other dots
Point usingPoint = new Point(startingPoint.x,startingPoint.y);
usingPoint.x+=2; //I want to move this two points in order to be able to draw the pixels with value 0 a bit away from the limit
Point tempPoint = new Point();
for(short i = 0; i<256; i++){
tempPoint.x = usingPoint.x;
tempPoint.y = (int)((heightSpacing+activeHeight-substraction) - (m_histogramArray[i] * factorHeight));
if((i!=0 && (i % 20 == 0)) || i == 255){
oldColor = g.getColor();
g.setColor(oldColor.brighter());
//Draw horizontal ruler sections
tempPoint.x = widthSpacing + i;
tempPoint.y = heightSpacing+activeHeight-substraction+4;
g.drawLine(tempPoint.x,tempPoint.y,widthSpacing + i,heightSpacing+activeHeight-substraction-4);
if(i <= 200){
//Draw vertical ruler sections
tempPoint.x = widthSpacing - substraction - 3;
tempPoint.y = heightSpacing+activeHeight-substraction-i;
g.drawLine(tempPoint.x,tempPoint.y,widthSpacing - substraction + 4, heightSpacing+activeHeight-substraction-i);
}
tempPoint.x = usingPoint.x;
tempPoint.y = usingPoint.y;
g.setColor(oldColor);
}
g.drawLine(usingPoint.x, usingPoint.y, tempPoint.x, tempPoint.y);
usingPoint.x++; //Set this to the next point
}
}
}