I am developing an app that deals with images and applying filters, borders etc to the image..(like photo editing app).
I am facing issue when applying filters or borders to HD images (as images will be of different size and resolution).
My Question is how do I apply filters without changing the size or dimension of the Image.
Please help regarding this, Thanks in advance.
As I mentioned in the comment section of the Code, for smaller size images getDrawingCache is returning a value, but for the bigger size images it is returning null.
public Bitmap getBorderAppliedBitmap(PorterShapeImageView imgView , GPUImage gpuImage)
{
Bitmap bmp = gpuImage.getBitmapWithFilterApplied();
imgView.setImageBitmap(bmp);
imgView.setDrawingCacheEnabled(true);
imgView.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
imgView.layout(0, 0,
imgView.getMeasuredWidth(), imgView.getMeasuredHeight());
imgView.buildDrawingCache(true);
Bitmap bmap = Bitmap.createBitmap(imgView.getDrawingCache());
<!-- getting null from the method imgView.getDraingCache() only for the bigger size Images -->
imgView.setDrawingCacheEnabled(false);
return bmap;
}
What you must be facing is limited Android resources issue for Image Processing techniques. The best advice for you would be to shift towards NDK and write image filters in JNI using OpenCV .
It is the best option for faster results in HD images. Any how there must be a boundary to the resolution to which an image can be processed because all professional apps create their own boundary parameters.
Related
I am using chrisbanes's photoview library for zoomable images. https://github.com/chrisbanes/PhotoView
Image i am trying to load has 2000x2000 resolution.
Simply, I first load the bitmap, then I draw something to the bitmap I loaded. then I load the bitmap to the imageview. I need to manipulate the source image differently for each use, so I need to create a bitmap to create a canvas. But at line where creating bitmaps, in some devices the app throws outOfMemoryException. I check the logs and the original bitmap size is 5250x5250 and creating another bitmap with same size drains a lot of memory i think.
I have tried loading bitmap with BitmapFactory.Options, i used inSampleSize but it degraded the image.
PhotoView imageView;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.test_image);
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setAntiAlias(true);
Bitmap tempBitmap = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(),Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(tempBitmap);
canvas.drawBitmap(bitmap,0,0,null);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(4);
//drawing operations, like
//canvas.drawCircle(tempX,tempY,tempRadius,paint);
//canvas.drawLine(firstX,firstY,secondX,secondY,paint);
imageView.setImageDrawable(new BitmapDrawable(getResources(),tempBitmap));
Since i use photoview, the imageview is zoomable and i need the image is well detailed even if fully zoomed but using inSampleSize regrades the image. I want it to be memory efficient and smooth as possible.
You should be able to use Subsampling Scale Image View for the purpose of processing large images without encountering the OutOfMemoryError. As recommended by others, it's not recommend to use large images but this should assist you achieve the functionality you desire.
The view optionally uses subsampling and tiles to support very large images - a low resolution base layer is loaded and as you zoom in, it is overlaid with smaller high resolution tiles for the visible area. This avoids holding too much data in memory. It's ideal for displaying large images while allowing you to zoom in to the high resolution details. You can disable tiling for smaller images and when displaying a bitmap object.
The important thing about this I want to point is: -
Tested up to 20,000x20,000px, though larger images are slower
I'm developing for Android, and my program involves a lot of bitmap images - these are set using BitmapFactory. One of my images is 80x30, and I assign it to a Bitmap variable wit the following instruction:
bmapImg = BitmapFactory.decodeResource(currContext.getResources(), R.drawable.brick);
When I then use the instruction
myImgWidth = bmapImg.getWidth();
myImgWidth is evaluated to 160 instead of 80. Likewise, when I run
myImgHeight = bmapImg.getHeight();
myImgHeight is evaluated to 60 instead of 30. I've looked through other questions on StackOverflow relating to these methods, but all of those issues were about getWidth() and getHeight() returning 0. Am I doing something wrong, or is there a reason as to why these functions return double the actual image dimensions?
You using decodeResource, so image is scaled based on your DPI. To scale it to size you need, place it in different folder. I suggest drawable_xhdpi is what you need, if scale factor is 2.
And keep in mind, it will be scaled on devices with different screen density.
To completely avoid scaling you may put your image to assets folder for example.
It is all about which screen you are running your app on. The values returned by those methods are the pixels that the image takes on the current screen. They will return values that are 1:1 with your image's resolution if you are running on a mdpi screen and you used 80dp and 30dp respectively for their dimensions. I would bet you are probably running your app on a xhdpi screen and do not have a resource for that density bucket
As mentioned in other answer, image is resized based on the device you check on.
If you don't want android to resize your image based on DPI, I recommend you to add your image in drawable-nodpi folder of your project
I recently ran into some OutOfMemory error in an android application I am working on, mostly because I am loading the images in their original size (now I know this is a bad idea).
Im now working on implementing methods to load scaled down versions of the images depending on the actual ImageView size and caching them as suggested in googles developer guide.
The guide states out very well how I should process and handle images loaded at runtime in Java code, but it leaves out how to work with images defined via XML in my layout files.
For example if I have an ImageView with a predefined Image, the XML code would look something like this:
<ImageView
android:id="#+id/my_image_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/my_image"
/>
If I now want to replace that image at runtime, im checking the width and height of the ImageView, and load the new image according to these dimensions:
imageView.setImageBitmap(decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight)
Obviously, this has not be done for the Image defined via XML.
Do I have to worry about these images, too, or does the system handle the downscaling for them? Or should I avoid declaring the src of an imageview via XML at all?
Thanks in advance,
danijoo
When you set in an image from your drawable folder android does take care for you. But in that case you have to help too :) . For example if you only provide an image for drawable-mdpi and you do run your application in hpdi devices android will scale up the image to fit it in the screen.But of course before it does it will try to find hdpi version of that image into drawable-hdpi folder. If you do provide hdpi version of that image into drawable-hdpi folder it will not scale.
Now as for your OutOfMemoryError i think you might've put your image in only drawable or any of the low dpi folder and you haven't put that image's larger version to other high dpi folders.(hdpi/xhdpi/xxhdpi).
As per documentation:
Provide different bitmap drawables for different screen densities:
By default, Android scales your bitmap drawables (.png, .jpg, and .gif files) and Nine-Patch drawables (.9.png files) so that they render at the appropriate physical size on each device. For example, if your application provides bitmap drawables only for the baseline, medium screen density (mdpi), then the system scales them up when on a high-density screen, and scales them down when on a low-density screen.
I think you will find more useful information in :screens_support
Use below code to scale down size of image when you are selecting it from Gallery or Camera.
Intent photoPickerIntent = new Intent(
Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
// photoPickerIntent.setType("image/*");
photoPickerIntent.putExtra("crop", "true");
photoPickerIntent.putExtra("outputX", 512);
photoPickerIntent.putExtra("outputY", 512);
photoPickerIntent.putExtra("aspectX", 1);
photoPickerIntent.putExtra("aspectY", 1);
photoPickerIntent.putExtra("scale", true);
This will load image of required size into your app and system will not run out of memory.
You can ask if you have any further queries.
I create 14 Bitmap objects like this:
bmp[0]=BitmapFactory.decodeResource(getResources(), R.drawable.a0000);
bmp[1]=BitmapFactory.decodeResource(getResources(), R.drawable.a0001);
bmp[2]=BitmapFactory.decodeResource(getResources(), R.drawable.a0002);
bmp[3]=BitmapFactory.decodeResource(getResources(), R.drawable.a0003);
bmp[4]=BitmapFactory.decodeResource(getResources(), R.drawable.a0004);
bmp[5]=BitmapFactory.decodeResource(getResources(), R.drawable.a0005);
bmp[6]=BitmapFactory.decodeResource(getResources(), R.drawable.a0006);
bmp[7]=BitmapFactory.decodeResource(getResources(), R.drawable.a0007);
bmp[8]=BitmapFactory.decodeResource(getResources(), R.drawable.a0008);
bmp[9]=BitmapFactory.decodeResource(getResources(), R.drawable.a0009);
bmp[10]=BitmapFactory.decodeResource(getResources(), R.drawable.a0010);
bmp[11]=BitmapFactory.decodeResource(getResources(), R.drawable.a0011);
bmp[12]=BitmapFactory.decodeResource(getResources(), R.drawable.a0012);
bmp[13]=BitmapFactory.decodeResource(getResources(), R.drawable.a0013);
those Bitmaps are used to make an animation of the background, the resources are 14 jpj images of 320x480 pixels. so I created a custom view and then call the bitmaps at the onDraw method. Then I test the app on my phone that have the same resolution of my images, it run smoothly, then I try to use the app on a tablet with a larger resolution(like 1280x720), but my background don't fit the entire screen, so I investigate how fill the entire screen by make a new bitmap with different resolution based on the device resolution and the result was this:
newWidth= context.getResources().getDisplayMetrics().widthPixels;
newHeight= context.getResources().getDisplayMetrics().heightPixels;
int contador=0;
do{
bmp[contador]= Bitmap.createScaledBitmap(bmp[contador], newWidth, newHeight,true);
contador++;
}while(contador<13);
Then I simply put these line on my onDraw method.
canvas.drawBitmap(bmp[i], 0, 0, null);
i++;
If I test the app on my phone it runs fine but when I test it on my tablet throws me the "bitmap size exceeds VM budget" error... So I investigate a little bit more and I had the following solution: Create the original 14 bmp Bitmap objects and make a second array of Bitmaps called bmp2 and then only assign the bitmap when I need it and then clear it like this on my onDraw method:
bmp2[i]= Bitmap.createScaledBitmap(bmp[i], newWidth, newHeight,true);
canvas.drawBitmap(bmp2[i], 0, 0, null);
bmp2[i].recycle();
bmp2[i]=null;
It work on my tablet and my phone but with a really bad performance I assume is because I assign the image in real time (on the onDraw method) but if I use the recycle method and don't assign again the bmp2 there will be not a new bmp2...
So finally my real question is: How can I clear some memory after I show my image and then when I need it again I can use it. Thanks!
Other have mentioned how scaling up small bitmaps to fill a large screen will not give high-quality results. But anyway, to answer your specific question, this is how you can draw your low-res bitmaps scaled up to fill the screen without memory errors.
You need to use one of the alternative Canvas.drawBitmap() methods which can scale your bitmap to the correct size while drawing.
For example:
public void drawBitmap (Bitmap bitmap, Rect src, RectF dst, Paint paint)
From the Javadoc:
Draw the specified bitmap, scaling/translating automatically to fill
the destination rectangle.
Set the dst rectangle to be the screen size you calculated earlier.
Then, you do not need to create separate scaled bitmaps in memory, so your problem goes away.
I am afraid there is no safe way of doing a fullscreen animation using scaled bitmaps.
Why?
Let's do some counting. Each your initial bitmap is 320x480, yes? That's 320*480*4*14=8601600, which is roughly 8Mb of memory. When you scale to 1280x720, you add up 1280*720*4*14=51609600 bytes, which is 49 megabytes. 49 + 8 = 57 megabytes. Given that the newest tablets have maximum of 64 megabytes allocated for your app VM, no surprise it won't fit. And even if it fits with your tablet, what would happen if a Nexus 10 owner would buy your app? N10 has 2560Ă—1600 resolution (not to mention that scaled bitmap would look pathetic).
Allocating single bitmap for single frame will hardly let you have a smooth animation, I'm afraid. You could try keeping a window of 5 bitmaps at once and recycling others, but still I don't thik it would be enough. Besides that would consume ton of CPU work for constant bitmap scaling and will destroy your battery.
If you really want an animated background, you should probably look at either OpenGL, or TextureView.
Most of the times the GC would do the job.
You should have different bitmap resolutions handled by Android by folder (xhdpi, hdpi, etc.).
You should also check BitmapFactory.Options in order to have lower quality version Bitmaps generated by your BitmapFactory signatures.
Edit
You might also want to consider using WeakReferences to your Bitmaps.
You should design new bitmaps for your Tablets and Other Higher Screens and put them in the xhdpi folder under the res directory. This is the most efficient option. Let The Android OS itself pull the best fitting Resource for you.
See this for more detailed help
http://developer.android.com/training/multiscreen/index.html
This is what the documentation says
Although the system performs scaling and resizing to make your
application work on different screens, you should make the effort to
optimize your application for different screen sizes and densities. In
doing so, you maximize the user experience for all devices and your
users believe that your application was actually designed for their
devices—rather than simply stretched to fit the screen on their
devices.
I've just encontered a tough problem...
Here's my story:
My device is Samsung Galaxy Note, normally, it takes pictures at a resolution of 3264 * 2448 pixels. I love fine pictures when am tring to find detailed information, but here I hate it because it's huge to handle in Android. I can read and display an image of this big resolution, I need to draw lines on top of it, then I need to save both picture and lines to a png.
My headache is that my Android always gives me Out of memery exception, when am trying to create a mutable bitmap like this:
Bitmap bmp = Bitmap.createBitmap(3264, 2448, Config.RGB_565);
Canvas cv = new Canvas(bmp);
//draw a line and save and restore canvas
// I found an alternative solution yet proven to be uncorrect:
myImageView.setDrawingCacheEnabled(true);
myImageView.buildDrawingCache(true);
Bitmap bmp = myImageView.getDrawingCache();
//save bmp to png
When the
bmp.width * bmp.height * 4 > getScaledMaximumDrawingCacheSize()
exception jumps out. I've tested myImageView.measure(...) and myImageView.layout(...), didn't work for me.
(Things are fine, when I test the same code using smaller images)
Any experts know how to kill this problem or throw a solution to me? Thanks!
Maybe not the definitve solution, but did you try to increase heap size?
For Android 2.2 or lower versions:
dalvik.system.VMRuntime.getRuntime().setMinimumHeapSize(sizeInBytes);
For Android 2.3 or or higher versions:
android:largeHeap="true"