So after hours of searching I am ready to pull my hair out on this one.
I am doing some research in Computer Vision and am working with grayscale images. I need to end up with an "image" (a double scripted double array) of Sobel filtered double values. My Sobel converter is set up to take in a double scripted int array (int[][]) and go from there.
I am reading in a buffered image and I gather the grayscale int values via a method that I am 99% sure works perfectly (I can present it if need be).
Next I am attempting to convert this matrix of int values to a BufferedImage by the below method:
private BufferedImage getBIFromIntArr(int[][] matrix){
BufferedImage img = new BufferedImage(matrix.length * 4, matrix[0].length, BufferedImage.TYPE_INT_ARGB);
- Gather the pixels in the form of [alpha, r, g, b]
- multiply the size of the array by 4 for the model
int[] pixels = new int[(matrix.length * 4 * matrix[0].length)];
int index = 0;
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[0].length; j++) {
int pixel = matrix[i][j];
pixels[index] = pixel;
index++;
for (int k = 0; k < 3; k++) {
pixels[index] = 0;
index++;
}
}
}
-get the raster
WritableRaster raster = img.getRaster();
-output the amount of pixels and a sample of the array
System.out.println(pixels.length);
for (int i = 0; i < pixels.length; i++) {
System.out.print(pixels[i] + " ");
}
- set the pixels of the raster
raster.setPixels(0, 0, matrix.length, matrix[0].length, pixels);
- paint the image via an external routing to check works (does not)
p.panel.setNewImage(img);
return img;
}
Here is my understanding. The ARGB Type consists of 4 values Alpha, Red, Green, and Blue. I am guessing that setting the alpha values in the new BufferedImage to the greyscale image int values (the matrix values passed in) then this will reproduce the image. Please correct me if I am wrong. So as you can see I create an array of pixels that stores the int values like this: [intValue, 0, 0, 0] repeatedly to try to stay with the 4 value model.
Then I create a writable raster and set the gathered pixels in it using the gathered pixels. The only thing is that I get nothing in the BufferedImage. No error with the code below and Im sure my indeces are correct.
What am I doing wrong? Im sure it is obvious but any help is appreciated because I cant see it. Perhaps my assumption about the model is wrong?
Thanks,
Chronic
Related
I have an arary of double value (or can be float values as well). The range of the values are between 0-255. The array is in shape of [128][128][3], thus a RGB array of image. Now I want save this array as image (png or jpg). How this can be done in Java?
Ok, finally I could understand the image in Java.
after having an array of int[][][] nwimage = new int[128][128][3], convert all values from the float array to integer. The float array has shape of [128][128][3] as well.
now create a buffered image with the shape of nwimage as 2D which is 128x128.
BufferedImage bfImage = new BufferedImage(128, 128, BufferedImage.TYPE_INT_RGB);
now setRGB for each index of the bfImage as below;
for(int i = 0; i < 128; i++) {
for(int j = 0; j < 128; j++) {
Color myRGB = new Color(nwimage[i][j][0], nwimage[i][j][1], nwimage[i][j][2]);
int rgb = myRGB.getRGB();
bfImage.setRGB(i, j, rgb);
}
}
I have written a steganography algorithm, but it takes a long time to complete. This is because I create a new instance of bitmap, BitmapStegan, and I take each pixel from my old bitmap, bitmap. Whether I modify it or not, I have to set it in the new bitmap object. Therefore, I end up looping through all of the pixels, even though I only need to edit a few of them.
How can I address that problem?
Bitmap BitmapStegan = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig());
for(int i=0; i<bitmap.getWidth(); i++){
for(int j=0; j<bitmap.getHeight(); j++){
int pixel=bitmap.getPixel(i, j);
int red= Color.red(pixel);
int green=Color.green(pixel);
int blue=Color.blue(pixel);
if (NumberBitsInMessage>0) {
/*
I put here my bit to red and greed and blue with LSB method
*/
}
BitmapStegan.setPixel(i, j, Color.argb(Color.alpha(pixel), red, green, blue));
}
}
imageView.setImageBitmap(BitmapStegan);
First things first, do you really need a copy of your original image? If yes, because you want to compare statistical differences between the original and the stego image, you want to create a copy of your bitmap. This way, you create all the pixels in one go, which is faster. If you don't need a copy, just apply your changes directly to the original image object. Either way, you need to modify only one image, which from now on I will call image.
Now, you have two choices about how to iterate through only enough pixels for embedding. Either use loops for the rows and columns of your image and break out of them after you have embedded the whole secret, or create a counter for NumberBitsInMessage and explicitly change the pixel coordinates as you embed your bits.
1. Breaking out of the loops
embedding:
for (int i = 0; i < image.getWidth(); i++) {
for (int j = 0; j < image.getHeight(); j++) {
if (NumberBitsInMessage == 0) {
break embedding;
}
int pixel = image.getPixel(i, j);
int red = Color.red(pixel);
int green = Color.green(pixel);
int blue = Color.blue(pixel);
/*
modify pixel logic here
*/
image.setPixel(i, j, Color.argb(Color.alpha(pixel), red, green, blue));
}
}
2. Embedding bits counter
int width = 0;
int height = 0;
int maxHeight = image.getHeight();
for (int embeddedBits = 0; embeddedBits < NumberBitsInMessage; ) {
int pixel = image.getPixel(width, height);
int red = Color.red(pixel);
int green = Color.green(pixel);
int blue = Color.blue(pixel);
/*
modify pixel logic here
don't forget to increase `embeddedBits` for each colour you modify
*/
image.setPixel(width, height, Color.argb(Color.alpha(pixel), red, green, blue));
height++;
if (height == maxHeight) {
width++;
height = 0;
}
}
Hi I am in need of some help. I need to write a convolution method from scratch that takes in the following inputs: int[][] and BufferedImage inputImage. I can assume that the kernel has size 3x3.
My approach is to do the follow:
convolve inner pixels
convolve corner pixels
convolve outer pixels
In the program that I will post below I believe I convolve the inner pixels but I am a bit lost at how to convolve the corner and outer pixels. I am aware that corner pixels are at (0,0), (width-1,0), (0, height-1) and (width-1,height-1). I think I know to how approach the problem but not sure how to execute that in writing though. Please to aware that I am very new to programming :/ Any assistance will be very helpful to me.
import java.awt.*;
import java.awt.image.BufferedImage;
import com.programwithjava.basic.DrawingKit;
import java.util.Scanner;
public class Problem28 {
// maximum value of a sample
private static final int MAX_VALUE = 255;
//minimum value of a sample
private static final int MIN_VALUE = 0;
public BufferedImage convolve(int[][] kernel, BufferedImage inputImage) {
}
public BufferedImage convolveInner(double center, BufferedImage inputImage) {
int width = inputImage.getWidth();
int height = inputImage.getHeight();
BufferedImage inputImage1 = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
//inner pixels
for (int x = 1; x < width - 1; x++) {
for (int y = 1; y < height - 1; y ++) {
//get pixels at x, y
int colorValue = inputImage.getRGB(x, y);
Color pixelColor = new Color(colorValue);
int red = pixelColor.getRed() ;
int green = pixelColor.getGreen() ;
int blue = pixelColor.getBlue();
int innerred = (int) center*red;
int innergreen = (int) center*green;
int innerblue = (int) center*blue;
Color newPixelColor = new Color(innerred, innergreen, innerblue);
int newRgbvalue = newPixelColor.getRGB();
inputImage1.setRGB(x, y, newRgbvalue);
}
}
return inputImage1;
}
public BufferedImage convolveEdge(double edge, BufferedImage inputImage) {
int width = inputImage.getWidth();
int height = inputImage.getHeight();
BufferedImage inputImage2 = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
//inner pixels
for (int x = 0; x < width - 1; x++) {
for (int y = 0; y < height - 1; y ++) {
//get pixels at x, y
int colorValue = inputImage.getRGB(x, y);
Color pixelColor = new Color(colorValue);
int red = pixelColor.getRed() ;
int green = pixelColor.getGreen() ;
int blue = pixelColor.getBlue();
int innerred = (int) edge*red;
int innergreen = (int) edge*green;
int innerblue = (int) edge*blue;
Color newPixelColor = new Color(innerred, innergreen, innerblue);
int newRgbvalue = newPixelColor.getRGB();
inputImage2.setRGB(x, y, newRgbvalue);
}
}
return inputImage2;
}
public BufferedImage convolveCorner(double corner, BufferedImage inputImage) {
int width = inputImage.getWidth();
int height = inputImage.getHeight();
BufferedImage inputImage3 = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
//inner pixels
for (int x = 0; x < width - 1; x++) {
for (int y = 0; y < height - 1; y ++) {
//get pixels at x, y
int colorValue = inputImage.getRGB(x, y);
Color pixelColor = new Color(colorValue);
int red = pixelColor.getRed() ;
int green = pixelColor.getGreen() ;
int blue = pixelColor.getBlue();
int innerred = (int) corner*red;
int innergreen = (int) corner*green;
int innerblue = (int) corner*blue;
Color newPixelColor = new Color(innerred, innergreen, innerblue);
int newRgbvalue = newPixelColor.getRGB();
inputImage3.setRGB(x, y, newRgbvalue);
}
}
return inputImage3;
}
public static void main(String[] args) {
DrawingKit dk = new DrawingKit("Compositor", 1000, 1000);
BufferedImage p1 = dk.loadPicture("image/pattern1.jpg");
Problem28 c = new Problem28();
BufferedImage p5 = c.convolve();
dk.drawPicture(p5, 0, 100);
}
}
I changed the code a bit but the output comes out as black. What did I do wrong:
import java.awt.*;
import java.awt.image.BufferedImage;
import com.programwithjava.basic.DrawingKit;
import java.util.Scanner;
public class Problem28 {
// maximum value of a sample
private static final int MAX_VALUE = 255;
//minimum value of a sample
private static final int MIN_VALUE = 0;
public BufferedImage convolve(int[][] kernel, BufferedImage inputImage) {
int width = inputImage.getWidth();
int height = inputImage.getHeight();
BufferedImage inputImage1 = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
//for every pixel
for (int x = 0; x < width; x ++) {
for (int y = 0; y < height; y ++) {
int colorValue = inputImage.getRGB(x,y);
Color pixelColor = new Color(colorValue);
int red = pixelColor.getRed();
int green = pixelColor.getGreen();
int blue = pixelColor.getBlue();
double gray = 0;
//multiply every value of kernel with corresponding image pixel
for (int i = 0; i < 3; i ++) {
for (int j = 0; j < 3; j ++) {
int imageX = (x - 3/2 + i + width) % width;
int imageY = (x -3/2 + j + height) % height;
int RGB = inputImage.getRGB(imageX, imageY);
int GRAY = (RGB) & 0xff;
gray += (GRAY*kernel[i][j]);
}
}
int out;
out = (int) Math.min(Math.max(gray * 1, 0), 255);
inputImage1.setRGB(x, y, new Color(out,out,out).getRGB());
}
}
return inputImage1;
}
public static void main(String[] args) {
int[][] newArray = {{1/9, 1/9, 1/9}, {1/9, 1/9, 1/9}, {1/9, 1/9, 1/9}};
DrawingKit dk = new DrawingKit("Problem28", 1000, 1000);
BufferedImage p1 = dk.loadPicture("image/pattern1.jpg");
Problem28 c = new Problem28();
BufferedImage p2 = c.convolve(newArray, p1);
dk.drawPicture(p2, 0, 100);
}
}
Welcome ewuzz! I wrote a convolution using CUDA about a week ago, and the majority of my experience is with Java, so I feel qualified to provide advice for this problem.
Rather than writing all of the code for you, the best way to solve this large program is to discuss individual elements. You mentioned you are very new to programming. As the programs you write become more complex, it's essential to write small working snippets before combining them into a large successful program (or iteratively add snippets). With this being said, it's already apparent you're trying to debug a ~100 line program, and this approach will cost you time in most cases.
The first point to discuss is the general approach you mentioned. If you think about the program, what is the simplest and most repeated step? Obviously this is the kernel/mask step, so we can start from here. When you convolute each pixel, you are performing a similar option, regardless of the position (corner, edge, inside). While there are special steps necessary for these edge cases, they share similar underlying steps. If you try to write code for each of these cases separately, you will have to update the code in multiple (three) places with each adjustment and it will make the whole program more difficult to grasp.
To support my point above, here's what happened when I pasted your code into IntelliJ. This illustrates the (yellow) red flag of using the same code in multiple places:
The concrete way to fix this problem is to combine the three convolve methods into a single one and use if statements for edge-cases as necessary.
Our pseudocode with this change:
convolve(kernel, inputImage)
for each pixel in the image
convolve the single pixel and check edge cases
endfor
end
That seems pretty basic right? If we are able to successfully check edge cases, then this extremely simple logic will work. The reason I left it so general above to show how convolve the single pixel and check edge cases is logically grouped. This means it's a good candidate for extracting a method, which could look like:
private void convolvePixel(int x, int y, int[][] kernel, BufferedImage input, BufferedImage output)
Now to implement our method above, we will need to break it into a few steps, which we may then break into more steps if necessary. We'll need to look at the input image, if possible for each pixel accumulate the values using the kernel, and then set this in the output image. For brevity I will only write pseudocode from here.
convolvePixel(x, y, kernel, input, output)
accumulation = 0
for each row of kernel applicable pixels
for each column of kernel applicable pixels
if this neighboring pixel location is within the image boundaries then
input color = get the color at this neighboring pixel
adjusted value = input color * relative kernel mask value
accumulation += adjusted value
else
//handle this somehow, mentioned below
endif
endfor
endfor
set output pixel as accumulation, assuming this convolution method does not require normalization
end
The pseudocode above is already relatively long. When implementing you could write methods for the if and the else cases, but it you should be fine with this structure.
There are a few ways to handle the edge case of the else above. Your assignment probably specifies a requirement, but the fancy way is to tile around, and pretend like there's another instance of the same image next to this input image. Wikipedia explains three possibilities:
Extend - The nearest border pixels are conceptually extended as far as necessary to provide values for the convolution. Corner pixels are extended in 90° wedges. Other edge pixels are extended in lines.
Wrap - (The method I mentioned) The image is conceptually wrapped (or tiled) and values are taken from the opposite edge or corner.
Crop - Any pixel in the output image which would require values from beyond the edge is skipped. This method can result in the output image being slightly smaller, with the edges having been cropped.
A huge part of becoming a successful programmer is researching on your own. If you read about these methods, work through them on paper, run your convolvePixel method on single pixels, and compare the output to your results by hand, you will find success.
Summary:
Start by cleaning-up your code before anything.
Group the same code into one place.
Hammer out a small chunk (convolving a single pixel). Print out the result and the input values and verify they are correct.
Draw out edge/corner cases.
Read about ways to solve edge cases and decide what fits your needs.
Try implementing the else case through the same form of testing.
Call your convolveImage method with the loop, using the convolvePixel method you know works. Done!
You can look up pseudocode and even specific code to solve the exact problem, so I focused on providing general insight and strategies I have developed through my degree and personal experience. Good luck and please let me know if you want to discuss anything else in the comments below.
Java code for multiple blurs via convolution.
I am manipulating code of a image renderer that is making output image from Color[] array and my code simply update it with additional stuff right before saving, that is when the original image is actually prepared (all pixels positions prepared to be filled with RGBs in that Color[] array ready for final saving).
Reason why I am doing this is to have ability to insert text describing my render without need of another external graphics program that would do that (I want to have it all in one-go! action without need of another external app).
For that cause - as I have no reach/access for the original prepared BufferedImage (but I have access to actual Color[] that it is created from) I had to make my own class method that:
convert that original Color[] to my own temporary BufferedImage
update that temp. BufferedImage with my stuff via Graphics2D (adding some text to image)
convert my result (temp. BufferedImage with Graphics2D) back to Color[]
send that final Color[] back to the original image rendering method
that would actually make it to be the final image that is rendered out
and saved as png
Now everything works just fine as I expected except one really annoying thing that I cannot get rid off: my updated image looks very bleached-like/pale (almost no depth or shadows presented) compared to the original un-watermarked version...
To me it simply looks like after the image2color[] conversion (using #stacker's solution from here Converting Image to Color array) something goes wrong/is not right so the colors become pale and I do not have any clue why.
Here is the main part of my code that is in question:
BufferedImage sourceImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// Color[] to BufferedImage
for (int k = 0; k < multiArrayList.size(); k++) {
// PREPARE...
int x = (int) multiArrayList.get(k)[0];
int y = (int) multiArrayList.get(k)[1];
int w = (int) multiArrayList.get(k)[2];
int h = (int) multiArrayList.get(k)[3];
Color[] data = (Color[]) multiArrayList.get(k)[4];
int border = BORDERS[k % BORDERS.length];
for (int by = 0; by < h; by++) {
for (int bx = 0; bx < w; bx++) {
if (bx == 0 || bx == w - 1) {
if (5 * by < h || 5 * (h - by - 1) < h) {
sourceImage.setRGB(x + bx, y + by, border);
}
} else if (by == 0 || by == h - 1) {
if (5 * bx < w || 5 * (w - bx - 1) < w) {
sourceImage.setRGB(x + bx, y + by, border);
}
}
}
}
// UPDATE...
for (int j = 0, index = 0; j < h; j++) {
for (int i = 0; i < w; i++, index++) {
sourceImage.setRGB(x + i, y + j, data[index].copy().toNonLinear().toRGB());
}
}
}
Graphics2D g2d = (Graphics2D) sourceImage.getGraphics();
// paints the textual watermark
drawString(g2d, text, centerX, centerY, sourceImage.getWidth());
// when saved to png at this point ALL IS JUST FINE
ImageIO.write(sourceImage, "png", new File(imageSavePath));
g2d.dispose();
// BufferedImage to Color array
int[] dt = ((DataBufferInt) sourceImage.getRaster().getDataBuffer()).getData();
bucketFull = new Color[dt.length];
for (int i = 0; i < dt.length; i++) {
bucketFull[i] = new Color(dt[i]);
}
// update and repaint output image - THIS OUTPUT IS ALREADY BLEACHED/PALE
d.ip(0, 0, width, height, renderThreads.length + 1);
d.iu(0, 0, width, height, bucketFull);
// reset objects
g2d = null;
sourceImage = null;
bucketFull = null;
multiArrayList = new ArrayList<>();
I have tested (by saving it to another .png file right after the Graphics2D addition) that before it gets that 2nd conversion it looks absolutely OK 1:1 to the original image incl. my text on that image.
But as I said when it is send for render it becomes bleached/pale that is a problem I am trying to solve.
BTW I first thought it might be that Graphics2D addition so I did try it without it but the result was the same, that is bleached/pale version.
Although my process and code is completely different the output image is basically suffering exactly the same way as in this topic (still not solved) BufferedImage color saturation
Here are my 2 examples - 1st ORIGINAL, 2nd UPDATED (bleached/pale)
As suspected, the problem is that you convert the color values from linear RGB to gamma-corrected/sRGB values when setting the RGB values to the BufferedImage, but the reverse transformation (back to linear RGB) is not done when you put the values back into the Color array.
Either change the line (inside the double for loop):
sourceImage.setRGB(x + i, y + j, data[index].copy().toNonLinear().toRGB());
to
sourceImage.setRGB(x + i, y + j, data[index].toRGB());
(you don't need the copy() any more, as you no longer mutate the values, using toNonLinear()).
This avoids the conversion altogether.
... or you could probably also change the line setting the values back, from:
bucketFull[i] = new Color(dt[i]);
to
bucketFull[i] = new Color(dt[i]).toLinear();
Arguably, this is more "correct" (as AWT treats the values as being in the sRGB color space, regardless), but I believe the first version is faster, and the difference in color is negligible. So I'd probably try the first suggested fix first, and use that unless you experience colors that are off.
Hi everyone i have problems in converting GrayScale bmp images into integer 2D-array (with values 0-255) in Java.
I have a pmb image that could be seen as an integer(0-255) 2D-array and i want to see that 2D-array in a Java data structure
i tried this way:
Image image = ImageIO.read(new File("my_img.bmp"));
BufferedImage img = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_BYTE_GRAY);
Graphics g = img.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
Then with my BufferedImage i create int[][] this way:
int w = img.getWidth();
int h = img.getHeight();
int[][] array = new int[w][h];
for (int j = 0; j < w; j++) {
for (int k = 0; k < h; k++) {
array[j][k] = img.getRGB(j, k);
}
}
But now all the 2D-array is full of number like "-9211021" or similar.
i think that the problem is in getRGB(j,k) but i don't know if it's possible to solve it.
edit:
i know RGB is not grayscale, so how can i get the grayscale value of a single pixel from a grayscale BufferedImage?
In a grayscale image, BufferedImage.getPixel(x,y) wont give values within the [0-255] range. Instead, it returns the corresponding value of a gray level(intensity) in the RGB colorspace. That's why you are getting values like "-9211021".
The following snippet should solve your problem :
Raster raster = image.getData();
for (int j = 0; j < w; j++) {
for (int k = 0; k < h; k++) {
array[j][k] = raster.getSample(j, k, 0);
}
}
where image is the created BufferedImage. The 0 in the getSample indicates that we are accessing the first byte/band(setting it to a greater value will throw a ArrayOutOfBoundException in grayscale images).
You can use Catalano Framework. Contains several filters for image processing.
http://code.google.com/p/catalano-framework/
Detail: That's it faster than using WritableRaster.
FastBitmap fb = new FastBitmap(bufferedImage);
int[][] image = new int[fb.getHeight()][fb.getWidth];
fb.toArrayGray(image);
//Do manipulations with image
//...
//Place the image into fastBitmap
fb.arrayToImage(image);
//Retrieve in bufferedImage if you desire.
bufferedImage = fb.toBufferedImage();