After merging images the quality decreased, Android - android

I have two images and I want to set one upon another.
However after merging I get low quality file.
This is my original topImage (96x96) pixels:
This is my bottomIamge (74x74) pixels:
You can see that quality is pretty good.
When I run under mentioned code I get merged Image (74x74):
Now you can see that topImage lost his quality.
Here is relevant code:
// load bottom image from assets:
InputStream is;
Bitmap bottomImage;
try {
is = context.getAssets().open("images/avatar1.png");
} catch (IOException e1) {
int resID = context.getResources().getIdentifier("unknown_item", "drawable", context.getPackageName());
is = context.getResources().openRawResource(resID);
}
BitmapFactory.Options options = new BitmapFactory.Options();
options.inMutable = true;
bottomImage = BitmapFactory.decodeStream(is,null,options);
try {
is.close();
is = null;
} catch (IOException e) {
}
Bitmap topImage = null;
String base64Img = null;
// get byte array from String (base64).
byte[] backToBytes = Base64.decode(base64Img, Base64.DEFAULT);
// here I verified that image I got from byte array still has good quality
//writeToStorage(backToBytes, "test.png");
// create Bitmap
topImage = BitmapFactory.decodeByteArray(backToBytes, 0, backToBytes.length, null);
// scale the image
topImage = Bitmap.createScaledBitmap(topImage, 74, 74, false); // set fixed size 74x74 image
topImage = Bitmap.createBitmap(topImage, topImage.getWidth()/4, 0, topImage.getWidth()/2, topImage.getHeight());
// shift it:
Canvas comboImage = new Canvas(bottomImage);
// Then draw the second on top of that
comboImage.drawBitmap(topImage, 0f, 0f, null);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bottomImage.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
String base64String = Base64.encodeToString(byteArray, Base64.DEFAULT);
If I'll draw base64String I get merged Image
Do I miss something?
Does Bitmap.createScaledBitmap(topImage, 37, 74, false); scales to 37x74 pixels?
Thanks,

For now I use this way (based on THIS answer):
// this method should replace Bitmap.createScaledBitmap
private Bitmap scaleBitmap(Bitmap bitmap, int newWidth, int newHeight){
Bitmap scaledBitmap = Bitmap.createBitmap(newWidth, newHeight, Config.ARGB_8888);
float ratioX = newWidth / (float) bitmap.getWidth();
float ratioY = newHeight / (float) bitmap.getHeight();
float middleX = newWidth / 2.0f;
float middleY = newHeight / 2.0f;
Matrix scaleMatrix = new Matrix();
scaleMatrix.setScale(ratioX, ratioY, middleX, middleY);
Canvas canvas = new Canvas(scaledBitmap);
canvas.setMatrix(scaleMatrix);
canvas.drawBitmap(bitmap, middleX - bitmap.getWidth() / 2, middleY - bitmap.getHeight() / 2, new Paint(Paint.FILTER_BITMAP_FLAG));
return scaledBitmap;
}
And now I can replace:
topImage = Bitmap.createScaledBitmap(topImage, 74, 74, false);
with:
topImage = scaleBitmap(topImage, 74, 74);
was:
now:
Its not the same quality but seems better

You cannot keep the quality when scaling by not hall number(2,3,4,5). In your case you are scaling 96 pixels to 74.
You can either change the scale to 48 pixels(x2) or keep it 96 pixels.
Also watch this video that explain what is anti-aliacing, and how it can help you.

Related

Scaled Bitmap print on PDF without loosing Image Quality in Android Studio

Requirements :- I need to print images on a PDF document (A4 size sheets).
Source :- I got the images through the Camera and from the Gallery.
Crop :- I just used UCROP for cropping and editing the image if it necessary as an option.
Problem :- I can do everything mentioned above without any error and result also very good. But I need those print on a A4 size sheet as a PDF Document. To do that, I had to scaled down those Bitmaps to match the required sized. But the thing is, when I do that the Image Quality of the Scaled Bitmap on PDF is very low and can't read the details correctly.
Screenshot of a Sample PDF
Please help me to get success on this issue.
Your kind reply are mostly welcome and highly appreciated.
//boolean img1_SetImage - used to check Img1 is available or not
//img1_Uri - Uri of Img1
if (img1_SetImage) {
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inScaled = false;
Bitmap bmp = BitmapFactory.decodeFile(img1_Uri.getPath(), opt);
int[] xyImg = xy(bmp.getWidth(), bmp.getHeight(), 298, 175);
PdfDocument.PageInfo myPageInfo2 =
new PdfDocument.PageInfo.Builder(595, 842, 1).create();
PdfDocument.Page myPage2 = myPDFDoc.startPage(myPageInfo2);
Canvas myCanvas2 = myPage2.getCanvas();
Bitmap scaledBmp = Bitmap.createScaledBitmap(bmp, xyImg[0], xyImg[1], false);
myCanvas2.drawBitmap(scaledBmp, xyImg[2], xyImg[3], new Paint(Paint.FILTER_BITMAP_FLAG));
bmp.recycle();
scaledBmp.recycle();
}
private int[] xy(float width, float height, float left, float top) {
int finalWidth, finalHeight, finalLeft, finalTop;
float wScale, hScale, scaleFactor;
wScale = (436 / width);
hScale = (270 / height);
if (wScale >= hScale) {
scaleFactor = hScale;
} else {
scaleFactor = wScale;
}
finalWidth = (int) (width * scaleFactor);
finalHeight = (int) (height * scaleFactor);
finalLeft = (int) (left - (finalWidth / 2));
finalTop = (int) (top - (finalHeight / 2));
int[] returnValues = {finalWidth, finalHeight, finalLeft, finalTop};
return returnValues;
}
Instead of scaling the bitmap before adding I scale the pdf canvas before adding the bitmap at original resolution, this produces good results for me as the pdf scales it down (the original bitmap inside the PDF is the higher quality image)
Below is your code adapted to use the method I use.
//boolean img1_SetImage - used to check Img1 is available or not
//img1_Uri - Uri of Img1
if (img1_SetImage) {
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inScaled = false;
Bitmap bmp = BitmapFactory.decodeFile(img1_Uri.getPath(), opt);
PdfDocument.PageInfo myPageInfo2 =
new PdfDocument.PageInfo.Builder(595, 842, 1).create();
PdfDocument.Page myPage2 = myPDFDoc.startPage(myPageInfo2);
Canvas myCanvas2 = myPage2.getCanvas();
// Work out scaleFactor to get all the image on the page
float wScale, hScale, scaleFactor;
wScale = (float) 595 / bmp.getWidth(); // If you don't cast Int/Int = Int so you loose any decimal places.
hScale = (float) 842 / bmp.getHeight(); // Alternative is to define the size as float e.g. 842.0f
if (wScale >= hScale) {
scaleFactor = hScale;
} else {
scaleFactor = wScale;
}
Paint paint = new Paint();
paint.setAntiAlias(true);
myCanvas2.scale(scaleFactor, scaleFactor);
myCanvas2.drawBitmap(bmp,0,0,paint);

How to reduce an Image file size

I use this function to reduce the size of image before uploading it,But using below method my file size is increasing
Before use below code my file size---> 157684
after using this code my file size ----->177435
Can some one help me please how can i reduce file size before upload to server
code:
public File saveBitmapToFile(File file){
try {
// BitmapFactory options to downsize the image
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
o.inSampleSize = 6;
// factor of downsizing the image
FileInputStream inputStream = new FileInputStream(file);
//Bitmap selectedBitmap = null;
BitmapFactory.decodeStream(inputStream, null, o);
inputStream.close();
// The new size we want to scale to
final int REQUIRED_SIZE=75;
// Find the correct scale value. It should be the power of 2.
int scale = 1;
while(o.outWidth / scale / 2 >= REQUIRED_SIZE &&
o.outHeight / scale / 2 >= REQUIRED_SIZE) {
scale *= 2;
}
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
inputStream = new FileInputStream(file);
Bitmap selectedBitmap = BitmapFactory.decodeStream(inputStream, null, o2);
inputStream.close();
// here i override the original image file
file.createNewFile();
FileOutputStream outputStream = new FileOutputStream(file);
selectedBitmap.compress(Bitmap.CompressFormat.JPEG, 100 , outputStream);
return file;
} catch (Exception e) {
return null;
}
}
This what I use to reduce my image size without compressing :
public static Bitmap getResizedBitmap(Bitmap bitmap, int newWidth, int newHeight) {
Bitmap scaledBitmap = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888);
float ratioX = newWidth / (float) bitmap.getWidth();
float ratioY = newHeight / (float) bitmap.getHeight();
float middleX = newWidth / 2.0f;
float middleY = newHeight / 2.0f;
Matrix scaleMatrix = new Matrix();
scaleMatrix.setScale(ratioX, ratioY, middleX, middleY);
Canvas canvas = new Canvas(scaledBitmap);
canvas.setMatrix(scaleMatrix);
canvas.drawBitmap(bitmap, middleX - bitmap.getWidth() / 2, middleY - bitmap.getHeight() / 2, new Paint(Paint.FILTER_BITMAP_FLAG));
return scaledBitmap;
}
You just need to enter correct new height and width to fit your needs
We want to make thumbnail of an image, so we need to first take the ByteArrayOutputStream and then pass it into Bitmap.compress() method.
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
youBitmapImage.compress(Bitmap.CompressFormat.JPEG, 90, bytes);
more about the function, from the docs
If your output file is larger:
it can mean that scale is wrong. And you save the file with 100% quality so it can grow
compression on the input file is extremely heavy and even though you scale it, using no compression on the output still generates a larger file
Change this line:
selectedBitmap.compress(Bitmap.CompressFormat.JPEG, 100 , outputStream);
to
int mCompressedSize = 50; // 0 is lowest and 100 original
selectedBitmap.compress(Bitmap.CompressFormat.PNG, mCompressedSize, outputStream);
Hope this will help.
Try
int compressionRatio = 2; //1 == originalImage, 2 = 50% compression, 4=25% compress
File file = new File (imageUrl);
try {
Bitmap bitmap = BitmapFactory.decodeFile (file.getPath ());
bitmap.compress (Bitmap.CompressFormat.JPEG, compressionRatio, new FileOutputStream (file));
}
catch (Throwable t) {
Log.e("ERROR", "Error compressing file." + t.toString ());
t.printStackTrace ();
}

bitmap exceeds 32 bits error with createScaledBitmap in android

When calling the following method:
Bitmap localBitmap = Bitmap.createScaledBitmap(paramBitmap, 360, (int)(360.0D / (paramBitmap.getWidth() / paramBitmap.getHeight())), false);
I get the exception trace as:
java.lang.IllegalArgumentException: bitmap size exceeds 32 bits
I printed the size of incoming bitmap using statements:
System.out.println("paramBitmap.getWidth() "+ paramBitmap.getWidth());
System.out.println("paramBitmap.getHeight() "+ paramBitmap.getHeight());
and it is 480x960
How to debug this problem and solve it.
i know its late but it might help somebody,I faced the same problem when i tried to draw a border for bitmap and rotate it to certain angle, it always crashed in xiaomi mi mobile, i solved it by scaling the bitmap as per the need,
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap bmp = BitmapFactory.decodeFile(path, options);
final int BORDER_WIDTH = 10;
final int BORDER_COLOR = Color.YELLOW;
Bitmap res = Bitmap.createBitmap(bmp.getWidth() + 2 * BORDER_WIDTH,bmp.getHeight() + 2 * BORDER_WIDTH,bmp.getConfig());
Canvas c = new Canvas(res);
Paint p = new Paint();
p.setColor(BORDER_COLOR);
c.drawRect(0, 0, res.getWidth(), res.getHeight(), p);
p = new Paint(Paint.FILTER_BITMAP_FLAG);
c.drawBitmap(bmp, BORDER_WIDTH, BORDER_WIDTH, p);
float viewWidth = (float) res.getWidth();
float viewHeight = `enter code here`(float) res.getHeight();
float ratiowidth = vi`enter code here`ewWidth / (float) res.getWidth();
float ratioheight = viewHeight / (float) res.getHeight();
Matrix mat = new Matrix();
mat.postScale(ratiowidth, ratioheight);
mat.postRotate(45);
Bitmap bMapRotate = Bitmap.createBitmap(res, 0,0,res.getWidth(),res.getHeight(), mat, true);

Pick image from sd card, resize the image and save it back to sd card

I am working on an application, in which I need to pick an image from sd card and show it in image view. Now I want the user to decrease/increase its width by clicking a button and then save it back to the sd card.
I have done the image picking and showing it on ui. But unable to find how to resize it.Can anyone please suggest me how to achieve it.
Just yesterday i have done this
File dir=Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
Bitmap b= BitmapFactory.decodeFile(PATH_ORIGINAL_IMAGE);
Bitmap out = Bitmap.createScaledBitmap(b, 320, 480, false);
File file = new File(dir, "resize.png");
FileOutputStream fOut;
try {
fOut = new FileOutputStream(file);
out.compress(Bitmap.CompressFormat.PNG, 100, fOut);
fOut.flush();
fOut.close();
b.recycle();
out.recycle();
} catch (Exception e) {}
Also don't forget to recycle your bitmaps: It will save memory.
You can also get path of new created file String: newPath=file.getAbsolutePath();
Solution without OutOfMemoryException in Kotlin
fun resizeImage(file: File, scaleTo: Int = 1024) {
val bmOptions = BitmapFactory.Options()
bmOptions.inJustDecodeBounds = true
BitmapFactory.decodeFile(file.absolutePath, bmOptions)
val photoW = bmOptions.outWidth
val photoH = bmOptions.outHeight
// Determine how much to scale down the image
val scaleFactor = Math.min(photoW / scaleTo, photoH / scaleTo)
bmOptions.inJustDecodeBounds = false
bmOptions.inSampleSize = scaleFactor
val resized = BitmapFactory.decodeFile(file.absolutePath, bmOptions) ?: return
file.outputStream().use {
resized.compress(Bitmap.CompressFormat.JPEG, 75, it)
resized.recycle()
}
}
Try using this method:
public static Bitmap scaleBitmap(Bitmap bitmapToScale, float newWidth, float newHeight) {
if(bitmapToScale == null)
return null;
//get the original width and height
int width = bitmapToScale.getWidth();
int height = bitmapToScale.getHeight();
// create a matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(newWidth / width, newHeight / height);
// recreate the new Bitmap and set it back
return Bitmap.createBitmap(bitmapToScale, 0, 0, bitmapToScale.getWidth(), bitmapToScale.getHeight(), matrix, true);
}
You can use Bitmap.createScaledBitmap (Bitmap src, int dstWidth, int dstHeight, boolean filter)
I found this useful library for this achievement: https://github.com/hkk595/Resizer
Here my static method:
public static void resizeImageFile(File originalImageFile, File resizedImageFile, int maxSize) {
Bitmap bitmap = BitmapFactory.decodeFile(originalImageFile.getAbsolutePath());
Bitmap resizedBitmap;
if (bitmap.getWidth() > bitmap.getHeight()) {
resizedBitmap = Bitmap.createScaledBitmap(bitmap, maxSize, maxSize * bitmap.getHeight() / bitmap.getWidth(), false);
} else {
resizedBitmap = Bitmap.createScaledBitmap(bitmap, maxSize * bitmap.getWidth() / bitmap.getHeight(), maxSize, false);
}
FileOutputStream fileOutputStream;
try {
fileOutputStream = new FileOutputStream(resizedImageFile);
resizedBitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream);
fileOutputStream.flush();
fileOutputStream.close();
bitmap.recycle();
resizedBitmap.recycle();
} catch (Exception e) {
e.printStackTrace();
}
}
You should ideally use multitouch instead of using a button to increase/decrease width. Here's an amazing library. Once the user decides to save the image, the image translation matrix must be stored persistently (in your sqlite database). Next time the user opens the image, you need to recall the matrix and apply it to your image.
I've actually done this before.

Android Crop Center of Bitmap

I have bitmaps which are squares or rectangles. I take the shortest side and do something like this:
int value = 0;
if (bitmap.getHeight() <= bitmap.getWidth()) {
value = bitmap.getHeight();
} else {
value = bitmap.getWidth();
}
Bitmap finalBitmap = null;
finalBitmap = Bitmap.createBitmap(bitmap, 0, 0, value, value);
Then I scale it to a 144 x 144 Bitmap using this:
Bitmap lastBitmap = null;
lastBitmap = Bitmap.createScaledBitmap(finalBitmap, 144, 144, true);
Problem is that it crops the top left corner of the original bitmap, Anyone has the code to crop the center of the bitmap?
This can be achieved with: Bitmap.createBitmap(source, x, y, width, height)
if (srcBmp.getWidth() >= srcBmp.getHeight()){
dstBmp = Bitmap.createBitmap(
srcBmp,
srcBmp.getWidth()/2 - srcBmp.getHeight()/2,
0,
srcBmp.getHeight(),
srcBmp.getHeight()
);
}else{
dstBmp = Bitmap.createBitmap(
srcBmp,
0,
srcBmp.getHeight()/2 - srcBmp.getWidth()/2,
srcBmp.getWidth(),
srcBmp.getWidth()
);
}
While most of the above answers provide a way to do this, there is already a built-in way to accomplish this and it's 1 line of code (ThumbnailUtils.extractThumbnail())
int dimension = getSquareCropDimensionForBitmap(bitmap);
bitmap = ThumbnailUtils.extractThumbnail(bitmap, dimension, dimension);
...
//I added this method because people keep asking how
//to calculate the dimensions of the bitmap...see comments below
public int getSquareCropDimensionForBitmap(Bitmap bitmap)
{
//use the smallest dimension of the image to crop to
return Math.min(bitmap.getWidth(), bitmap.getHeight());
}
If you want the bitmap object to be recycled, you can pass options that make it so:
bitmap = ThumbnailUtils.extractThumbnail(bitmap, dimension, dimension, ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
From: ThumbnailUtils Documentation
public static Bitmap extractThumbnail (Bitmap source, int width, int
height)
Added in API level 8 Creates a centered bitmap of the desired size.
Parameters source original bitmap source width targeted width
height targeted height
I was getting out of memory errors sometimes when using the accepted answer, and using ThumbnailUtils resolved those issues for me. Plus, this is much cleaner and more reusable.
Have you considered doing this from the layout.xml ? You could set for your ImageView the ScaleType to android:scaleType="centerCrop" and set the dimensions of the image in the ImageView inside the layout.xml.
You can used following code that can solve your problem.
Matrix matrix = new Matrix();
matrix.postScale(0.5f, 0.5f);
Bitmap croppedBitmap = Bitmap.createBitmap(bitmapOriginal, 100, 100,100, 100, matrix, true);
Above method do postScalling of image before cropping, so you can get best result with cropped image without getting OOM error.
For more detail you can refer this blog
Here a more complete snippet that crops out the center of an [bitmap] of arbitrary dimensions and scales the result to your desired [IMAGE_SIZE]. So you will always get a [croppedBitmap] scaled square of the image center with a fixed size. ideal for thumbnailing and such.
Its a more complete combination of the other solutions.
final int IMAGE_SIZE = 255;
boolean landscape = bitmap.getWidth() > bitmap.getHeight();
float scale_factor;
if (landscape) scale_factor = (float)IMAGE_SIZE / bitmap.getHeight();
else scale_factor = (float)IMAGE_SIZE / bitmap.getWidth();
Matrix matrix = new Matrix();
matrix.postScale(scale_factor, scale_factor);
Bitmap croppedBitmap;
if (landscape){
int start = (tempBitmap.getWidth() - tempBitmap.getHeight()) / 2;
croppedBitmap = Bitmap.createBitmap(tempBitmap, start, 0, tempBitmap.getHeight(), tempBitmap.getHeight(), matrix, true);
} else {
int start = (tempBitmap.getHeight() - tempBitmap.getWidth()) / 2;
croppedBitmap = Bitmap.createBitmap(tempBitmap, 0, start, tempBitmap.getWidth(), tempBitmap.getWidth(), matrix, true);
}
Probably the easiest solution so far:
public static Bitmap cropCenter(Bitmap bmp) {
int dimension = Math.min(bmp.getWidth(), bmp.getHeight());
return ThumbnailUtils.extractThumbnail(bmp, dimension, dimension);
}
imports:
import android.media.ThumbnailUtils;
import java.lang.Math;
import android.graphics.Bitmap;
To correct #willsteel solution:
if (landscape){
int start = (tempBitmap.getWidth() - tempBitmap.getHeight()) / 2;
croppedBitmap = Bitmap.createBitmap(tempBitmap, start, 0, tempBitmap.getHeight(), tempBitmap.getHeight(), matrix, true);
} else {
int start = (tempBitmap.getHeight() - tempBitmap.getWidth()) / 2;
croppedBitmap = Bitmap.createBitmap(tempBitmap, 0, start, tempBitmap.getWidth(), tempBitmap.getWidth(), matrix, true);
}
public Bitmap getResizedBitmap(Bitmap bm) {
int width = bm.getWidth();
int height = bm.getHeight();
int narrowSize = Math.min(width, height);
int differ = (int)Math.abs((bm.getHeight() - bm.getWidth())/2.0f);
width = (width == narrowSize) ? 0 : differ;
height = (width == 0) ? differ : 0;
Bitmap resizedBitmap = Bitmap.createBitmap(bm, width, height, narrowSize, narrowSize);
bm.recycle();
return resizedBitmap;
}
public static Bitmap resizeAndCropCenter(Bitmap bitmap, int size, boolean recycle) {
int w = bitmap.getWidth();
int h = bitmap.getHeight();
if (w == size && h == size) return bitmap;
// scale the image so that the shorter side equals to the target;
// the longer side will be center-cropped.
float scale = (float) size / Math.min(w, h);
Bitmap target = Bitmap.createBitmap(size, size, getConfig(bitmap));
int width = Math.round(scale * bitmap.getWidth());
int height = Math.round(scale * bitmap.getHeight());
Canvas canvas = new Canvas(target);
canvas.translate((size - width) / 2f, (size - height) / 2f);
canvas.scale(scale, scale);
Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG);
canvas.drawBitmap(bitmap, 0, 0, paint);
if (recycle) bitmap.recycle();
return target;
}
private static Bitmap.Config getConfig(Bitmap bitmap) {
Bitmap.Config config = bitmap.getConfig();
if (config == null) {
config = Bitmap.Config.ARGB_8888;
}
return config;
}
val sourceWidth = source.width
val sourceHeight = source.height
val xScale = newWidth.toFloat() / sourceWidth
val yScale = newHeight.toFloat() / sourceHeight
val scale = xScale.coerceAtLeast(yScale)
val scaledWidth = scale * sourceWidth
val scaledHeight = scale * sourceHeight
val left = (newWidth - scaledWidth) / 2
val top = (newHeight - scaledHeight) / 2
val targetRect = RectF(
left, top, left + scaledWidth, top
+ scaledHeight
)
val dest = Bitmap.createBitmap(
newWidth, newHeight,
source.config
)
val mutableDest = dest.copy(source.config, true)
val canvas = Canvas(mutableDest)
canvas.drawBitmap(source, null, targetRect, null)
binding.imgView.setImageBitmap(mutableDest)

Categories

Resources