I am new to android.I have an application which has functionality to upload image from the gallery or take image from camera. Below I have written the code where I get the image.
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode!=0){
if(PICK_MEDIA_FROM_GALLERY == requestCode){
if(null != data){
Uri uri = data.getData();
//String type = getMimeType(uri);
filePath = getRealPathFromURI(uri);
checkFileSupport(filePath);
}
}else{
if(ACTION_TAKE_PHOTO == requestCode){
checkFileSupport(filePath);
}else if(ACTION_TAKE_VIDEO == requestCode){
handleCameraVideo();
}
}
}
}
This function is used to fix the write orientation for the image.
public Bitmap fixOrientation(Bitmap bmp) {
try {
ExifInterface exif = new ExifInterface(filePath);
int orientation = exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
if (orientation==6)
{
Matrix matrix = new Matrix();
matrix.postRotate(90);
bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix, true);
}
else if (orientation==8)
{
Matrix matrix = new Matrix();
matrix.postRotate(270);
bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix, true);
}
else if (orientation==3)
{
Matrix matrix = new Matrix();
matrix.postRotate(180);
bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix, true);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return bmp;
}
I set Image to imageview using the function
private void setImagePreview(final String realPath) throws IOException {
Display display = getWindowManager().getDefaultDisplay();
Bitmap bitmap = ImageUtils.getThumbnailBitmap(realPath, display.getWidth());
if(null!=bitmap){
//rotate image and save in the same filepath
bitmap = fixOrientation(bitmap);
FileOutputStream fOut = new FileOutputStream(filePath);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fOut);
fOut.flush();
fOut.close();
RelativeLayout tempLayout = (RelativeLayout) findViewById(R.id.previewLayout);
tempLayout.setVisibility(View.VISIBLE);
ImageView previewImageView = (ImageView) findViewById(R.id.previewImageView);
previewImageView.setImageBitmap(bitmap);
}
}
I have the an image in Camera.
When I select that image from Gallery for the first time,
When I try Changing the attachment and select the same picture the view appears like this
How can I solve this issue?Can anyone please help me out?
Your code seems to be OK. Whenever I resize the image I use BitmapFactory.Options class. See this class.
What Bitmap bitmap = ImageUtils.getThumbnailBitmap(realPath, display.getWidth()); returns? A full bitmap or a Thumbnail? Thumbnail is a pretty small.
It seems that you are changing the original image instead of copy of the image. Get the file path and create your image by yourself. And, to avoid OutOfMemoryException use BitmapFactory.Options property InSampleSize. I am not sure about property. Just Google out and you will find.
I hope this helps you solve your problem.
Your second image quality seems worse than the first one. You can check whether you set ImageView width and height wrap_content? If yes, try to set a fixed value for test. and whether the two bitmap sizes are same, width and height?
Related
I use this photo filter from https://github.com/Zomato/AndroidPhotoFilters to develop my app
this is my code when i got IllegalStateExpression
Caused by: java.lang.IllegalStateException
at android.graphics.Bitmap.setPixels(Bitmap.java:1556)
at com.zomato.photofilters.imageprocessors.ImageProcessor.doBrightness(ImageProcessor.java:45)
at com.zomato.photofilters.imageprocessors.subfilters.BrightnessSubfilter.process(BrightnessSubfilter.java:28)
at com.zomato.photofilters.imageprocessors.Filter.processFilter(Filter.java:88)
at org.d3ifcool.photostation.PhotoEditorActivity.onCreate(PhotoEditorActivity.java:103)
at org.d3ifcool.photostation.PhotoEditorActivity.onCreate(PhotoEditorActivity.java:104)
on Bitmap image1 = mMyFilter.processFilter(mOriginalImage);
BitmapFactory.Options mOriginalOption = new BitmapFactory.Options();
mOriginalOption.inSampleSize = 2;
Bitmap mOriginalImage = BitmapFactory.decodeFile(selectedImagePath, mOriginalOption);
mMyFilter = SampleFilters.getBlueMessFilter();
Bitmap image1 = mMyFilter.processFilter(mOriginalImage);
loadFilter(image1);
But it'll success if i use this code
context = this.getApplicationContext();
Bitmap outputImage = mMyFilter.processFilter(Bitmap.createScaledBitmap(
BitmapFactory.decodeResource(
context.getResources(), R.drawable.ic_bg_main_activity), 640, 640, false));
loadFilter(outputImage);
And this the methode from the documentation
public Bitmap processFilter(Bitmap inputImage) {
Bitmap outputImage = inputImage;
if (outputImage != null) {
for (SubFilter subFilter : subFilters) {
try {
outputImage = subFilter.process(outputImage);
} catch (OutOfMemoryError oe) {
System.gc();
try {
outputImage = subFilter.process(outputImage);
} catch (OutOfMemoryError ignored) {
}
}
}
}
return outputImage;
}
Why cant i use the BitmapFactory.decodeFile?
I need to pick the image from SD Card, not from the drawable resouce.
Does .decodeFile and .decodeResource have a different type of Bitmap?
Sorry for my bad english
So, as the Bitmap returned by BitmapFactory.decodeFile is not null, your problem is with the processFilter method.
In the second example (the one you say it works), you create a scaled Bitmap before calling processFilter. So in order to solve your issue, you should probably do:
BitmapFactory.Options originalOption = new BitmapFactory.Options();
originalOption.inSampleSize = 2;
Bitmap bitmap = BitmapFactory.decodeFile(selectedImagePath, originalOption );
if (bitmap != null) {
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, 640, 640, false);
mMyFilter = SampleFilters.getBlueMessFilter();
Bitmap image1 = mMyFilter.processFilter(scaledBitmap);
loadFilter(image1);
} else {
Log.e("Decode image", "Decoded Bitmap is null");
// Manage the error
}
Before applying filter you need to set its width and height for custom scaling then you can apply zomato(basic image processing library) filters on your bitmap image otherwise it will give you error.
public static void filterApply(Filter filter){
Bitmap bitmcopy = PhotoModel.getInstance().getPhotoCopyBitmap();
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmcopy, bitmcopy.getWidth()-1, bitmcopy.getHeight()-1, false);
filter.processFilter(scaledBitmap);
filterImage.setImageBitmap(scaledBitmap);
}
i M passing a bitmap from one activity to other, after taking the screen shot
Bitmap bitmap;
bitmap = takeScreenshot();
try {
//Write file
String filename = "bitmap.png";
FileOutputStream stream = this.openFileOutput(filename, Context.MODE_PRIVATE);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
//Cleanup
stream.close();
bitmap.recycle();
//Pop intent
Intent in1 = new Intent(this, FinalImageShare.class);
in1.putExtra("image", filename);
startActivity(in1);
} catch (Exception e) {
e.printStackTrace();
}
here i am getting the image in other activity , the problem is that the tool bar height is also coming (i m hiding the toool bar by setVisibility, )i want to crop the image so that toolbar height wont come.TIA
imageView=(ImageView)findViewById(R.id.imageView);
String filename = getIntent().getStringExtra("image");
try {
FileInputStream is = this.openFileInput(filename);
bmp = BitmapFactory.decodeStream(is);
is.close();
} catch (Exception e) {
e.printStackTrace();
}
You can use createBitmap method, like so:
resizedbitmap = Bitmap.createBitmap(bitmap, 0, 0, yourwidth, yourheight);
Where bitmap is the bitmap you create, and resizedbitmap is the cropped one.
createBitmap() method takes as parameter in this case: bitmap, start X, start Y, width and height.
You can use those two methods to get your width and height:
bitmap.getWidth(), bitmap.getHeight()
Check also this link to learn about the createBitmap method:
Developer site
Or you can use the drawing cache property for this, like this :
View main = findViewById(R.id.view);
Bitmap screenshot;
main.setDrawingCacheEnabled(true);
screenshot = Bitmap.createBitmap(main.getDrawingCache());
main.setDrawingCacheEnabled(false);
Or similar to the last one, ctx.getWindow().getDecorView() View to get full screen bitmap cache:
View view = ctx.getWindow().getDecorView();
view.setDrawingCacheEnabled(true);
Bitmap bmap = view.getDrawingCache();
int contentViewTop = ctx.getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop(); /* skip status bar in screenshot */
Storage.shareBitmapInfo = Bitmap.createBitmap(bmap, 0, contentViewTop, bmap.getWidth(), bmap.getHeight() - contentViewTop, null, true);
view.setDrawingCacheEnabled(false);
Hope this helps!
When I use following code, it ends up with outofmemory exception. After doing researh Render script looks like a good candidate. Where can I find sample code for similar operation and how can integrate it to my project.
public Bitmap rotateBitmap(Bitmap image, int angle) {
if (image != null) {
Matrix matrix = new Matrix();
matrix.postRotate(angle, (image.getWidth()) / 2,
(image.getHeight()) / 2);
return Bitmap.createBitmap(image, 0, 0, image.getWidth(),
image.getHeight(), matrix, true);
}
return null;
}
Basically rotating bitmap is a task of rotating 2D array without using additional memory. And this is the correct implementation with RenderScript: Android: rotate image without loading it to memory .
But this is not necessary if all you want is just to display rotated Bitmap. You can simply extend ImageView and rotate the Canvas while drawing on it:
canvas.save();
canvas.rotate(angle, X + (imageW / 2), Y + (imageH / 2));
canvas.drawBitmap(imageBmp, X, Y, null);
canvas.restore();
What about ScriptIntrinsic, since it's just a built-in RenderScript kernels for common operations you cannot do nothing above the already implemented functions: ScriptIntrinsic3DLUT, ScriptIntrinsicBLAS, ScriptIntrinsicBlend, ScriptIntrinsicBlur, ScriptIntrinsicColorMatrix, ScriptIntrinsicConvolve3x3, ScriptIntrinsicConvolve5x5, ScriptIntrinsicHistogram, ScriptIntrinsicLUT, ScriptIntrinsicResize, ScriptIntrinsicYuvToRGB. They do not include functionality to rotate bitmap at the moment so you should create your own ScriptC script.
Try this code..
private Bitmap RotateImage(Bitmap _bitmap, int angle) {
Matrix matrix = new Matrix();
matrix.postRotate(angle);
_bitmap = Bitmap.createBitmap(_bitmap, 0, 0, _bitmap.getWidth(), _bitmap.getHeight(), matrix, true);
return _bitmap;
}
Use this code when select image from gallery.
like this..
File _file = new File(file_name);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 1;
Bitmap bitmap = BitmapFactory.decodeFile(file_name, options);
try {
ExifInterface exif = new ExifInterface(file_name);
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
if (orientation == ExifInterface.ORIENTATION_ROTATE_90) {
bitmap = RotateImage(bitmap, 90);
} else if (orientation ==ExifInterface.ORIENTATION_ROTATE_270) {
bitmap = RotateImage(bitmap, 270);
}
} catch (Exception e) {
e.printStackTrace();
}
image_holder.setImageBitmap(bitmap);
I just finished my camera activity and it's wonderfully saving the data.
What I do after the picture is taken:
protected void savePictureData() {
try {
FileOutputStream fs = new FileOutputStream(this.photo);
fs.write(this.lastCamData);
fs.close(); //okay, wonderful! file is just written to the sdcard
//---------------------
//---------------------
//TODO in here: dont save just the file but ROTATE the image and then save it!
//---------------------
//---------------------
Intent data = new Intent(); //just a simple intent returning some data...
data.putExtra("picture_name", this.fname);
data.putExtra("byte_data", this.lastCamData);
this.setResult(SAVED_TOOK_PICTURE, data);
this.finish();
} catch (IOException e) {
e.printStackTrace();
this.IOError();
}
}
What I want to is already as comment given in the code above. I dont want the image just to be saved to file but to be rotated and then saved! Thanks!
//EDIT: What I am currently up to (Works but still runs into memory issues with large images)
byte[] pictureBytes;
Bitmap thePicture = BitmapFactory.decodeByteArray(this.lastCamData, 0, this.lastCamData.length);
Matrix m = new Matrix();
m.postRotate(90);
thePicture = Bitmap.createBitmap(thePicture, 0, 0, thePicture.getWidth(), thePicture.getHeight(), m, true);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
thePicture.compress(CompressFormat.JPEG, 100, bos);
pictureBytes = bos.toByteArray();
FileOutputStream fs = new FileOutputStream(this.photo);
fs.write(pictureBytes);
fs.close();
Intent data = new Intent();
data.putExtra("picture_name", this.fname);
data.putExtra("byte_data", pictureBytes);
this.setResult(SAVED_TOOK_PICTURE, data);
this.finish();
Read the path from sd card and paste the following code...It'll Replace the existing photo after rotating it..
Note: Exif doesn't work on most of the devices, it returns incorrect data so it's good to hard code the rotation before saving to any degree you want to, Just change the angle value in postRotate.
String photopath = tempphoto.getPath().toString();
Bitmap bmp = BitmapFactory.decodeFile(photopath);
Matrix matrix = new Matrix();
matrix.postRotate(90);
bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix, true);
FileOutputStream fOut;
try {
fOut = new FileOutputStream(tempphoto);
bmp.compress(Bitmap.CompressFormat.JPEG, 85, fOut);
fOut.flush();
fOut.close();
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Before you create your FileOutputStream you can create a new Bitmap from the original that has been transformed using a Matrix. To do that you would use this method:
createBitmap(Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter)
Where the m defines a matrix that will transpose your original bitmap.
For an example on how to do this look at this question:
Android: How to rotate a bitmap on a center point
bitmap = RotateBitmap(bitmap, 90);
public static Bitmap RotateBitmap(Bitmap source, float angle)
{
Matrix matrix = new Matrix();
matrix.postRotate(angle);
return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix, true);
}
I have an app that displays quite a few images for the user, and we've been seeing a lot of error reports with OutOfMemoryError exception.
What we currently do is this:
// Check if image is a landscape image
if (bmp.getWidth() > bmp.getHeight()) {
// Rotate it to show as a landscape
Matrix m = image.getImageMatrix();
m.postRotate(90);
bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true);
}
image.setImageBitmap(bmp);
The obvious problem with this is that we have to recreate the bitmap from the image on memory and rotate the matrix, this is quite expensive for the memory.
My question is simple:
Is there a better way to rotate images without causing OutOfMemoryError?
2 methods of rotating a large image:
using JNI , like on this post.
using a file : it's a very slow way (depending on the input and the device , but still very slow) , which puts the decoded rotated image into the disk first , instead of putting it into the memory .
code of using a file is below:
private void rotateCw90Degrees()
{
Bitmap bitmap=BitmapFactory.decodeResource(getResources(),INPUT_IMAGE_RES_ID);
// 12 => 7531
// 34 => 8642
// 56 =>
// 78 =>
final int height=bitmap.getHeight();
final int width=bitmap.getWidth();
try
{
final DataOutputStream outputStream=new DataOutputStream(new BufferedOutputStream(openFileOutput(ROTATED_IMAGE_FILENAME,Context.MODE_PRIVATE)));
for(int x=0;x<width;++x)
for(int y=height-1;y>=0;--y)
{
final int pixel=bitmap.getPixel(x,y);
outputStream.writeInt(pixel);
}
outputStream.flush();
outputStream.close();
bitmap.recycle();
final int newWidth=height;
final int newHeight=width;
bitmap=Bitmap.createBitmap(newWidth,newHeight,bitmap.getConfig());
final DataInputStream inputStream=new DataInputStream(new BufferedInputStream(openFileInput(ROTATED_IMAGE_FILENAME)));
for(int y=0;y<newHeight;++y)
for(int x=0;x<newWidth;++x)
{
final int pixel=inputStream.readInt();
bitmap.setPixel(x,y,pixel);
}
inputStream.close();
new File(getFilesDir(),ROTATED_IMAGE_FILENAME).delete();
saveBitmapToFile(bitmap); //for checking the output
}
catch(final IOException e)
{
e.printStackTrace();
}
}
you can try:
image.setImageBitmap(null);
// Check if image is a landscape image
if (bmp.getWidth() > bmp.getHeight()) {
// Rotate it to show as a landscape
Matrix m = image.getImageMatrix();
m.postRotate(90);
bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true);
}
BitmapDrawable bd = new BitmapDrawable(mContext.getResources(), bmp);
bmp.recycle();
bmp = null;
setImageDrawable(bd);
bd = null;
When working with lots of Bitmaps be sure to call recycle() on them as soon as they are not needed. This call will instantly free memory associated with a particular bitmap.
In your case if you do not need the original bitmap after rotation, then recycle it. Something along the lines of:
Bitmap result = bmp;
// Check if image is a landscape image
if (bmp.getWidth() > bmp.getHeight()) {
// Rotate it to show as a landscape
Matrix m = image.getImageMatrix();
m.postRotate(90);
result = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true);
// rotating done, original not needed => recycle()
bmp.recycle();
}
image.setImageBitmap(result);