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
Related
I'm working on a word game and I was dynamically creating the textures for the letter tiles when the game loads, comprising of a background image and a font.
To do this I was drawing pixmaps onto pixmaps, this was all fine until I started working on scaling. The font scaling on the pixmaps was terrible, even with bilinear filtering turned on (left image below) even though my scaled fonts were looking pretty good elsewhere.
So I decided to get round this I'd use a frame buffer, render everything to that and then copy that out to a pixmap and create a texture from that. That way I could use the gpu filtering and it should look exactly the same as my other fonts, (middle image below) but it still didn't look quite as nice as the other fonts. A slight dark line round the outside, it looks like the alpha blending isn't working properly.
I then tried drawing straight over the tiles with the font at runtime to make sure it wasn't my imagination, and this definitely looks better with smooth blending into the image below (right image below), but this impacts my frame rate quite a lot.
So my question is, why is drawing to the frame buffer not producing the same result as when I draw to the screen? Code below.
Texture tx = Assets.loadTexture("bubbles/BubbleBlue.png");
tx.setFilter(TextureFilter.Linear, TextureFilter.Linear);
SpriteBatch sb = new SpriteBatch();
FrameBuffer fb = new FrameBuffer(Format.RGBA8888,
LayoutManager.getWidth(), LayoutManager.getHeight(), false);
fb.begin();
sb.begin();
sb.draw(tx, 0, 0, LetterGrid.blockWidth, LetterGrid.blockHeight);
Assets.candara80.font.getRegion().getTexture()
.setFilter(TextureFilter.Linear, TextureFilter.Linear);
Assets.candara80.setSize(0.15f);
TextBounds textBounds = Assets.candara80.getBounds(letter);
Assets.candara80.drawText(sb, letter,
(LetterGrid.blockWidth - textBounds.width) / 2,
(LetterGrid.blockHeight + textBounds.height) / 2);
sb.end();
Pixmap pm = ScreenUtils.getFrameBufferPixmap(0, 0,
(int) LetterGrid.blockWidth, (int) LetterGrid.blockHeight);
Pixmap flipped = flipPixmap(pm);
result = new Texture(flipped);
fb.end();
pm.dispose();
flipped.dispose();
tx.dispose();
fb.dispose();
sb.dispose();
set PROJECTION is the problem.
EXAMPLE
public Texture texture(Color fg_color, Color bg_color)
{
Pixmap pm = render( fg_color, bg_color );
texture = new Texture(pm);//***here's your new dynamic texture***
disposables.add(texture);//store the texture
}
//---------------------------
public Pixmap render(Color fg_color, Color bg_color)
{
int width = Gdx.graphics.getWidth();
int height = Gdx.graphics.getHeight();
SpriteBatch spriteBatch = new SpriteBatch();
m_fbo = new FrameBuffer(Format.RGB565, (int)(width * m_fboScaler), (int)(height * m_fboScaler), false);
m_fbo.begin();
Gdx.gl.glClearColor(bg_color.r, bg_color.g, bg_color.b, bg_color.a);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
/**set PROJECTION**/
Matrix4 normalProjection = new Matrix4().setToOrtho2D(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
spriteBatch.setProjectionMatrix(normalProjection);
spriteBatch.begin();
spriteBatch.setColor(fg_color);
//do some drawing ***here's where you draw your dynamic texture***
...
spriteBatch.end();//finish write to buffer
pm = ScreenUtils.getFrameBufferPixmap(0, 0, (int) width, (int) height);//write frame buffer to Pixmap
m_fbo.end();
// pm.dispose();
// flipped.dispose();
// tx.dispose();
m_fbo.dispose();
m_fbo = null;
spriteBatch.dispose();
// return texture;
return pm;
}
I'm loading a big jpeg file from a url using an InputStream from a URLConnection. The goal is to get an int[] with the image data as this is more efficient than using a Bitmap for further use. There are two options here.
The first is to create a Bitmap object and to copy the results in an int[]. This works in my application but the full image is in memory twice upon loading as the image data is copied into the int[] image.
Bitmap full = BitmapFactory.decodeStream(conn.getInputStream());
full.getPixels(image, 0, width, 0, 0, width, height);
To save memory, I'm trying to perform this process in a tiled fashion using a BitmapRegionDecoder.
int block = 256;
BitmapRegionDecoder decoder = BitmapRegionDecoder.
newInstance(conn.getInputStream(), false);
Rect tileBounds = new Rect();
// loop blocks
for (int i=0; i<height; i+=block) {
// get vertical bounds limited by image height
tileBounds.top = i;
int h = i+block<height ? block : height-i;
tileBounds.bottom = i+h;
for (int j=0; j<width; j+=block) {
// get hotizontal bounds limited by image width
tileBounds.left = j;
int w = j+block<width ? block : width-j;
tileBounds.right = j+w;
// load tile
tile = decoder.decodeRegion(tileBounds, null);
// copy tile in image
int index = i*width + j;
tile.getPixels(image, index, width, 0, 0, w, h);
}
}
Technically this works and I get the full image in the int[] image. Also the tiles are seemlessly inserted into the image.
Now my problem. The second method results in an image which has some kind of strange checkerboard distortion. Pixels seem to alternate between being slightly darker or slightly lighter. BitmapRegionDecoder is supposed to support jpeg, and BitmapFactory.decodeStream has no problems. What is the problem here?
Found it! apparently if you feed null into decoder.decodeRegion(tileBounds, null); it returns a Bitmap with quality Bitmap.Config.RGB_565 (not sure if this is device dependant). Simply feeding it a new options set returns a Bitmap of Bitmap.Config.RGB_ARGB8888 quality. By default this preferred quality is set.
BitmapFactory.Options options = new BitmapFactory.Options();
...
// load tile
tile = decoder.decodeRegion(tileBounds, options);
Thanks for your self-investigation!
Though I would recommend avoid relying on some default and make it clear:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig=Config.ARGB_8888; //explicit setting!
result_bitmap=regionDecoder.decodeRegion(cropBounds, options);
Thanks!
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.
I'm working on an app that needs to apply perspective distortion correction to a photo taken with the phone's camera.
Once the photo is taken, the idea is to show it on an imageview and let the user mark the four corners of the document (a card, a sheet of paper, etc.) and then apply the correction based on those points.
This is an example of what im trying to achieve:
http://1.bp.blogspot.com/-ro9hniPj52E/TkoM0kTlEnI/AAAAAAAAAbQ/c2R5VrgmC_w/s640/s4.jpg
Any ideas on how to do this on android?
You don't have to use a library for this.
You can just as well use one of the drawBitmap functions of the Canvas class with a matrix that's initialized using the setPolyToPoly function of the Matrix class.
public static Bitmap cornerPin(Bitmap b, float[] srcPoints, float[] dstPoints) {
int w = b.getWidth(), h = b.getHeight();
Bitmap result = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
Canvas c = new Canvas(result);
Matrix m = new Matrix();
m.setPolyToPoly(srcPoints, 0, dstPoints, 0, 4);
c.drawBitmap(b, m, p);
return result;
}
(The Paint object is only needed to enable anti-aliasing.)
Usage:
int w = bitmap.getWidth(), h = bitmap.getHeight();
float[] src = {
0, 0, // Coordinate of top left point
0, h, // Coordinate of bottom left point
w, h, // Coordinate of bottom right point
w, 0 // Coordinate of top right point
};
float[] dst = {
0, 0, // Desired coordinate of top left point
0, h, // Desired coordinate of bottom left point
w, 0.8f * h, // Desired coordinate of bottom right point
w, 0.2f * h // Desired coordinate of top right point
};
Bitmap transformed = cornerPin(bitmap, src, dst);
Where src are the coordinates of the source points, dst are the coordinates of the destination points. Result:
What you want to do goes under various names of art, "corner-pin" being the one commonly used in the visual effects industry. You need to proceed in two steps:
Compute the mapping from the the desired, rectified image, to the original, distorted, image
Actually warp the original image according to the mapping computed in (1).
The 4 (non-collinear, perspective-distorted) corners of the original image, and the 4 corners of the target (undistorted) image, define the mapping. This mapping is called a "homography" - read the pointed wikipedia page for details. Once the mapping is known, the warping at step (2) can be computed by interpolation: for every pixel in the target image, find the corresponding pixel in the original image. As this will typically not be at integer coordinates, you interpolate its color from the neighbors. Various interpolation schemes are used, the common ones being nearest-neighbor, bilinear and bicubic (in increasing order of smoothness in the results).
For Android, I'd recommend installing the OpenCV SDK , and then use the geometry transformation routines (getPerspectiveTransform and warpPerspective for the two steps above).
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.