I am new to Android and need to process a bitmap to extract the pixel information into a multi-dimensional array by collecting only the black pixels i.e. R = 0, G = 0, and B = 0. I noticed that there is a GetPixel(x, y) method which is apparently slow and I need something like this instead. But I'm lost on how to implement a better GetPixel(x, y) method using information from GetPixels(). I am trying something like this currently:
private static int[][] GetPixels(Bitmap bmp)
{
int height = bmp.getHeight();
int width = bmp.getWidth();
int length = width * height;
int[] pixels = new int[length];
bmp.getPixels(pixels, 0, 0, 0, 0, width, height);
int[][]result = new int[width][height];
for(int pixel = 0, x = 0, y = 0; pixel < pixels.length; pixel += 4)
{
int argb = pixels[pixel];//how to access pixel information?
result[x][y] = argb;//store only black pixels??
x++;
if(y == width)
{
x = 0;
y++;
}
}
return result;
}
Related
I am trying to set a region of pixels in a mutable bitmap a different color in my android app. Unfortunately I cannot get setPixels() to work properly. I am constantly getting ArrayOutOfBoundsExceptions. I think it may have something to do with the stride, but I'm really not sure. That is the only parameter that I still don't understand. The only other post I have seen on setPixels (not setPixel) is here: drawBitmap() and setPixels(): what's the stride? and it did not help me. I tried setting the stride as 0, as the width of the bitmap, as the width of the bitmap - the area i'm trying to draw and it still crashes. Here is my code:
public void updateBitmap(byte[] buf, int offset, int x, int y, int width, int height) {
// transform byte[] to int[]
IntBuffer intBuf = ByteBuffer.wrap(buf).asIntBuffer();
int[] intarray = new int[intBuf.remaining()];
intBuf.get(intarray);
int stride = ??????
screenBitmap.setPixels(intarray, offset, stride, x, y, width, height); // crash here
My bitmap is mutable, so I know that is not the problem. I am also certain that my byte array is being properly converted to an integer array. But I keep getting ArrayOutOfBoundsExceptions and I don't understand why. Please help me figure this out
EDIT -
here is how I construct the fake input:
int width = 1300;
int height = 700;
byte[] buf = new byte[width * height * 4 * 4]; // adding another * 4 here seems to work... why?
for (int i = 0; i < width * height * 4 * 4; i+=4) {
buf[i] = (byte)255;
buf[i + 1] = 3;
buf[i + 2] = (byte)255;
buf[i + 3] = 3;
}
//(byte[] buf, int offset, int x, int y, int width, int height) - for reference
siv.updateBitmap(buf, 0, 0, 0, width, height);
So the width and height are the correct amount of ints (at least it should be).
EDIT2 - here is the code for the original creation of screenBitmap:
public Bitmap createABitmap() {
int w = 1366;
int h = 766;
byte[] buf = new byte[h * w * 4];
for (int i = 0; i < h * w * 4;i+=4) {
buf[i] = (byte)255;
buf[i+1] = (byte)255;
buf[i+2] = 0;
buf[i+3] = 0;
}
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
IntBuffer intBuf = ByteBuffer.wrap(buf).asIntBuffer();
int[] intarray = new int[intBuf.remaining()];
intBuf.get(intarray);
Bitmap bmp = Bitmap.createBitmap(metrics, w, h, itmap.Config.valueOf("ARGB_8888"));
bmp.setPixels(intarray, 0, w, 0, 0, w, h);
return bmp;
}
It seems to work in this instance, not sure what the difference is
Short
I think, stride is the width of the image contained in pixels.
long
What I take from the documentation, the algorithm should work somehow like this:
public void setPixels (int[] pixels,
int offset,
int stride,
int x,
int y,
int width,
int height)
for (int rowIndex = 0; rowIndex < height; rowIndex++) {
int rowStart = offset + stride * rowIndex; // row position in pixels
int bitmapY = y + rowIndex; // y position in the bitmap
for (int columnIndex = 0; columnIndex < width; columnIndex++) {
int bitmapX = x + columnIndex; // x position in the bitmap
int pixelsIndex = rowStart + columnIndex; // position in pixels
setPixel(bitmapX, bitmapY, pixels[pixelsIndex]);
}
}
}
At least this is what I would do with these arguments because is allows you to have one pixels as source and cut out different images in different sizes.
Feel free to correct the algorithm if I am wrong.
So, say you have an image with pixelsWidth and pixelsHeight in pixels.
Then you want to copy a section at pixelsX and pixelsY with width and height to a bitmap at position bitmapX and bitmapY.
This should be the call:
bitmap.setPixels(
pixelsWidth * pixelsY + pixelsX, // offset
pixelsWidth, // stride
bitmapX, // x
bitmapY, // y
width,
height)
probably it should be:
screenBitmap.setPixels(intarray, 0, width / 4, x, y, width / 4, height);
because you have converted byte to int. your error is ArrayOutOfBoundsExceptions. check whether the size intBuf.remaining() = width * height / 4.
If you're trying to draw on a bitmap, your best approach would be to abandon this method and use a canvas instead:
Canvas canvas = new Canvas(screenBitmap);
You can then draw specific points (if you want to draw a pixel), or other shapes like rectangles, circles, etc:
canvas.drawPoint(x, y, paint);
etc
Hope this helps.
I have images which I display in my app. They are downloaded from the web. These images are pictures of objects on an almost-white background. I want this background to be white (#FFFFFF). I figure, if I look at pixel 0,0 (which should always be off-white), I can get the color value and replace every pixel in the image having that value with white.
This question has been asked before and the answer seems to be this:
int intOldColor = bmpOldBitmap.getPixel(0,0);
Bitmap bmpNewBitmap = Bitmap.createBitmap(bmpOldBitmap.getWidth(), bmpOldBitmap.getHeight(), Bitmap.Config.RGB_565);
Canvas c = new Canvas(bmpNewBitmap);
Paint paint = new Paint();
ColorFilter filter = new LightingColorFilter(intOldColor, Color.WHITE);
paint.setColorFilter(filter);
c.drawBitmap(bmpOriginal, 0, 0, paint);
However, this isn't working.
After running this code, the entire image seems to be the color I was wanting to remove. As in, the entire image is 1 solid color now.
I was also hoping to not have to loop through every pixel in the entire image.
Any ideas?
Here is a method I created for you to replace a specific color for the one you want. Note that all the pixels will get scanned on the Bitmap and only the ones that are equal will be replaced for the one you want.
private Bitmap changeColor(Bitmap src, int colorToReplace, int colorThatWillReplace) {
int width = src.getWidth();
int height = src.getHeight();
int[] pixels = new int[width * height];
// get pixel array from source
src.getPixels(pixels, 0, width, 0, 0, width, height);
Bitmap bmOut = Bitmap.createBitmap(width, height, src.getConfig());
int A, R, G, B;
int pixel;
// iteration through pixels
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
// get current index in 2D-matrix
int index = y * width + x;
pixel = pixels[index];
if(pixel == colorToReplace){
//change A-RGB individually
A = Color.alpha(colorThatWillReplace);
R = Color.red(colorThatWillReplace);
G = Color.green(colorThatWillReplace);
B = Color.blue(colorThatWillReplace);
pixels[index] = Color.argb(A,R,G,B);
/*or change the whole color
pixels[index] = colorThatWillReplace;*/
}
}
}
bmOut.setPixels(pixels, 0, width, 0, 0, width, height);
return bmOut;
}
I hope that helped :)
I am trying to add snow flow effect. but not succeed
I tried to do the effect of continuously flow the snow.
is it possible? if yes than please give suggestion.
my code is below.
public class MainActivity extends Activity {
private int COLOR_MAX = 0xff;
ImageView image;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
image = (ImageView)findViewById(R.id.imageView1);
Bitmap imagebitmap = BitmapFactory.decodeResource(getResources(),R.drawable.hydrangeas);
applySnowEffect(imagebitmap);
}
Bitmap applySnowEffect(Bitmap source)
{
// get image size
int width = source.getWidth();
int height = source.getHeight();
int[] pixels = new int[width * height];
// get pixel array from source
source.getPixels(pixels, 0, width, 0, 0, width, height);
// random object
Random random = new Random();
int R, G, B, index = 0, thresHold = 50;
// iteration through pixels
for(int y = 0; y < height; ++y) {
for(int x = 0; x < width; ++x) {
// get current index in 2D-matrix
index = y * width + x;
// get color
R = Color.red(pixels[index]);
G = Color.green(pixels[index]);
B = Color.blue(pixels[index]);
// generate threshold
thresHold = random.nextInt(COLOR_MAX );
if(R > thresHold && G > thresHold && B > thresHold) {
pixels[index] = Color.rgb(COLOR_MAX, COLOR_MAX, COLOR_MAX);
}
}
}
// output bitmap
Bitmap bmOut = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
bmOut.setPixels(pixels, 0, width, 0, 0, width, height);
Toast.makeText(getApplicationContext(),"processed",10).show();
return bmOut;
}
I actually read about this the other day. You can use a particle system for Android. A library that can assist with that is found here - https://github.com/plattysoft/Leonids
Well I've just find the solution.
The source is here: code link
The main idea is SnowFallView extends View, and overriding onDraw(Canvas canvas) event, where we drawing our snow flakes drawables.
The code is just tested and works well.
I have written the following function to change the gamma of a Bitmap, but it's kind of slow, even on small (300 by 300) bitmaps. How can I make this function run faster? For example, is there a better way (i.e., faster way) to access individual pixel values from a Bitmap?
public Bitmap apply(Bitmap bmp, float gamma) {
if (bmp == null)
return null;
int width = bmp.getWidth();
int height = bmp.getHeight();
int[] pixels = new int[width * height];
bmp.getPixels(pixels, 0, width, 0, 0, width, height);
int[] powers = new int[256];
for (int i = 0; i < powers.length; i++)
powers[i] = (int)(Math.pow(i / 255.0f, 1.0f / gamma) * 255);
for (int p = 0; p < pixels.length; p++) {
int r = Color.red(pixels[p]);
int g = Color.green(pixels[p]);
int b = Color.blue(pixels[p]);
int newR = powers[r];
int newG = powers[g];
int newB = powers[b];
pixels[p] = Color.rgb(newR, newG, newB);
}
Bitmap newBmp = Bitmap.createBitmap(pixels, 0, width, width, height, Config.ARGB_8888);
return newBmp;
}
As an optimization, I calculate the powers ahead of time on all possible pixel values (0 to 255), which helps, but it's not enough. Also, declaring all the int's outside of the second for loop didn't help much, so I left them in. Thanks in advance.
I would look into using OpenGL or RenderScript for that if you really want it to be fast. Both have nice integration on Android.
I am trying to display a bitmap image pixel by pixel (which explicitly means with some delay).
For that I am using two "for-loops", but it prints only a single row of pixels...
My code:
Button start = (Button) findViewById(R.id.start);
start.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Toast.makeText(getBaseContext(), "Printing...", Toast.LENGTH_SHORT).show();
iImageArray = new int[bMap.getWidth()* bMap.getHeight()]; //initializing the array for the image size
bMap.getPixels(iImageArray, 0, bMap.getWidth(), 0, 0, bMap.getWidth(), bMap.getHeight()); //copy pixel data from the Bitmap into the 'intArray' array
//Canvas canvas = new Canvas (bMap);
//replace the red pixels with yellow ones
for (int i=0; i < bMap.getHeight(); i++) {
for(int j=0; j<bMap.getWidth(); j++) {
iImageArray[j] = 0xFFFFFFFF;
}
}
bMap = Bitmap.createBitmap(iImageArray, bMap.getWidth(), bMap.getHeight(), Bitmap.Config.ARGB_8888);//Initialize the bitmap, with the replaced color
image.setImageBitmap(bMap);
//canvas.drawPoints(iImageArray, 0, bMap.getHeight()*bMap.getWidth(), paint);
}
});
And I want to print the bitmap in grayscale and for that I found this code...
public Bitmap toGrayscale(Bitmap bmpOriginal) {
int width, height;
height = bmpOriginal.getHeight();
width = bmpOriginal.getWidth();
Bitmap bmpGrayscale = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
Canvas c = new Canvas(bmpGrayscale);
Paint paint = new Paint();
ColorMatrix cm = new ColorMatrix();
cm.setSaturation(0);
ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
paint.setColorFilter(f);
c.drawBitmap(bmpOriginal, 0, 0, paint);
return bmpGrayscale;
}
It is iImageArray[i] = 0xFFFFFFFF; that I have to test, not the actual grayscale value...
Your code looks basically correct except for your inner loop. Your comment says that you're setting them to yellow, but you're actually storing white. The loop will only affect the first row of pixels because your array index has a range of 0 to width-1. You can either calculate the index by multiplying (i*width+j) or keep a counter that increments.
Original Version:
//replace the red pixels with yellow ones
for (int i=0; i < bMap.getHeight(); i++)
{
for(int j=0; j<bMap.getWidth(); j++)
{
iImageArray[j] = 0xFFFFFFFF;
}
}
Fixed Version:
//replace the red pixels with yellow ones
int iWidth = bMap.getWidth();
for (int i=0; i < bMap.getHeight(); i++)
{
for(int j=0; j<bMap.getWidth(); j++)
{
iImageArray[(i*iWidth)+j] = 0xFF00FFFF; // actual value of yellow
}
}
I also made a program that reads the RGB colors of every pixels from an image.
Here is my code to read:
int orgWidth = bmp.getWidth();
int orgHeight = bmp.getHeight();
//define the array size
int[] pixels = new int[orgWidth * orgHeight];
bmp.getPixels(pixels, 0, orgWidth, 0, 0, orgWidth, orgHeight);
for (int y = 0; y < orgHeight; y++){
for (int x = 0; x < orgWidth; x++){
}
}
This is how my array works.
I made a question for ranged pixel check last week, however I also fixed it by my self :)
Optimize Color range check with nested for loop
EDIT:
Now I see, sorry.
at: iImageArray[j] = 0xFFFFFFFF;
also add iImageArray[i] = 0xFFFFFFFF;
then it should work