I know this question has been asked over and over again in these
forums. But the reason I am posting it again is because I have nt
received a clear answer to it. It would be great if someone could help
me with this.
I am current using opencv + android to make some simple app that will
do image manipulation like edge, blur etc and display it using an
imagebutton. The images are being loaded through the gallery. There
are 3 buttons to blur, edge, and reset to original which will display
the modified image on the image button. given below is my code.
package com.example;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.Buffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import android.app.Activity;
import android.content.Intent;
import android.graphics.*;
import android.graphics.Bitmap.Config;
import android.net.Uri;
import android.os.Bundle;
import android.os.Debug;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import org.opencv.core.*;
import org.opencv.imgproc.*;
import org.opencv.android.*;
import org.opencv.highgui.*;
import dalvik.system.VMRuntime;
import android.graphics.Color;
public class ImageProcessing extends Activity implements
View.OnClickListener{
private ImageButton choice;
private Button Blur;
private Button Canny;
private Button Reset;
Uri photoUri;
static private Bitmap bitmap;
static private Bitmap mutBitmap;
private static String TAG = "ImageProcessing";
private static int TAKE_GALLERY_REQUEST = 2;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.img);
choice = (ImageButton) findViewById(R.id.imagechoice);
Blur = (Button) findViewById(R.id.Blur);
Canny = (Button) findViewById(R.id.edge);
Reset = (Button) findViewById(R.id.reset);
choice.setOnClickListener(this);
Blur.setOnClickListener(this);
Canny.setOnClickListener(this);
Reset.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if(v.getId() == R.id.imagechoice)
{
Intent pickPhoto = new Intent(Intent.ACTION_PICK);
pickPhoto.setType("image/*");
startActivityForResult(pickPhoto, TAKE_GALLERY_REQUEST);
}
if(v.getId() == R.id.Blur)
{
if(mutBitmap != null)
mutBitmap.recycle();
mutBitmap = bitmap.copy(Config.ARGB_8888, true);
choice.setImageBitmap(mutBitmap);
Mat mImg = Utils.bitmapToMat(mutBitmap);
Size ksize = new Size(15,15);
Imgproc.blur(mImg, mImg, ksize);
mutBitmap.recycle();
mutBitmap = Bitmap.createBitmap(mImg.cols(), mImg.rows(),
Bitmap.Config.ARGB_8888);
Utils.matToBitmap(mImg, mutBitmap);
choice.setImageBitmap(mutBitmap);
}
if(v.getId() == R.id.edge)
{
if(mutBitmap != null)
mutBitmap.recycle();
mutBitmap = bitmap.copy(Config.ARGB_8888, true);
choice.setImageBitmap(mutBitmap);
Mat mImg = new Mat();
mImg = Utils.bitmapToMat(mutBitmap);
/**Converting to grayscale**/
Mat mGray = new Mat(mImg.rows(), mImg.cols(), CvType.CV_8UC1,
new Scalar(0));
Imgproc.cvtColor(mImg , mGray, Imgproc.COLOR_BGRA2GRAY, 4);
/**Applying Canny**/
Imgproc.Canny(mGray, mGray, 80, 90);
//Converting back to 4 channel image
Imgproc.cvtColor(mGray , mImg, Imgproc.COLOR_GRAY2RGBA, 4);
mutBitmap.recycle();
mutBitmap = Bitmap.createBitmap(mImg.cols(), mImg.rows(),
Bitmap.Config.ARGB_8888);
Utils.matToBitmap(mImg, mutBitmap);
choice.setImageBitmap(mutBitmap);
}
if(v.getId() == R.id.reset)
{
choice.setImageBitmap(bitmap);
}
}
@Override
protected void onPause() {
super.onPause();
Log.v(TAG,"on pause called");
Log.v(TAG,"Allocated memoryin on pause before recycle done:
"+Debug.getNativeHeapAllocatedSize());
choice.setImageBitmap(null);
if(bitmap != null)
{
Log.v(TAG,"bitmap is not null");
bitmap.recycle();
if(bitmap.isRecycled())
Log.v(TAG,"bitmap is recycled");
bitmap = null;
}
if(mutBitmap != null)
{
mutBitmap.recycle();
if(mutBitmap.isRecycled())
Log.v(TAG,"mutBitmap is recycled");
mutBitmap = null;
}
Log.v(TAG,"Allocated memory on pause after recycle done: "
+Debug.getNativeHeapAllocatedSize());
}
@Override
protected void onResume() {
super.onResume();
choice.setImageBitmap(bitmap);
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
Log.v(TAG,"onActivityResult called before loading bitmap,
Allocated
memory: "+Debug.getNativeHeapAllocatedSize());
if (resultCode == RESULT_OK) {
photoUri = intent.getData();
if (photoUri != null) {
try {
Log.v(TAG, "No crash 0");
bitmap =
Bitmap.createScaledBitmap(MediaStore.Images.Media.getBitmap(this
.getContentResolver(), photoUri), 300,
400, true);
Log.v(TAG, "No crash 1");
Log.v(TAG,"onActivityResult called after
loading bitmap,
Allocated memory:
"+Debug.getNativeHeapAllocatedSize());
choice.setImageBitmap(bitmap);
} catch (Exception e) {
Log.v(TAG, "Unable to load image at path: " +
photoUri.getPath());
}
}
}
}
}
bitmap - used for loading the image that user has selected from
gallery
mutBitmap - copy the image in bitmap and sets the mutable boolean to
true. so that I can manipulate the image
I have included calls to Debug.getNativeHeapAllocatedSize() at four
places
1)In onPause() before I recycle the bitmap(onPause is called when the
image button is clicked each time)
2) after bitmap.recycle() is called in onPause()
3) before new bitmap is loaded in onActivityResult()
4) after new bitmap is loaded in onActivityResult()
the following are the results in log cat:
>12-19 13:22:20.629: VERBOSE/ImageProcessing(4551): on pause called
>12-19 13:22:20.629: VERBOSE/ImageProcessing(4551): Allocated memoryin on pause
>before recycle done: 5508024
>12-19 13:22:20.629: VERBOSE/ImageProcessing(4551): Allocated memory on pause
>after recycle done: 5508096
>12-19 13:22:39.279: VERBOSE/ImageProcessing(4551): onActivityResult called
>before loading bitmap, Allocated memory: 5507488
>12-19 13:22:39.279: VERBOSE/ImageProcessing(4551): No crash 0
>12-19 13:22:39.839: VERBOSE/ImageProcessing(4551): No crash 1
>12-19 13:22:39.850: VERBOSE/ImageProcessing(4551): onActivityResult called
>after loading bitmap, Allocated memory: 18246120
>12-19 13:22:51.329: VERBOSE/ImageProcessing(4551): on pause called
>12-19 13:22:51.329: VERBOSE/ImageProcessing(4551): Allocated memoryin on pause
>before recycle done: 18246704
>12-19 13:22:51.339: VERBOSE/ImageProcessing(4551): bitmap is not null
>12-19 13:22:51.339: VERBOSE/ImageProcessing(4551): bitmap is recycled
>12-19 13:22:51.339: VERBOSE/ImageProcessing(4551): Allocated memory on pause
>after recycle done: 18005032
>12-19 13:23:04.779: VERBOSE/ImageProcessing(4551): onActivityResult called
>before loading bitmap, Allocated memory: 18004408
>12-19 13:23:04.779: VERBOSE/ImageProcessing(4551): No crash 0
As you can see, the second time I tried to load the same image the app
crashed. In onPause(), the nativeheapsize allocatted before and after
bitmap.recycle() is called is the same, even though bitma.isRecycled()
gives true.
I also did adb shell dumpsys meminfo and this is what I got:
When the image is loaded:
** MEMINFO in pid 3679 [com.example] **
native dalvik other total limit
bitmap nativeBmp
size: 19240 2743 N/A 21983 24576 N/
A N/A
allocated: 17871 2569 N/A 20440 N/A
12678 0
free: 360 174 N/A 534 N/A N/
A N/A
(Pss): 1205 1048 16403 18656 N/A N/
A N/A
(shared dirty): 1660 4260 1424 7344 N/A
N/A N/A
(priv dirty): 1168 832 15876 17876 N/A N/
A N/A
When the button is clicked again to load another image(i.e. onpause()
is called)
** MEMINFO in pid 3679 [com.example] **
native dalvik other total limit
bitmap nativeBmp
size: 19240 2743 N/A 21983 24576 N/
A N/A
allocated: 5186 2487 N/A 7673 N/A
0 0
free: 361 256 N/A 617 N/A N/
A N/A
(Pss): 1205 1048 3718 5971 N/A N/
A N/A
(shared dirty): 1660 4260 1424 7344 N/
A N/A N/A
(priv dirty): 1168 832 3192 5192 N/
A N/A N/A
As you can see the allocated native heap size is changing in meminfo
when image is loaded and when onPause is called. But I am not sure why
the nativeheapallocated function in code still shows the same value.
I have 2 questions now:
1) where is bitmap memory allocated. What happens when recycle()
function is called
2) What is the native referred to in meminfo and in the
Debug.getNativeHeapAllocatedSize()? Are they different?
If you think my approach is not the most effecient one I am also happy
to change it.
Thanks,
Shyama
--
You received this message because you are subscribed to the Google
Groups "Android Developers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en