From this post I took the following code. It crops a region from an original image using OpenCV4Android.
Mat uncropped = getUncroppedImage();
Rect roi = new Rect(x, y, width, height);
Mat cropped = new Mat(uncropped, roi);
Works fine, but imagine the memory for the Mat that is returned by getUncroppedImage is allocated only once. But the memory for the cropped image is re-allocated all the time. Is there a way to crop a region from OpenCV:Mat without using the Mat-constructor?
#Matthias, 'cropped' image in your code points to the same memory as 'uncropped'. No memory is reallocated. To test this you can change content of cropped image (set it to be white for example) and you will see that content of uncropped will be changed as well.
In OpenCV when you want to make two images have same content but different memory you should say so explicitly, i.e. use functions like copy, copyTo or clone. OpenCV tries to avoid memory reallocation and copy whenever possible.
Yes, you can just use a subimage:
Mat uncropped = getUncroppedImage();
Rect roi = new Rect(x, y, width, height);
Mat cropped = uncropped(roi);
that way, cropped uses the same image array data as uncropped, but only in the subimage area. The Matrix Header is created, but the pixel data array (that's what is expensive) isn't.
nearly all openCV functions can work on subimages because theyre just like normal images with another widthstep (byte length of a single row)
edit: this i c++ syntax, not sure how android openCV differs!
Related
In OpenCV I retrieve a Gabor kernel for image processing which is a 9:9 matrix using:
Imgproc.getGaborKernel(...)
I have a gray matrix of the original image. (i'm not even sure if the kernel is supposed to be the size of the image or just a small segment, I'm fairly certain of the small kernel)
How do I convolve the two and get the output of the convolution?
I'm trying to put together a Gabor wavelet filter for edge detection.
EDIT: as far as convolution of matrices seems to be concerned it looks like the opencv "filter2d" method is what is used to do it and is found in Imgproc class of Android OpenCV api.
However when I do my convolution and put it to the screen its just a black image.
Size size = new Size(9,9);
Mat gaborKernel = Imgproc.getGaborKernel(size, 3.0, -Math.PI/4, Math.PI, 10.0, Math.PI*0.5, CvType.CV_64F);
Imgproc.filter2D(intermediate, output, -1, gaborKernel);
Bitmap temp = Bitmap.createBitmap(intermediate.cols(), intermediate.rows(), Config.ARGB_8888);
Utils.matToBitmap(output, temp);
I did a system output to see the values and all of the values are extremely small as seen below.
You need to normalize your kernel.
Just loop over the kernel matrix, calculate the sum of values. Then loop again to divide each value to the sum. This ensures that your kernel does not change the overal brightness.
I'm making a game, while a friend of mine is designing all the graphics. All graphics are PNG files, and drawn and animated using Bitmaps and Canvas in SurfaceView.
One piece of animation involves the main character moving around during the menu, which uses 35 large PNG files. It's too big to have it all loaded in memory, and loading each file when needed is too slow for what he wants. So, what can I do to make this work?
OPTIONS THAT DON'T WORK
Making the image smaller. Making the image smaller than what he wanted won't fly. Anything less than original quality won't fly either.
OPTIONS WE'RE LOOKING INTO
Making the animation a video. However, I don't know how to take a video and turn it into an interface.
My code:
Matrix matrix = new Matrix();
matrix.postScale(scaleFactorX, scaleFactorY);
penguin = BitmapFactory.decodeResource(getResources(), R.drawable.fish_01);
penguin = Bitmap.createBitmap(penguin, 0, 0, penguin.getWidth(), penguin.getHeight(), matrix, true);
canvas.drawBitmap(penguin, canvasWidth - penguin.getWidth(), canvasHeight*0.02f, null);
I'm trying to dynamically create images in android by taking an existing Bitmap and removing the centre of it in order to make a "cropped" version. The resulting image's height would naturally be smaller than the original, something like the attached example.
I've got a rough way of doing this by creating two new Bitmaps from the original, one containing the top of the image above the crop section (e.g. the android's head in the example) and the other containing the remaining image below the crop section (the android's feet) using the Bitmap.createBitmap(source, x, y, width, height) method, then drawing both of these bitmaps onto a canvas of a size equal to the original image minus the removed space.
This feels a bit clunky, and as I could be calling this method several times a second, it seems wasteful to create two bitmaps each time.
I was wondering if there was a more efficient way of doing this. Something like drawing the original Bitmap onto a canvas using a Path with it's Paint's xfermode set to a
new PorterDuffXfermode(Mode.DST_OUT) in order to cut out the portion of the image I wish to delete. But this seems to clear that area and not shrink the image down i.e. it leaves a big empty gap in the Android's middle.
Any suggestions greatly appreciated!
Why do you create two bitmaps? You only need to create one bitmap and then do canvas.drawBitmap() twice.
Bitmap bmpOriginal;
Bitmap bmpDerived = Bitmap.create(...);
Canvas canvas = new Canvas(bmpDerived);
canvas.drawBitmap(bmpOriginal, rectTopSrc, rectTopDst, null);
canvas.drawBitmap(bmpOriginal, rectBottomSrc, rectBottomDst, null);
Done.
I'm sorry if this topic has been brought before, but all my searches on the web and google groups did not help me.
I'm currently developing a little game with the Android SDK, and use hi-res bitmaps that I resize accordingly to match the device's resolution (letting the system do it for me is
not "crisp" enough).
I use a SurfaceView, on which I paint in one pass a canvas filling the whole surface. The paint uses setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)) to allow masking.
Beforehand, I retrieve various bitmaps -- which are resized at initialization with createScaledBitmap() and put in a cache -- and I apply the bitmaps with a paint on this canvas, before drawing this canvas on the SurfaceView.
My problem is, whatever I try, whatever paint settings I use (dithering, antialias, etc..), the resized bitmaps are not antialiased and the drawing present jagged edges. I tried everything.
The only little success I had was using inSampleSize to approach the desired scaled size and force a first pass of antialiasing, before invoking createScaledBitmap on the retrieved
hi-res bitmap, but it is not beautiful enough. I just can't allow to create multitudes of pre-sized bitmaps for every combination of resolution. What did I miss ?
Thanks a lot in advance
First when you load your bitmap you make sure that you don't lose any image quality by settings options to argb_8888:
Options options = new Options();
options.inScaled = false;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap pic = BitmapFactory.decodeResource(getResources(), R.id.pic, options);
When you scale the bitmap turn on the filter:
pic = Bitmap.createScaledBitmap(pic, screenW, screenH, true);
However if one streaches the image too much inevitably it degrades in quality.
When you use paint you can improve quality but lose on speed with turning on ditherig and filtering:
Paint paint = new Paint();
paint.setFlags(Paint.DITHER_FLAG);
paint.setFilterBitmap(true);
Finally the entire activity window could be set on argb_4444 instead on argb_8888 (OS < 2.3). You can chage this if you instert this line before setContentView:
getWindow().setFormat(PixelFormat.RGBA_8888);
If it comes down to it, you can manually antialias without too much trouble. Just apply a simple lowpass filter (something like an NxN average) to the pixel data before asking the bitmap object to rescale itself.
you may clear canvas buffer by youself! such as follows:
canvas.drawColor(Color.TRANSPARENT, android.graphics.PorterDuff.Mode.CLEAR);
Say I have a somewhat large (i.e. not fit in most phones' memory) bitmap on disk. I want to draw only parts of it on the screen in a way that isn't scaled (i.e. inSampleSize == 1)
Is there a way to load/draw just the part I want given a Rect specifying the area without loading the entire bitmap content?
I'm quite confident this is possible since you can load a really large bitmap file into an ImageView without problems so there must be some sort of a built-in way to handle large bitmaps... and after a few attempts, I've found a solution:
Instead of loading the entire bitmap and manually draw it yourself, load it as a Drawable instead:
InputStream mapInput = getResources().openRawResource(
R.drawable.transit_map);
_map = Drawable.createFromStream(mapInput, "transit_map");
_map.setBounds(0, 0, _mapDimension.width(), _mapDimension.height());
I'm using a resource file but since you can use Drawable.createFromStream to load image from any InputStream, it should works with arbitrary bitmap.
Then, use the Drawable.draw method to draw it onto the desired canvas like so:
int left = -(int) contentOffset.x;
int top = -(int) contentOffset.y;
int right = (int) (zoom * _mapDimension.width() - contentOffset.x);
int bottom = (int) (zoom * _mapDimension.height() - contentOffset.y);
_map.setBounds(left, top, right, bottom);
_map.draw(canvas);
As in the above case, You can also scale and translate the bitmap as well by manipulating the drawable's bounds and only the relevant parts of the bitmap will be loaded and drawn onto the Canvas.
The result is a pinch-zoomable view from just one single 200KB bitmap file. I've also tested this with a 22MB PNG file and it still works without any OutOfMemoryError including when screen orientation changes.
Now it's very relevant: BitmapRegionDecoder.
Note: available since Android SDK 10
It can easily be done by using RapidDecoder.
import rapid.decoder.BitmapDecoder;
Rect bounds = new Rect(10, 20, 30, 40);
Bitmap bitmap = BitmapDecoder.from("your-file.png")
.region(bounds)
.decode();
imageView.setImageBitmap(bitmap);
It supports down to Android 2.2 (API Level 8).
Generally speaking, that isn't possible, particularly since most image formats are compressed, so you don't even know which bytes to read until you've extracted the uncompressed form.
Break your image up into small tiles and load just the tiles you need to cover the region you want to display at runtime. To avoid jittery scrolling, you might also want to preload tiles that are just out of sight (the ones that border the visible tiles) on a background thread.