Bitmap resizing and rotating: linear noise - android

I am resizing image and rotating it using Matrix:
Matrix mtx = new Matrix();
if(orientation>0) {
mtx.postRotate(orientation);
Log.d(TAG,"image rotated: "+orientation);
}
if(scale<1) {
mtx.postScale(scale,scale);
Log.d(TAG,"image scaled: "+scale);
}
bmp = Bitmap.createBitmap(bm_orig, 0, 0, width, height, mtx, true);
bm_orig.recycle();
bmp.compress(Bitmap.CompressFormat.JPEG,95,output);
bmp.recycle();
When bmp_orig is taken, used 3.2 Mpx Camera, image resized and rotated looks normal.
But when source is 4 Mpx or bigger, result after resizing has barely-noticeable linear noise
I dont know, why does this noise appear, and how to remove it.
Any idea?
May be another way to resize and rotate?

Found that this problem is related with source and resulting image size.
Solved it, when check image size before loading it, and then load halfsized image, if source image size is more than 2 times bigger than resulting size is needed.
BitmapFactory.Options options_to_get_size = new BitmapFactory.Options();
options_to_get_size.inJustDecodeBounds = true;
BitmapFactory.decodeStream(input, null, options_to_get_size);
int load_scale = 1; // load 100% sized image
int width_tmp=options_to_get_size.outWidth
int height_tmp=options_to_get_size.outHeight;
while(width_tmp/2>maxW && height_tmp/2>maxH){
width_tmp/=2;//load half sized image
height_tmp/=2;
load_scale*=2;
}
Log.d(TAG,"load inSampleSize: "+ load_scale);
//Now load image with precalculated scale. scale must be power of 2 (1,2,4,8,16...)
BitmapFactory.Options option_to_load = new BitmapFactory.Options();
option_to_load.inSampleSize = load_scale;
((FileInputStream)input).getChannel().position(0); # reset input stream to read again
Bitmap bm_orig = BitmapFactory.decodeStream(input,null,option_to_load);
input.close();
//now you can resize and rotate image using matrix as stated in question

Related

Android: BitmapFactory changes dimensions of image when decoding from ByteArray

I am converting an android Image captured in my application to a bitmap. I am doing this by getting the image buffer from the pixel plane of the image and then using BitMapFactory to decode it into a Bitmap. However, doing so seems to change the resolution of the Image from 1920 x 1440 to 1800 x 1600, cropping out the top and bottom of the image in the process. The code for the method is shown here.
`protected void getImageFromBuffer(ImageReader reader){
Image image = null;
image = reader.acquireLatestImage();
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
System.out.println("Getting Image Ready");
synchronized (this){
image_to_upload = new byte[buffer.capacity()];
buffer.get(image_to_upload);
Bitmap storedBitmap = BitmapFactory.decodeByteArray(image_to_upload, 0, image_to_upload.length, null);
Matrix mat = new Matrix();
mat.postRotate(jpegOrientation); // angle is the desired angle you wish to rotate
storedBitmap = Bitmap.createBitmap(storedBitmap, 0, 0, storedBitmap.getWidth(), storedBitmap.getHeight(), mat, true);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
storedBitmap.compress(Bitmap.CompressFormat.JPEG,70, byteArrayOutputStream);
image_to_upload = byteArrayOutputStream.toByteArray();
image_ready = true;
System.out.println("Image Ready");
}
}`
Debugging shows that the height and width of the Image are correct before the buffer is converted to a bitmap, but the bitmap dimensions are wrong immediately after decodeByteArray. Can anyone suggest why this may be? I have checked the dimensions before applying the matrix transformation.
EDIT: To add further details, I have tried using BitmapFactory.Options() to disable scaling or to set the target density and neither have any impact on the resulting Bitmap, it is always size 1800 x 1600.
You can change some options that affects the result resolution of your bitmap by using the Options param to the decodeByteArray:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inDensity = DisplayMetrics.DENSITY_XXHIGH;//for example
BitmapFactory.decodeByteArray(image_to_upload, 0, image_to_upload.length, options);

Prevent bitmap too large to be uploaded into a texture android

I need to display original image in full screen in gallery form. For thumb it will be work perfectly and when I try to display that image in full screen with original source it will not be able to display. In most cases if the image resolution is greater then 2000 then it will display error bitmap too large to be uploaded into a texture android.
I want to prevent this, I have search google but not getting any answer regarding this.
I came across the same problem and came up with a one liner solution for this problem here:
Picasso.with(context).load(new File(path/to/File)).fit().centerCrop().into(imageView);
i just created a if else function to check if the image is bigger than 1M pixels here's the sample code:
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
if (requestCode == SELECT_PICTURE) {
Uri selectedImageUri = data.getData();
selectedImagePath = getPath(selectedImageUri);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4;
Bitmap bitmap = BitmapFactory.decodeFile(selectedImagePath);
int height = bitmap.getHeight(), width = bitmap.getWidth();
if (height > 1280 && width > 960){
Bitmap imgbitmap = BitmapFactory.decodeFile(selectedImagePath, options);
imageView.setImageBitmap(imgbitmap);
System.out.println("Need to resize");
}else {
imageView.setImageBitmap(bitmap);
System.out.println("WORKS");
}
Google provided a training how to do that. Download the sample from Displaying Bitmaps Efficiently
Take a look to ImageResizer class.
ImageResizer.decodeSampledBitmapFrom* use this method to get downscaled image.
This is the code I used to rectify my problem of fitting an image of size 3120x4196 resolution in an image view of 4096x4096 resolution. Here ImageViewId is the id of the image view created in the main layout and ImageFileLocation is the path of the image which is to be resized.
ImageView imageView=(ImageView)findViewById(R.id.ImageViewId);
Bitmap d=BitmapFactory.decodeFile(ImageFileLcation);
int newHeight = (int) ( d.getHeight() * (512.0 / d.getWidth()) );
Bitmap putImage = Bitmap.createScaledBitmap(d, 512, newHeight, true);
imageView.setImageBitmap(putImage);
You don't need to load the whole image, cause it's too large and probably your phone won't able to show the full bitmap pixels.
You need to scale it first according to your device screen size.
This is the best method that I found and it works pretty good:
Android: Resize a large bitmap file to scaled output file
I found a way to do this without using any external libraries:
if (bitmap.getHeight() > GL10.GL_MAX_TEXTURE_SIZE) {
// this is the case when the bitmap fails to load
float aspect_ratio = ((float)bitmap.getHeight())/((float)bitmap.getWidth());
Bitmap scaledBitmap = Bitmap.createBitmap(bitmap, 0, 0,
(int) ((GL10.GL_MAX_TEXTURE_SIZE*0.9)*aspect_ratio),
(int) (GL10.GL_MAX_TEXTURE_SIZE*0.9));
imageView.setImageBitmap(scaledBitmap);
}
else{
// for bitmaps with dimensions that lie within the limits, load the image normally
if (Build.VERSION.SDK_INT >= 16) {
BitmapDrawable ob = new BitmapDrawable(getResources(), bitmap);
imageView.setBackground(ob);
} else {
imageView.setImageBitmap(bitmap);
}
}
Basically, the maximum image dimensions are a system-imposed limit. The above approach will correctly resize bitmaps that exceed this limit. However, only a portion of the entire image will be loaded. To change the region displayed, you can alter the x and y parameters of the createBitmap() method.
This approach handles bitmaps of any size, including pictures taken with professional cameras.
References:
Android Universal Image Loader.

Android Rotate image/photo

I have a problem with the rotation of images.
I try:
Matrix matrix = new Matrix();
matrix.postRotate(90);
BitmapFactory.Options options=new BitmapFactory.Options();
options.inSampleSize = 4;
Bitmap pic = BitmapFactory.decodeFile(filePath, options);
Bitmap rotatedPhoto = Bitmap.createBitmap(pic, 0, 0, pic.getWidth(), pic.getHeight(), matrix, true);
photo.setImageBitmap(rotatedPhoto);
try {
stream = new FileOutputStream(filePath);
rotatedPhoto.compress(CompressFormat.JPEG, 100 ,stream);
}
catch (FileNotFoundException e) {
e.printStackTrace();
}
Picture rotating, but the quality is very much lost.
How do I solve this problem? How do I rotate the image without losing quality?
Thank you!
Update:
And how to rotate image without losing resolution?
I think your problem is arising because you are setting inSampleSize to 4. This means the returned image will be a factor of 4 smaller than the original image.
http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html#inSampleSize
Try setting options.inSampleSize to 1 - does this help?
Be careful when dealing with images though - you have very little memory to play with in an Android app. Loading just a couple of large images into memory at once can often cause your app to crash.

creating scaled bitmap leads to invalid image

I've created a function that scales a bitmap directly to a specific surface area. The function first gets the width and height of the bitmap and then finds the sample size closest to the required size. Lastly the image is scaled to the exact size. This is the only way I could find to decode a scaled bitmap. The problem is that the bitmap returned from BitmapFactory.createScaledBitmap(src,width,height,filter) always comes back with a width and height of -1. I've already implemented other functions that use the createScaledBitmap() method with out this error and I can not find any reason why creating a scaled bitmap would produce invalid output. I've also found that if I create a copy of the image bitmap that is mutable causes the same error. Thanks
public static Bitmap load_scaled_image( String file_name, int area) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file_name, options);
double ratio = (float)options.outWidth / (float)options.outHeight;
int width, height;
if( options.outWidth > options.outHeight ) {
width = (int)Math.sqrt(area/ratio);
height = (int)(width*ratio);
}
else {
height = (int)Math.sqrt(area/ratio);
width = (int)(height*ratio);
}
BitmapFactory.Options new_options = new BitmapFactory.Options();
new_options.inSampleSize = Math.max( (options.outWidth/width), (options.outHeight/height) );
Bitmap image = BitmapFactory.decodeFile(file_name, new_options);
return Bitmap.createScaledBitmap(image, width, height, true);
}
I added this function to scale large camera images to a specific number of mega pixels. So a typical area passed in would be 1000000 for 1 megapixel. The camera image after being decoded yields a outWidth of 1952 and a outHieght of 3264. I then calculate the ratio this way I can keep the same height to width ratio with the scaled image, in this case the ratio is 0.598... Using the ratio and the new surface area I can find the new width which is 773 and a height of 1293. 773x1293=999489 which is just about 1 megapixel. Next I calculate the sample size for which to decode the new image, in this case the sample size is 4 and the image is decoded to 976x1632. So I'm passing in a width of 773 a height of 1293.
I was having a similar problem (getting -1 for height and width of the scaled bitmap).
Following this stackOverflow thread:
Android how to create runtime thumbnail
I've tried to use the same bitmap twice while calling the function:
imageBitmap = Bitmap.createScaledBitmap(imageBitmap, THUMBNAIL_SIZE,
THUMBNAIL_SIZE, false);
For some reason, this solved my problem, perhaps it would solve yours too.

Save image overlay with camera captured image underneith

My application has a "photobooth" feature which will allow the user to take a picture with the camera and at the same time show an overlay image on top of the camera view. After the picture is taken, i need to save what the user saw while taking the picture to the filesystem.
I have experienced 1 big problem while developing a solution to this: capturing an image with the compatible dimensions in which i can attach an overlay image to resulting in what the user saw while taking the picture.
It seems i cannot capture an image from the camera with defined dimensions(i have to basically pick from a list of them). Some phones only can produce certain dimensions.
Since i cannot choose the size of the captured image, it seems as though i will be required to include many different sizes of the overlay image, and attach the best match to the captured image. I can't just slap any old overlay on top of the camera image and make it look right.
Questions:
Am i over-complicating this "camera image + overlay image creation" process?
What suggestions do you have in completing this task without the need of including several different sizes overlay images?
Edit:
Here is my solution(brief). Please realize this is not a perfect and maybe not most efficient way to do this, but it works. Some things may be unnecessary/redundant but whatever!
Notes:
this doesn't work too great on tablet devices.
the overlay image needs to be rotated to be in landscape mode(even though you will be taking the image holding the phone in portrait)
overlay size is 480x320
you need to force the activity to landscape mode while taking the picture(now the overlay looks like its portrait!)
i add the overlay image view using addContentView(overlayImageView, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
...
final Camera.PictureCallback jpegCallback = new Camera.PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
BitmapFactory.Options options = new BitmapFactory.Options();
Bitmap mutableBitmap = null;
try {
//for a PORTRAIT overlay and taking the image holding the phone in PORTRAIT mode
mutableBitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options).copy(Bitmap.Config.RGB_565, true);
Matrix matrix = new Matrix();
int width = mutableBitmap.getWidth();
int height = mutableBitmap.getHeight();
int newWidth = overlayImage.getDrawable().getBounds().width();
int newHeight = overlayImage.getDrawable().getBounds().height();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
matrix.postScale(scaleWidth, scaleHeight);
matrix.postRotate(90);
Bitmap resizedBitmap = Bitmap.createBitmap(mutableBitmap, 0, 0, mutableBitmap.getWidth(), mutableBitmap.getHeight(), matrix, true);
finalBitmap = resizedBitmap.copy(Bitmap.Config.RGB_565, true);
Canvas canvas = new Canvas(finalBitmap);
Bitmap overlayBitmap = BitmapFactory.decodeResource(getResources(), overlay);
matrix = new Matrix();
matrix.postRotate(90);
Bitmap resizedOverlay = Bitmap.createBitmap(overlayBitmap, 0, 0, overlayBitmap.getWidth(), overlayBitmap.getHeight(), matrix, true);
canvas.drawBitmap(resizedOverlay, 0, 0, new Paint());
canvas.scale(50, 0);
canvas.save();
//finalBitmap is the image with the overlay on it
}
catch(OutOfMemoryError e) {
//fail
}
}
}
I think this is a question of how you manipulate your overlays. You can crop it according to the captured image size and resize it to fit, preserving its ratio. You can place the overlay, by comparing its ratio to the backround ratio, to its optimal position.
I would keep overlays big enough, with a wide border (bleed), to easily size them to an image using filters to draw it with good qaulity. I guess overlays are something which you would design and have transparent parts, like an image of a clown without a face so the user can snap somebody elses face into it?

Categories

Resources