Split big bitmap to list of bitmaps in android - android

I have a big bitmap - sometimes with height 2000 sometimes 4000 etc. This is possible to split this big bitmap dividing by 1500 and save into array?
For example if I have bitmap with height 2300 I want to have array with two bitmaps: one 1500 height and second 800.

You can use the createBitmap() to create bitmap chunks from the original Bitmap.
The below function takes in a bitmap and the desired chunk size (1500 in your case).
And split the bitmap vertically if the width is greater than height, and horizontally otherwise.
fun getBitmaps(bitmap: Bitmap, maxSize: Int): List<Bitmap> {
val width = bitmap.width
val height = bitmap.height
val nChunks = ceil(max(width, height) / maxSize.toDouble())
val bitmaps: MutableList<Bitmap> = ArrayList()
var start = 0
for (i in 1..nChunks.toInt()) {
bitmaps.add(
if (width >= height)
Bitmap.createBitmap(bitmap, start, 0, width / maxSize, height)
else
Bitmap.createBitmap(bitmap, 0, start, width, height / maxSize)
)
start += maxSize
}
return bitmaps
}
Usage:
getBitmaps(myBitmap, 1500)

Yes, you can use Bitmap.createBitmap(bmp, offsetX, offsetY, width, height);
to create a "slice" of the bitmap starting at a particular x and y offset, and having a particular width and height.
I'll leave the math up to you.

Related

Separate YUV buffers to Bitmap in Android

As a brief context, I am getting raw video data from Zoom SDK as separate Y, U, and V ByteBuffers and trying to convert them to bitmaps.
However, I noticed that this conversion is resulting in grayscale bitmap images with green and pink spots [as shown in the screenshot below]
the method I’m using for conversion is the following:
fun ZoomVideoSDKVideoRawData.toBitmap(): Bitmap? {
val width = streamWidth
val height = streamHeight
val yuvBytes = ByteArray(width * (height + height / 2))
val yPlane = getyBuffer()
val uPlane = getuBuffer()
val vPlane = getvBuffer()
// copy Y
yPlane.get(yuvBytes, 0, width * height)
// copy U
var offset = width * height
uPlane.get(yuvBytes, offset, width * height / 4)
// copy V
offset += width * height / 4
vPlane.get(yuvBytes, offset, width * height / 4)
// make YUV image
val yuvImage = YuvImage(yuvBytes, ImageFormat.NV21, width, height, null)
// convert image to bitmap
val out = ByteArrayOutputStream()
yuvImage.compressToJpeg(Rect(0, 0, width, height), 50, out)
val imageBytes = out.toByteArray()
val image = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
// return result
return image
}
any idea why the colors of the resulting bitmap are corrupted?
Thanks in advance!

x + width must be <= bitmap.width()

I'm trying to develop face detection using google-mlkit. After setup camera, analyzer, detector etc following docs from google-mlkit https://developers.google.com/ml-kit/vision/face-detection/android, I'm able to detect face. After detect face I'm trying to extract detected face image.
Get detected face boundingBox, extract full bitmap into detected face bitmap using boundingBox. Code below.
Bitmap bmp = Bitmap.createBitmap(mediaImage.getWidth(), mediaImage.getHeight(), Bitmap.Config.ARGB_8888);
YuvToRgbConverter converter = new YuvToRgbConverter(context);
converter.yuvToRgb(mediaImage, bmp);
Bitmap extractedBmp = extractFace(
bmp,
(int) (face.getBoundingBox().exactCenterX() / 2),
(int) (face.getBoundingBox().exactCenterY() / 2),
face.getBoundingBox().width(),
face.getBoundingBox().height()
);
private Bitmap extractFace(Bitmap bmp, int x, int y, int width, int height) {
return Bitmap.createBitmap(bmp, x, y, width, height);
}
Sometimes It's success and sometimes also failed. After debugging I found it's error because of x + width must be <= bitmap.width().
What I'm doing wrong?
Exception is correct.
It happens because face boundingBox can go outside of the image (bitmap).
Also it possible for x and y as well. Try this simple recalculation.
private fun extractFace(bmp: Bitmap, x: Int, y: Int, width: Int, height: Int): Bitmap? {
val originX = if (x + width > bmp.width) (bmp.width - width) else x
val originY = if (y + height > bmp.height) (bmp.height - height) else y
return Bitmap.createBitmap(bmp, originX, originY, width, height)
}
PS: Please check this calculation someone, it should work, thank you
Could you try util methods provided in the mlkit sample here to create a bimap from YUV image?

How to reduce image size into 1MB

I want my application to upload image with no size limit, but in the code, I want to resize the image into 1MB if the image size exceeds. I have tried many ways but I couldn't find any code for the requirement I have mentioned above.
For once, I have tried this:
public void scaleDown() {
int width = stdImageBmp.getWidth();
int height = stdImageBmp.getHeight();
Matrix matrix = new Matrix();
float scaleWidth = ((float) MAX_WIDTH) / width;
float scaleHeight = ((float) MAX_HEIGHT) / height;
matrix.postScale(scaleWidth, scaleHeight);
stdImageBmp = Bitmap.createBitmap(stdImageBmp, 0, 0, width, height, matrix, true);
File Image = new File("path");
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
//compress bmp
stdImageBmp.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream);
byte[] byteArray = byteArrayOutputStream.toByteArray();
imgViewStd.setImageBitmap(stdImageBmp);
Log.d("resizedBitmap", stdImageBmp.toString());
width = stdImageBmp.getWidth();
height = stdImageBmp.getHeight();
System.out.println("imgWidth" + width);
System.out.println("imgHeight" + height);
}
you can use this code to resize a bitmap and for image size < 1MB i recommend use resolution of 480x640
public Bitmap getResizedBitmap(Bitmap bm, int newWidth, int newHeight) {
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// CREATE A MATRIX FOR THE MANIPULATION
Matrix matrix = new Matrix();
// RESIZE THE BIT MAP
matrix.postScale(scaleWidth, scaleHeight);
// "RECREATE" THE NEW BITMAP
return Bitmap.createBitmap(
bm, 0, 0, width, height, matrix, false);
}
If you really want the Bitmap that scales down to the Bitmap that is the closest to a given amount of bytes, heres the method I use. (It does not uses a while loop)
NOTE: This method only works if passed bitmap is in ARGB_8888 configuration.
See: Compress bitmap to a specific byte size in Android for the conversion method.
/**
* Method to scale the Bitmap to respect the max bytes
*
* #param input the Bitmap to scale if too large
* #param maxBytes the amount of bytes the Image may be
* #return The scaled bitmap or the input if already valid
* #Note: The caller of this function is responsible for recycling once the input is no longer needed
*/
public static Bitmap scaleBitmap(final Bitmap input, final long maxBytes) {
final int currentWidth = input.getWidth();
final int currentHeight = input.getHeight();
final int currentPixels = currentWidth * currentHeight;
// Get the amount of max pixels:
// 1 pixel = 4 bytes (R, G, B, A)
final long maxPixels = maxBytes / 4; // Floored
if (currentPixels <= maxPixels) {
// Already correct size:
return input;
}
// Scaling factor when maintaining aspect ratio is the square root since x and y have a relation:
final double scaleFactor = Math.sqrt(maxPixels / (double) currentPixels);
final int newWidthPx = (int) Math.floor(currentWidth * scaleFactor);
final int newHeightPx = (int) Math.floor(currentHeight * scaleFactor);
Timber.i("Scaled bitmap sizes are %1$s x %2$s when original sizes are %3$s x %4$s and currentPixels %5$s and maxPixels %6$s and scaled total pixels are: %7$s",
newWidthPx, newHeightPx, currentWidth, currentHeight, currentPixels, maxPixels, (newWidthPx * newHeightPx));
final Bitmap output = Bitmap.createScaledBitmap(input, newWidthPx, newHeightPx, true);
return output;
}
Where the Sample use would look something like:
// (1 MB)
final long maxBytes = 1024 * 1024;
// Scale it
final Bitmap scaledBitmap = BitmapUtils.scaleBitmap(yourBitmap, maxBytes);
if(scaledBitmap != yourBitmap){
// Recycle the bitmap since we can use the scaled variant:
yourBitmap.recycle();
}
// ... do something with the scaled bitmap
I've tried to comment it but the comment become too big.
So, I've tested many solutions and it seems like there are only TWO solutions for this problem, which give some results.
Lets discuss Koen solution first. What it actually does is creates a scaled JPG
Bitmap.createScaledBitmap(input, newWidthPx, newHeightPx, true)
Seems like it does not compress it at all but just cuts off the resolution.
I've tested this code and when I pass MAX_IMAGE_SIZE = 1024000 it gives me 350kb compressed image out of 2.33Mb original image. Bug?
Also it lacks quality. I was unable to recognize a text on A4 sheet of paper photo made by Google Pixel.
There is another solution to this problem, which gives good quality, but lacks in speed.
A WHILE LOOP!
Basically you just loop through image size, until you get the desired size
private fun scaleBitmap() {
if (originalFile.length() > MAX_IMAGE_SIZE) {
var streamLength = MAX_IMAGE_SIZE
var compressQuality = 100
val bmpStream = ByteArrayOutputStream()
while (streamLength >= MAX_IMAGE_SIZE) {
bmpStream.use {
it.flush()
it.reset()
}
compressQuality -= 8
val bitmap = BitmapFactory.decodeFile(originalFile.absolutePath, BitmapFactory.Options())
bitmap.compress(Bitmap.CompressFormat.JPEG, compressQuality, bmpStream)
streamLength = bmpStream.toByteArray().size
}
FileOutputStream(compressedFile).use {
it.write(bmpStream.toByteArray())
}
}
}
I think that this approach will consume exponential time depending on image resolution.
9mb image takes up to 12 seconds to compress down to 1mb.
Quality is good.
You can tweak this by reducing the original bitmap resolution(which seems like a constant operation), by doing:
options.inSampleSize = 2;
What we need to do is to somehow calculate compressQuality for any image.
There should be a math around this, so we can determinate compressQuality from original image size or width + height.

How to divide a bitmap into parts that are bitmaps too

I need some info on the possible methods for dividing a bitmap in smaller pieces.
More importantly I would need some options to judge. I have checked many posts and I am still not entirely convinced about what to do:
cut the portion of bitmap
How do I cut out the middle area of the bitmap?
These two posts are some good options I found, but I cant calculate the CPU and RAM cost of each method, or maybe I should not bother with this calculation at all. Nonetheless if I am about to do something, why not do it the best way from the start.
I would be grateful to get some tips and links on bitmap compression so maybe I get better performance combining the two methods.
This function allows you to split a bitmap into and number of rows and columns.
Example Bitmap[][] bitmaps = splitBitmap(bmp, 2, 1);
Would create a vertically split bitmap stored in a two dimensional array.
2 columns 1 row
Example Bitmap[][] bitmaps = splitBitmap(bmp, 2, 2);
Would split a bitmap into four bitmaps stored in a two dimensional array.
2 columns 2 rows
public Bitmap[][] splitBitmap(Bitmap bitmap, int xCount, int yCount) {
// Allocate a two dimensional array to hold the individual images.
Bitmap[][] bitmaps = new Bitmap[xCount][yCount];
int width, height;
// Divide the original bitmap width by the desired vertical column count
width = bitmap.getWidth() / xCount;
// Divide the original bitmap height by the desired horizontal row count
height = bitmap.getHeight() / yCount;
// Loop the array and create bitmaps for each coordinate
for(int x = 0; x < xCount; ++x) {
for(int y = 0; y < yCount; ++y) {
// Create the sliced bitmap
bitmaps[x][y] = Bitmap.createBitmap(bitmap, x * width, y * height, width, height);
}
}
// Return the array
return bitmaps;
}
You want to divide a bitmap into parts. I assume you want to cut equal parts from bitmap. Say for example you need four equal parts from a bitmap.
This is a method which divides a bitmap onto four equal parts and has it in an array of bitmaps.
public Bitmap[] splitBitmap(Bitmap src) {
Bitmap[] divided = new Bitmap[4];
imgs[0] = Bitmap.createBitmap(
src,
0, 0,
src.getWidth() / 2, src.getHeight() / 2
);
imgs[1] = Bitmap.createBitmap(
src,
src.getWidth() / 2, 0,
src.getWidth() / 2, src.getHeight() / 2
);
imgs[2] = Bitmap.createBitmap(
src,
0, src.getHeight() / 2,
src.getWidth() / 2, src.getHeight() / 2
);
imgs[3] = Bitmap.createBitmap(
src,
src.getWidth() / 2, src.getHeight() / 2,
src.getWidth() / 2, src.getHeight() / 2
);
return divided;
}

Android Image Resize basic

I like to resize an bitmap,if it is big make it small and place it on an specific location in the surface view, I need to get device width and height and then bitmap size and place them in an surface view and then take another image resize it and place it in any location has i like.
How to know the location co ordinates first(to place second image - this should be device independent if larger/smaller screen layout should not change)
and how to scale an big bitmap and set as background so i can draw an image over it.
my code :
final int canvasWidth = getWidth();
final int canvasHeight = getHeight();
int imageWidth = img.getWidth();
int imageHeight = img.getHeight();
float scaleFactor = Math.min( (float)canvasWidth / imageWidth,
(float)canvasHeight / imageHeight );
Bitmap scaled = Bitmap.createScaledBitmap( img,
(int)(scaleFactor * imageWidth),
(int)(scaleFactor * imageHeight),
true );
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(scaled, 10, 10, null);
this scale the big image but it doesn't fit the whole screen "img - bitmap is like an background image "
Someone can help me to understand the resize basics (I'm new so finding hard to understand resizing) to resize image to fit screen and resize any image to smaller one and place it in any location i like .
Store your bitmap as a source for manipulation and display it using ImageView:
Bitmap realImage = BitmapFactory.decodeFile(filePathFromActivity.toString());
Bitmap newBitmap = scaleDown(realImage, MAX_IMAGE_SIZE, true);
imageView.setImageBitmap(newBitmap);
//scale down method
public static Bitmap scaleDown(Bitmap realImage, float maxImageSize,
boolean filter) {
float ratio = Math.min(
(float) maxImageSize / realImage.getWidth(),
(float) maxImageSize / realImage.getHeight());
int width = Math.round((float) ratio * realImage.getWidth());
int height = Math.round((float) ratio * realImage.getHeight());
Bitmap newBitmap = Bitmap.createScaledBitmap(realImage, width,
height, filter);
return newBitmap;
}
And set your ImageView's width and height to "fill_parent".

Categories

Resources