I want to print a Bitmap to a mobile Bluetooth Printer (Bixolon SPP-R200) - the SDK doesn't offer direkt methods to print an in-memory image. So I thought about converting a Bitmap like this:
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
To a Monochrome Bitmap. I am drawing black text on above given Bitmap using a Canvas, which works well. However, when I convert the above Bitmap to a ByteArray, the printer seems to be unable to handle those bytes. I suspect I need an Array with one Bit per Pixel (a Pixel would be either white = 1 or black = 0).
As there seems to be no convenient, out of the box way to do that, one idea I had was to use:
bitmap.getPixels(pixels, offset, stride, x, y, width, height)
to Obtain the pixels. I assume, I'd have to use it as follows:
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int [] pixels = new int [width * height];
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
However - I am not sure about a few things:
In getPixels - does it make sense to simply pass the width as the "Stride" argument?
I guess I'd have to evaluate the color information of each pixel and either switch it to black or white (And I'd write this value in a new target byte array which I would ultimately pass to the printer)?
How to best evaluate each pixel color information in order to decide that it should be black or white? (The rendered Bitmap is black pain on a white background)
Does this approach make sense at all? Is there an easier way? It's not enough to just make the bitmap black & white, the main issue is to reduce the color information for each pixel into one bit.
UPDATE
As suggested by Reuben I'll first convert the Bitmap to a monochrome Bitmap. and then I'll iterate over each pixel:
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int[] pixels = new int[width * height];
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
// Iterate over height
for (int y = 0; y < height; y++) {
int offset = y * height;
// Iterate over width
for (int x = 0; x < width; x++) {
int pixel = bitmap.getPixel(x, y);
}
}
Now Reuben suggested to "read the lowest byte of each 32-bit pixel" - that would relate to my question about how to evaluate the pixel color. My last question in this regard: Do I get the lowest byte by simply doing this:
// Using the pixel from bitmap.getPixel(x,y)
int lowestByte = pixel & 0xff;
You can convert the image to monochrome 32bpp using a ColorMatrix.
Bitmap bmpMonochrome = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmpMonochrome);
ColorMatrix ma = new ColorMatrix();
ma.setSaturation(0);
Paint paint = new Paint();
paint.setColorFilter(new ColorMatrixColorFilter(ma));
canvas.drawBitmap(bmpSrc, 0, 0, paint);
That simplifies the color->monochrome conversion. Now you can just do a getPixels() and read the lowest byte of each 32-bit pixel. If it's <128 it's a 0, otherwise it's a 1.
Well I think its quite late now to reply to this thread but I was also working on this stuff sometimes back and decided to build my own library that will convert any jpg or png image to 1bpp .bmp. Most printers that require 1bpp images will support this image (tested on one of those :)).
Here you can find library as well as a test project that uses it to make a monochrome single channel image. Feel free to change it..:)
https://github.com/acdevs/1bpp-monochrome-android
Enjoy..!! :)
You should convert each pixel into HSV space and use the value to determine if the Pixel on the target image should be black or white:
Bitmap bwBitmap = Bitmap.createBitmap( bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.RGB_565 );
float[] hsv = new float[ 3 ];
for( int col = 0; col < bitmap.getWidth(); col++ ) {
for( int row = 0; row < bitmap.getHeight(); row++ ) {
Color.colorToHSV( bitmap.getPixel( col, row ), hsv );
if( hsv[ 2 ] > 0.5f ) {
bwBitmap.setPixel( col, row, 0xffffffff );
} else {
bwBitmap.setPixel( col, row, 0xff000000 );
}
}
}
return bwBitmap;
Converting to monochrome with exact the same size as the original bitmap is not enough to print.
Printers can only print each "pixel" (dot) as monochrome because each spot of ink has only 1 color, so they must use much more dots than enough and adjust their size, density... to emulate the grayscale-like feel. This technique is called halftoning. You can see that printers often have resolution at least 600dpi, normally 1200-4800dpi, while display screen often tops at 200-300ppi.
So your monochrome bitmap should be at least 3 times the original resolution in each side.
Related
I'm trying to convert all the black pixels in one Bitmap (Created from an ImageView that was a PNG file)..
I've tried it in many ways but I still couldn't succeed in that.
Please help me I'm trying it for like 3 days straight...
A little example of my code:
headSkin.buildDrawingCache();
final Bitmap bmp = headSkin.getDrawingCache();
int w = bmp.getWidth();
int h = bmp.getHeight();
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int color = bmp.getPixel(x, y);
// Shift your alpha component value to the red component's.
bmp.setPixel(x, y, Color.RED);
}
}
As you can see... I didn't even state an IF statement..
I just tried to make all the pixels red in this bitmap and even this didn't work.. pls help?
I see 2 problems here,
First, you have this Bitmap object in your memory, and you change the black pixels to red, but how do you know if it is changed or not? You should set an ImageView to this Bitmap to see the result (or save it to file etc.)
Second, use getPixels and setPixels instead, getPixels will give you 1 dimensional array, it goes like 1.row, 2.row, 3.row etc. And setPixels also accepts a 1 dimensional array. This function is incredibly faster than altering pixels 1 by 1.
#Anil
Hi dude, just tried it and I can't use it 'cuz of IndexOutOfBound exception...
headSkin.buildDrawingCache();
bmp = headSkin.getDrawingCache();
int [] allpixels = new int [bmp.getHeight()*bmp.getWidth()];
bmp.getPixels(allpixels, 0, bmp.getWidth(), 0, 0, bmp.getWidth(), bmp.getHeight());
for(int i = 0; i < allpixels.length; i++)
{
if(allpixels[i] == Color.BLACK)
{
allpixels[i] = Color.RED;
}
}
bmp.setPixels(allpixels, 0, bmp.getWidth(), 0, 0, bmp.getWidth(), bmp.getHeight());
headSkin.setImageBitmap(bmp);
what is the problem here?
EDIT: Just tried it now while running, not debugging and it doesn't even show me an error or something.. It just makes about 1-2 single pixels red in this whole bitmap
headSkin.buildDrawingCache();
final Bitmap bmp = headSkin.getDrawingCache();
I think you have problem on these lines. The rest of the code looks fine.
Maybe the bitmap is not initialized, so you only have a Bitmap reference, instead of Bitmap object with data inside.
Can you delete the bitmap part from your code and initialize Bitmap like this:
Bitmap myBitmap = Bitmap.createBitmap(500, 500, Bitmap.Config.RGB8888);
and then perform pixel operations like you did above, just set all the pixels to same color.
I'm using c code to get frame from a gif file and it's working fine using ffmpeg library av_read_frame, then I convert the returned image from this format BGRA to ARGB format using this method libyuv::ABGRToARGB.
In the java part I receive the bitmap and when I put it in the ImageView the transparent pixels drawn white.
Bitmap bitmap= Bitmap.createBitmap(150, 150, Bitmap.Config.ARGB_8888);
getGifFrame(gifFile,bitmap); //native method which get image frame from gif file.
imageTest.setImageBitmap(backgroundBitmap);//this bitmap in debug mode I can see that it has transparent pixels, but in drawn it appears white!
even when I loop on the returned bitmap pixels and check for each pixel I find them transparent!
for (int x = 0; x < b.getWidth(); x++)
{
for (int y = 0; y < b.getHeight(); y++)
{
int color = b.getPixel(x, y);
if (color == Color.WHITE)//This condition never occurred
{
b.setPixel(x, y, Color.TRANSPARENT);
}
}
}
Nothing happened and it's still white. Then I did convert all transparent pixels to TRANSPARENT !! and guess what, It's working!
else if (color == Color.TRANSPARENT)
{
b.setPixel(x, y, Color.TRANSPARENT);
}
I don't understand why that happen. Any help would be appreciated.
EDIT 1:
I get all pixels from the bitmap and set them again without doing anything and it worked also!?
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
bitmap.setPixels(pixels, 0, width, 0, 0, width,height);
I have a medical device that sends pixel's values in order to plot as an image (frame after frame).
I need to take the pixels and build from them an image on the screen. Currently with the code I wrote, I manage to receive 2fps for image size of 800x600.
What is the fastest way plot an image on screen? and doing it continuously.
Bitmap mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
Canvas c = new Canvas(mBitmap);
Paint paint = new Paint();
int[] colorMatrix = new int[width * height];
for (int i = 0; i < imageXY.length; i++) {
int indexValue = Integer.parseInt(strValueIndex[i]);
int pixelValue = Integer.parseInt(imageValue[indexValue - 1]);
int pixelIndex = GetXY(imageXY[i]);
//int pixelIndex = Integer.parseInt(imageXY[i].split(",")[2]);
colorMatrix[pixelIndex] = pixelValue;
}
c.drawBitmap(colorMatrix, 0, width, 0, 0, width, height, false, paint);
myImage.setImageBitmap(mBitmap);
However its take about 500ms for each frame
This is the easiest place to start OpenGL in Android: http://developer.android.com/guide/topics/graphics/opengl.html
I would also look into using multiple cores for this process; although the GPU based calculations may offer higher single core speeds, a fairly "simple" task such as this will be greatly accelerated by utilizing multiple cores and parallel processing, and shouldn't be too hard to implement. Here is an introduction to parallel processing on Android devices.
https://developer.qualcomm.com/blog/multi-threading-android-apps-multi-core-processors-part-1-2
I have a jpg image. First i want to convert it to bitmap so that i can get all pixels from my picture.
Then i want to keep the pixels into a two dimensional array as row and column of a matrix.
Then i want to search either row or column or both to match with a color.That means i want to find if a row(or a column whatever i want) of that 2D matrix contains RED color(for example). I tried something like this:
Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.laser);
bmp = bmp.copy(Bitmap.Config.ARGB_8888, true);
int[] pixels = new int[bmp.getHeight()*bmp.getWidth()];
bmp.getPixels(pixels, 0, bmp.getWidth(), 0, 0, bmp.getWidth(), bmp.getHeight());
for(int i =0; i<pixels.length;i++){
if(pixels[i]==0xffff0000)
Toast.makeText(getApplicationContext(), "this is my Toast message!!! =)", Toast.LENGTH_LONG).show();
}
Here i took pixels into an array(1D) and check if a red color is exist or not in that array.
But it didn't find although red color exists in my pic.
Here may be the array cant get pixels.
But this is not what i want. What i want mentioned above. How can i reach my goal???
if you have a Bitmap object, you can get pixels using Bitmap::getPixels method.
for example, here i get first column of an image:
// byte[] data2 contains image binary date from the network
Bitmap bitmap = BitmapFactory.decodeByteArray(data2, 0, data2.length);
int[] pixels = new int[bitmap.getHeight()];
int offset = 0;
int stride = 1;
int x = 0;
int y = 0;
int width = 1;
int height = bitmap.getHeight();
bitmap.getPixels(pixels, offset, stride, x, y, width, height);
each pixels is just an Color object so you can check it's RGB values.
I'm having the OutOfMemory error when inverting a bitmap.. Here is the code I use to invert:
public Bitmap invertBitmap(Bitmap bm) {
Bitmap src = bm.copy(bm.getConfig(), true);
// image size
int height = src.getHeight();
int width = src.getWidth();
int length = height * width;
int[] array = new int[length];
src.getPixels(array, 0, src.getWidth(), 0, 0, src.getWidth(), src.getHeight());
int A, R, G, B;
for (int i = 0; i < array.length; i++) {
A = Color.alpha(array[i]);
R = 255 - Color.red(array[i]);
G = 255 - Color.green(array[i]);
B = 255 - Color.blue(array[i]);
array[i] = Color.argb(A, R, G, B);
}
src.setPixels(array, 0, src.getWidth(), 0, 0, src.getWidth(), src.getHeight());
return src;
}
The image is ~80 kb big, the dimensions are 800x1294 and the picture has words which are black and an invisible background..
The images are in a ViewPager..
when you copy bm, try: bm = null;
In android , due to 16MB (on almost all phones) memory cap for applications, it is not wise to hold entire bitmap in memory. This is a common scenario and is happening to may developers.
You can get many information about this problem in this stackoverflow thread. But I really urges you to read android's official document about efficient usage of Bitmaps. They are here and here.
The memory size used by an image in completelly different from the file size of that image.
While in a file the image may be compressed using different alghorithms (jpg, png, etc.) and when loaded in memory as a bitmap, it uses 2 or 4 bytes per pixel.
So in your case (you are not sowing the code but it lloks like you are using 4 bytes per pixel), the memory size per image is:
size = width * height * 4; // this is aprox 2MB
In your code, first you copy the original bitmap to a new one, and then ceate an array to manipulate the colors. So in total you are using size x 3 = 6MB per image inversion.
There are plenty of examples on how to handle large bitmap in Android, but I'll leave you what I think is the most important topics:
Try to use only one copy of bitmap in your code above
If you are only having words in your image use Bitmap.Config = RGB_565. This only uses 2 bytes per pixel, reducing size by half.
Call recycle() on a bitmap that you don't need anymore.
Have a lool at scale option in Bitmap.Factory. You may reduce the size of image that still fit your needs.
good luck.