Java nullpointerexception with bufferedimage.getrgb - java

Currently I want to get an array with the pixels of an image. I'm using this code now:
int[] pixels;
int width = firstfloorimg.getWidth();
int height = firstfloorimg.getHeight();
firstfloorimg.getRGB(0, 0, width, height, pixels, 0, width);
But than when i want to use the pixels array it gives a NullpointerException. I have used this code before with no errors.
Why does it occure and how do can I make this work?

The Class BufferedImage offers two variants of getRGB() method:
First one, int getRGB(int x, int y) which will return a single pixel as the return type says.
Second one:
int[] getRGB(int startX, int startY, int w, int h,
int[] rgbArray, int offset, int scansize)
Which Returns an array of integer pixels in the default RGB color model. However if your passed rgbArray is null this function will create a new rgbArray inside of it and return it:
public int[] getRGB(int startX, int startY, int w, int h,
int[] rgbArray, int offset, int scansize) {
// other code
if (rgbArray == null) {
rgbArray = new int[offset+h*scansize];
}
// other code
return rgbArray;
}
But again, you will have to assign the returned array to pixels before using it. The array that was created inside the getRGB function can't change the reference of pixels array which was null before passing to this function.
Consider using getPixel(x, y) function over the second one, because, unlike the second one, getPixel(x, y) does not throw away the optimizations made by Java2D. Discussing about it is out of the scope of this question.
Reference:
BufferedImage.getRGB

Related

How to efficiently read a Bitmap object into a two-dimensional integer array?

I need to read the entirety of a Bitmap object into a 2-dimensional integer array in my Android application.
Currently, I am reading each pixel individually, one at a time, like so:
for (int y = 0; y < coverImageHeight; y++)
{
for (int x = 0; x < coverImageWidth; x++)
{
coverImageIntArray[x][y] = coverImageBitmap.getPixel(x, y);
}
}
However, this takes a really long time on large images (about 15 seconds).
Is there a way to do it all in one fell swoop for better efficiency?
I'm not familiar with Android dev, but typically for image objects you're able to just grab a reference or copy of some underlying buffer. This buffer is usually 1D, but you should be able to covert it fairly easily. In your case, for grabbing the pixels, there's a function getPixels which looks perfect.
int[] coverImageIntArray1D = new int[coverImageWidth * coverImageHeight]
coverImageBitmap.getPixels(coverImageIntArray1D, 0, coverImageWidth,
0, 0, coverImageWidth, coverImageHeight)
// coverImageIntArray1D should now contain the entire image's pixels
FYI, you can index into this type of 1D array using 2D indices:
int pixel = coverImageIntArray1D[x + y*coverImageWidth]
Which will give you the pixel at [x][y]. So you can still use it in a 2D manner without performing an inefficient transformation.
I just checked an old project I did involving OCR, and I used the method you present in your answer. My images were only 28x28 pixels, though.
It's likely that using getPixels() is faster.
See void getPixels (int[] pixels,
int offset,
int stride,
int x,
int y,
int width,
int height)
The code might look like
bitmap.getPixels(intArray, 0, bmp.getWidth(), 0, 0, bmp.getWidth(), bmp.getHeight());
This will copy the bitmap Color values into the int array.
Instead of using a two-dimensional array, consider using a little bit of for-loop logic and a one-dimensional array with
void getPixels (int[] pixels,
int offset,
int stride,
int x,
int y,
int width,
int height)
This approach should be much more performant than trying to split a one-dimensional array up into two-dimensional arrays, since you'd be converting the pixel data, which seems to be internally stored as int[], to an int[][], only to simplify your logic slightly.
int[] pixels = new int[bitmap.getWidth() * bitmap.getHeight()];
bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
for (int row = 0; row < bitmap.getHeight(); row++)
for (int column = 0; column < bitmap.getWidth(); column++)
Color color = Color.valueOf(pixels[row * bitMap.getWidth() + column]);
Have a look at the Android Developers reference for Bitmap.getPixels(int[], int, int, int, int, int, int) and Color.valueOf(int) (to extract single color components, Color.alpha(int), Color.red(int), Color.green(int) and Color.blue(int) respectively).

Does the constructor call have to be the first statement in the constructor?

I have a class cShape to handle graphic images. It takes the starting position in its constructor. I would like the starting position to be random, so I have the following code
cBalloon(Context InContext, int w, int h) {
// set up random postion
Random randomGenerator = new Random();
int sx=randomGenerator.nextInt(w);
int sy=randomGenerator.nextInt(h);
super( InContext, sx, sy, 0,0, 50,50, "balloon", 50,50,0);
}
I get a error saying "Constructor class must be first in a constructor"
Is there a way to do this?
The super(...) must be a first statement in the constructor.
But, there is a workaround: create a static method
private static int getRandomtInt(int x) {
Random randomGenerator = new Random();
return randomGenerator.nextInt(x);
}
and call it in a super constructor:
cBalloon(Context InContext, int w, int h) {
super( InContext, getRandomInt(w), getRandomInt(h), 0,0, 50,50, "balloon", 50,50,0);
}
super or this should be the first statement in the constructor. You can do it like this
super(InContext, new Random().nextInt(w), new Random().nextInt(h),
0, 0, 50, 50, "balloon", 50, 50, 0)
OR Even this
class cBalloon{
Random randomGenerator=new Random();
cBalloon(Context InContext, int w, int h) {
// set up random postion
super( InContext, randomGenerator.nextInt(w), randomGenerator.nextInt(h), 0,0, 50,50, "balloon", 50,50,0);
}
Hope this helps.
You cannot call the super like that. The super function must be called first in the cBalloon constructor. So you would need to pass the random numbers directly to it.
Like so:
cBalloon(Context InContext, int randx, int randY int w, int h)
{
super( InContext, randX, randY, 0,0, 50,50, "balloon", 50,50,0);
}
Yes, the super(..) should be the first statement in the constructor.
You basically have four options:
1) You can call directly to the constructor like this
cBalloon(Context InContext, int randx, int randY int w, int h, Random randomGenerator) {
super(InContext, randomGenerator.nextInt(w), randomGenerator.nextInt(h), ...)
}
2) Or you can get the random variables BEFORE calling the constructor
cBalloon(Context InContext, int randx, int randY int w, int h){
super(InContext, randx, randY, 0, 0, ...);
}
3) Create a static method that will generate the random number every time you call it. This will keep you constructor method very clean, which is good.
private static int getRandomInteger(final int x) {
Random randomGenerator = new Random();
return randomGenerator.nextInt(x);
}
cBalloon(Context InContext, int w, int h){
super(InContext, getRandomInteger(w), getRandomInteger(h), 0, 0, ...);
}
4) Just generate the random numbers in the super class.
There is no need to declare those integer variables sx and sy like that.
Hope it helps.
There are a number of workable pragmatic solutions here, but it bothered me that none are truly general for this class of task. A general solution would work for some data source which was not truly random, ie, where it matters that you get both values from the same instance. And it would be nice to avoid static variables, even temporary ones, for cleanliness and easy thread safety.
So here's my idea: have one constructor which invokes another, passing it a temporary instance of the data source Random(), which it will use to get both values.
public class cBalloon {
public cBalloon(Context inContext, int w, int h) {
this( inContext, new Random(), w, h);
}
public cBalloon(Context inContext, Random randomGenerator, int w, int h) {
super(inContext,
randomGenerator.nextInt(w),
randomGenerator.nextInt(h),
0,0, 50,50, "balloon", 50,50,0);
}
}

Using a Flood Fill Algorith to create an Array

I'm using a flood fill algorithm to sort through an image. If it encounters the same color, I want it copy that pixel over into an identically sized array called filled. The array filled is then transformed back into an image and saved as a jpg. However, when I open the jpg, it appears entirely black.
public static void findFace(int[][] image) throws IOException {
int height = image.length;
int width = image[0].length;
Color centerStart = new Color(image[width / 2][height / 2]);
int[][] filled = new int[width][height];
floodFill(width / 2, height / 2, centerStart, image, filled);
//construct the filled array as image. Show if the face was found.
BufferedImage bufferImage2 = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int Pixel = filled[x][y] << 16 | filled[x][y] << 8 | filled[x][y];
bufferImage2.setRGB(x, y, Pixel);
}
}
//save filled array as image file
File outputfile = new File("/home/lily/Pictures/APicaDay/saved.jpg");
ImageIO.write(bufferImage2, "jpg", outputfile);
}
public static int[][] floodFill(int x, int y, Color targetColor, int[][] image, int[][] filled) {
if (image[x][y] != targetColor.getRGB()) {
return filled;
}
filled[x][y] = image[x][y];
floodFill(x - 1, y, targetColor, image, filled);
floodFill(x + 1, y, targetColor, image, filled);
floodFill(x, y - 1, targetColor, image, filled);
floodFill(x, y + 1, targetColor, image, filled);
return filled;
}
bonus question: I would like the flood fill to also accept colors that are similar, but not the exact same, since I'm dealing with a photograph.
The floodFill function you've posted is missing two important elements:
If the area containing the same color as the first pixel extends all the way to the boundary of the image, the function will try to access image at an invalid index. You can fix this by first checking the x and y coordinates of the pixel you are checking, and returning immediately if they are out of bounds.
If there is more than one adjacent pixel of the same color, the function will cause recurse infinitely, since the initial call will call floodFill on the second pixel, which will then proceed to call floodFill on the first pixel, and so on. You need a way to make sure that you only call floodFill on a particular pixel once.
Since you're not observing either of these two symptoms, and you don't observe anything from the resulting image, I guess that the initial pixel's color check is not correct. When you pass an integer to the Color constructor, are you sure that it uses an RBG interpretation of that integer?

Java Draw Image from Int Array

If you have an int array representing individual pixels' RGB and Alpha values, how can I turn this into an image? Thanks in advance.
You might want to use BufferedImage and setRGB method, see the docs and look for following method:
public void setRGB(int startX,
int startY,
int w,
int h,
int[] rgbArray,
int offset,
int scansize)

int cannot be dereferenced

I am beginning in java (I'm learning in microedition) and I got this error: "int cannot be dereferenced" in the following class:
class DCanvas extends Canvas{
public DCanvas(){
}
public void drawString(String str, int x, int y, int r, int g, int b){
g.setColor(r, g, b); //The error is here
g.drawString(str, x, y, 0); //and here
}
public void paint(Graphics g){
g.setColor(100, 100, 220);
g.fillRect(0, 0, getWidth(), getHeight());
}
}
What am I doing wrong here?
Well I came from PHP and ECMAScripts where I was able to pass my function arguments this way so I really don't understand this error.
The g in drawString is the color value you've passed in, not your Graphics reference. So the error is when you're trying to call a method on an int, which you can't do.
// Passing an integer 'g' into the function here |
// V
public void drawString(String str, int x, int y, int r, int g, int b){
// | This 'g' is the integer you passed in
// V
g.setColor(r, g, b);
g.drawString(str, x, y, 0);
}
You are calling the setColor and fillRect methods on g, which is a parameter of type int.
Since int is not a reference type, you cannot call methods on it.
You probably want to add a Graphics parameter to the function.
While g is in the paint-method an object of the class Graphics (that contains methods named setColor, fillRect and also drawString) in the method drawString is g defined as an Integer that conatins the value for the color green. Especially in the line g.setColor(r, g, b); you use g to set a color on it and also as the argument for setting the color. int has no method setColor (that also doesn't make sense), so you get an error. You probably want to get an Graphics-object also in this method. As you extend canvas, you can get a graphics-object by calling getGraphics(), so your example could look like this:
public void drawString(String str, int x, int y, int r, int g, int b){
getGraphics().setColor(r, g, b);
getGraphics().drawString(str, x, y, 0);
}

Categories

Resources