I'm trying to compress an image that I saved in the file. I'm trying to compress the File into 1MB. I try a few way but it usually make an OutofMemoryError.
and then i tried to use this solution, but it makes the bitmap blank.
How to compress bitmap from 10mb image from camera to 300kb beforw setting to imageview in android
Here is my code :
System.gc();
getActivity().getContentResolver().notifyChange(mImageTempUri, null);
Bitmap bitmap;
bitmap = BitmapFactory.decodeFile(mImageDirectory + mImageName, options);
if(bitmap == null){
howRequestFailedErrorMessage("Gambar gagal di-upload");
return;
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 25, bytes);
File f = new File(mImageDirectory + mImageName);
if(f.exists()){
f.delete();
}
FileOutputStream fo;
try {
fo = new FileOutputStream(f);
fo.write(bytes.toByteArray());
fo.flush();
fo.close();
} catch (IOException e) {
e.printStackTrace();
}
bitmap.recycle();
okay, I got my own answer
File f = new File(mImageDirectory + mImageName);
if(f.exists()){
f.delete();
}
int MAX_IMAGE_SIZE = 1000 * 1024;
int streamLength = MAX_IMAGE_SIZE;
int compressQuality = 105;
ByteArrayOutputStream bmpStream = new ByteArrayOutputStream();
while (streamLength >= MAX_IMAGE_SIZE && compressQuality > 5) {
try {
bmpStream.flush();//to avoid out of memory error
bmpStream.reset();
} catch (IOException e) {
e.printStackTrace();
}
compressQuality -= 5;
bitmap.compress(Bitmap.CompressFormat.JPEG, compressQuality, bmpStream);
byte[] bmpPicByteArray = bmpStream.toByteArray();
streamLength = bmpPicByteArray.length;
if(BuildConfig.DEBUG) {
Log.d("test upload", "Quality: " + compressQuality);
Log.d("test upload", "Size: " + streamLength);
}
}
FileOutputStream fo;
try {
fo = new FileOutputStream(f);
fo.write(bmpStream.toByteArray());
fo.flush();
fo.close();
} catch (IOException e) {
e.printStackTrace();
}
Kotlin Way
if (file.length() > MAX_IMAGE_SIZE) {
var streamLength = MAX_IMAGE_SIZE
var compressQuality = 105
val bmpStream = ByteArrayOutputStream()
while (streamLength >= MAX_IMAGE_SIZE && compressQuality > 5) {
bmpStream.use {
it.flush()
it.reset()
}
compressQuality -= 5
val bitmap = BitmapFactory.decodeFile(file.absolutePath, BitmapFactory.Options())
bitmap.compress(Bitmap.CompressFormat.JPEG, compressQuality, bmpStream)
val bmpPicByteArray = bmpStream.toByteArray()
streamLength = bmpPicByteArray.size
if (BuildConfig.DEBUG) {
Log.d("test upload", "Quality: $compressQuality")
Log.d("test upload", "Size: $streamLength")
}
}
FileOutputStream(file).use {
it.write(bmpStream.toByteArray())
}
}
Constant
companion object {
//2000 * 1024 = 2 MB
private const val MAX_IMAGE_SIZE = 2048000
}
Related
After hours of searching, i'm finally able to save screenshot of ArFragment.
but the problem is it only saves the current image of the camera except the 3D object which is placed.
how can i get the full screenshot (current image of the camera + 3D object which is placed)?
the codes that i used is below here.
ImageButton btn3 = (ImageButton)findViewById(R.id.camera_btn);
btn3.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onSceneUpdate((FrameTime) frameTime);
Toast.makeText(AR_Activity.this, "스크린샷이 저장되었습니다.", Toast.LENGTH_SHORT).show();
}
});
private void onSceneUpdate(FrameTime frameTime) {
try {
Date now = new Date();
android.text.format.DateFormat.format("yyyy-MM-dd_hh:mm:ss", now);
String mPath = Environment.getExternalStorageDirectory().toString() + "/" + now + ".jpg";
Frame currentFrame = arFragment.getArSceneView().getArFrame();
Image currentImage = currentFrame.acquireCameraImage();
int imageFormat = currentImage.getFormat();
if (imageFormat == ImageFormat.YUV_420_888) {
Log.d("ImageFormat", "Image format is YUV_420_888");
}
WriteImageInformation((Image) currentImage, (String) mPath);
} catch (Exception e) {
}
}
private static byte[] NV21toJPEG(byte[] nv21, int width, int height) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
YuvImage yuv = new YuvImage(nv21, ImageFormat.NV21, width, height, null);
yuv.compressToJpeg(new Rect(0, 0, width, height), 100, out);
return out.toByteArray();
}
public static void WriteImageInformation(Image image, String path) {
byte[] data = null;
data = NV21toJPEG(YUV_420_888toNV21(image),
image.getWidth(), image.getHeight());
BufferedOutputStream bos = null;
try {
bos = new BufferedOutputStream(new FileOutputStream(path));
bos.write(data);
bos.flush();
bos.close();
} catch (Throwable e) {
e.printStackTrace();
}
}
private static byte[] YUV_420_888toNV21(Image image) {
byte[] nv21;
ByteBuffer yBuffer = image.getPlanes()[0].getBuffer();
ByteBuffer uBuffer = image.getPlanes()[1].getBuffer();
ByteBuffer vBuffer = image.getPlanes()[2].getBuffer();
int ySize = yBuffer.remaining();
int uSize = uBuffer.remaining();
int vSize = vBuffer.remaining();
nv21 = new byte[ySize + uSize + vSize];
//U and V are swapped
yBuffer.get(nv21, 0, ySize);
vBuffer.get(nv21, ySize, vSize);
uBuffer.get(nv21, ySize + vSize, uSize);
return nv21;
}
Use PixelCopy. it worked perfectly.
For those who might wonder,
I will add my code below.
ImageButton btn3 = (ImageButton)findViewById(R.id.camera_btn);
btn3.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
takePhoto();
}
});
private String generateFilename() {
//현재시간을 기준으로 파일 이름 생성
String date =
new SimpleDateFormat("yyyyMMddHHmmss", java.util.Locale.getDefault()).format(new Date());
return Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES) + File.separator + "IM/" + date + "_screenshot.jpg";
}
private void saveBitmapToDisk(Bitmap bitmap, String filename) throws IOException {
//사용자의 갤러리에 IM 디렉토리 생성 및 Bitmap 을 JPEG 형식으로 갤러리에 저장
File out = new File(filename);
if (!out.getParentFile().exists()) {
out.getParentFile().mkdirs();
}
try (FileOutputStream outputStream = new FileOutputStream(filename);
ByteArrayOutputStream outputData = new ByteArrayOutputStream()) {
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputData);
outputData.writeTo(outputStream);
outputStream.flush();
outputStream.close();
} catch (IOException ex) {
throw new IOException("Failed to save bitmap to disk", ex);
}
}
private void takePhoto(){
//PixelCopy 를 사용하여 카메라 화면과 object 를 bitmap 으로 생성
final String filename = generateFilename();
ArSceneView view = arFragment.getArSceneView();
final Bitmap bitmap = Bitmap.createBitmap(view.getWidth(),view.getHeight(),
Bitmap.Config.ARGB_8888);
final HandlerThread handlerThread = new HandlerThread("PixelCopier");
handlerThread.start();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
PixelCopy.request(view, bitmap, (copyResult) -> {
if (copyResult == PixelCopy.SUCCESS) {
try {
saveBitmapToDisk(bitmap, filename);
//Media Scanning 실시
Uri uri = Uri.parse("file://" + filename);
Intent i = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
i.setData(uri);
sendBroadcast(i);
} catch (IOException e) {
Toast toast = Toast.makeText(AR_Activity.this, e.toString(),
Toast.LENGTH_LONG);
toast.show();
return;
}
Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content),
"스크린샷이 저장되었습니다.", Snackbar.LENGTH_LONG);
snackbar.setAction("갤러리에서 보기", v -> {
//어플 내에서 저장한 스크린샷을 확인 가능
File photoFile = new File(filename);
Uri photoURI = FileProvider.getUriForFile(AR_Activity.this,
AR_Activity.this.getPackageName() + ".ar.codelab.name.provider",
photoFile);
Intent intent = new Intent(Intent.ACTION_VIEW, photoURI);
intent.setDataAndType(photoURI, "image/*");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
});
snackbar.show();
} else {
Toast toast = Toast.makeText(AR_Activity.this,
"스크린샷 저장 실패!: " + copyResult, Toast.LENGTH_LONG);
toast.show();
}
handlerThread.quitSafely();
}, new Handler(handlerThread.getLooper()));
}
}
I made a gallery application and stored more images in a SQL Server databse,
now I am getting images takes a lot of time.
So I want image uploading time MB to KB conversion (i.e.: 20KB, 30KB).
What shall I do? Help me, please.
My code is:
private void onSelectFromGalleryResult(Intent data) {
Bitmap bm = null;
if (data != null) {
try {
bm = MediaStore.Images.Media.getBitmap(Admin.this.getApplicationContext().getContentResolver(), data.getData());
imgadminview.setImageBitmap(bm);
int bitmapByteCount= BitmapCompat.getAllocationByteCount(bm);
System.out.print(bitmapByteCount);
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
byteArray = bytes.toByteArray();
long lengthbmp = byteArray.length;
System.out.print(lengthbmp);
if(lengthbmp<=1048576){
//KB or more
rxKb = lengthbmp/1024;
System.out.print(rxKb + " KBs");}
else if(rxKb>=1024) {
//MB or more
rxMB = rxKb / 1024;
System.out.print(rxMB + " MBs");
}else if(rxMB>=1024) {
//GB or more
long rxGB = rxMB / 1024;
System.out.print(rxGB + Long.toString(rxGB));
}else {
//rxMB>1024
//rxKb > 1024
}//rxBytes>=1024
byte[] test=String.valueOf(rxKb).getBytes();
encodedImage = Base64.encodeToString(test, Base64.DEFAULT);
btarray = Base64.decode(encodedImage, Base64.DEFAULT);
bmimage = BitmapFactory.decodeByteArray(byteArray, 0, btarray.length);
} catch (IOException e) {
e.printStackTrace();
}
}
imgadminview.setImageBitmap(bm);
}
At start up of my app I want to extract my images (if they does not exists) from drawable folder to internal app folder to use later with FileProvider. Images have dimensions 2000*2000 and average size 380kb, format png.
Those images are not to be displayed (smaller ones are used to display). They are only for file sharing and I have to keep their original size.
I get out of memory at calling
Bitmap bm = BitmapFactory.decodeResource(getResources(), imageResID);
Code
private void extractImages() {
TypedArray imgs = getResources().obtainTypedArray(R.array.smile_list_share);
File imagePath = new File(getFilesDir(), "images");
File checkImage;
for (int i = 0; i < imgs.length(); i++) {
int imageResID = imgs.getResourceId(i, 0);
if (imageResID > 0) {
String name = getResources().getResourceEntryName(imageResID);
checkImage = new File(imagePath, name + ".png");
if (!checkImage.exists()) {
Bitmap bm = BitmapFactory.decodeResource(getResources(), imageResID);
boolean b = saveBitmapToFile(imagePath, name + ".png", bm, Bitmap.CompressFormat.PNG, 100);
Log.e("mcheck","saved "+b+", file "+name);
Log.e("mcheck", "file does not exists " + name);
} else {
Log.e("mcheck", "file exists " + name);
}
} else {
Log.e("mcheck", "ERROR " + i);
}
}
imgs.recycle();
}
public boolean saveBitmapToFile(File dir, String fileName, Bitmap bm,
Bitmap.CompressFormat format, int quality) {
File imageFile = new File(dir, fileName);
FileOutputStream fos = null;
try {
fos = new FileOutputStream(imageFile);
bm.compress(format, quality, fos);
bm.recycle();
fos.close();
return true;
} catch (IOException e) {
Log.e("app", e.getMessage());
if (fos != null) {
try {
fos.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
return false;
}
You have to call recycle on your bitmap object everytime you are done with it.
Here is a good guide on how to manipulate bitmaps efficiently
https://developer.android.com/training/displaying-bitmaps/load-bitmap.html
I found that I dont need to create bitmap object at all. It is possible to obtaine intput stream from getResourses directly.
public boolean saveBitmapToFile(File dir, String fileName, int imageResourse) {
File imageFile = new File(dir, fileName);
FileOutputStream fos = null;
InputStream inputStream = null;
try {
fos = new FileOutputStream(imageFile);
inputStream = getResources().openRawResource(imageResourse);
int bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
int len = 0;
while ((len = inputStream.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
inputStream.close();
fos.close();
return true;
} catch (IOException e) {
Log.e("app", e.getMessage());
if (fos != null) {
try {
fos.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
return false;
}
I have the following code:
YuvImage yuv = new YuvImage(result.getExtractImageData(),
camera.getParameters().getPreviewFormat(),
result.getWidth(),
result.getHeight(), null);
ByteArrayOutputStream out = new ByteArrayOutputStream();
yuv.compressToJpeg(new Rect(0, 0, result.getWidth(), result.getHeight()), 100, out);
byte[] bytes = out.toByteArray();
Bitmap image = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
image = RotateImage(image, rotation);
createDirectoryAndSaveFile(image, "Image_" + result.getCaptureTime() + ".jpg");
String filePath = Environment.getExternalStorageDirectory() + "/MyFolder/Image_" + result.getCaptureTime() + ".jpg";
try {
ExifInterface exif = new ExifInterface(filePath);
exif.getAttribute("UserComment");
// call this next setAttributes a few times to write all the GPS data to it.
exif.setAttribute("UserComment", String.valueOf(result.getCaptureTime()));
exif.saveAttributes();
}
catch (IOException e) {
e.printStackTrace();
}
It is supposed to punt under UserComment the result.getCaptureTime() at which it was captured from the camera. I download it to a Windows folder and I can't see the properties I just created...
What I'm doing wrong?
EDIT:
private void createDirectoryAndSaveFile(Bitmap imageToSave, String fileName)
{
File direct = new File(Environment.getExternalStorageDirectory() + "/MyFolder");
if (!direct.exists()) {
File wallpaperDirectory = new File("/sdcard/MyFolder/");
wallpaperDirectory.mkdirs();
}
File file = new File(new File("/sdcard/MyFolder/"), fileName);
if (file.exists()) {
file.delete();
}
try {
FileOutputStream out = new FileOutputStream(file);
imageToSave.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
I want to Save Image from res/drawable to Image Gallery. I am using following code but it is doing nothing.
What is the wrong with my code ? String Drawable stands for Image Name which is there in drawable folder.
File direct = new File(Environment.getExternalStorageDirectory()
+ "/Images");
if (!direct.exists()) {
direct.mkdirs();
}
ByteArrayOutputStream bos = null;
FileOutputStream fos = null;
try {
Bitmap bitmap = BitmapFactory.decodeResource(
context.getResources(),
context.getResources().getIdentifier(
"#drawable/" + Drawable, "drawable",
context.getPackageName()));
bos = new ByteArrayOutputStream();
bitmap.compress(CompressFormat.JPEG, 95, bos);
byte[] bitmapdata = bos.toByteArray();
fos = new FileOutputStream(direct + "/" + "IMG-" + CurrentDateTime
+ ".jpg");
fos.write(bitmapdata);
} catch (Exception e) {
Log.e("Internal Image Save Error->", e.toString());
} finally {
try {
if (bos != null) {
bos.close();
}
if (fos != null) {
fos.close();
fos.flush();
}
} catch (IOException ignored) {
Log.e("Internal Image Save Error->", ignored.toString());
}
}
I just found that it is saving image but It is taking some time like 10 mins.
Copy Image from Drawable to Gallery : It is giving File Not Found Exception on Input Stream. String Drawable is image name, i.e. data1
public static void downloadInternalImage(String Drawable, Context context) {
Toast.makeText(context, "Downloading Image...\nPlease Wait.",
Toast.LENGTH_LONG).show();
File direct = new File(Environment.getExternalStorageDirectory()
+ "/Images");
if (!direct.exists()) {
direct.mkdirs();
}
InputStream input = null;
OutputStream output = null;
try {
input = new FileInputStream("android.resource://"
+ context.getPackageName() + "/drawable/" + Drawable + "");
output = new FileOutputStream(direct + "/" + "IMG-"
+ CurrentDateTime + ".jpg");
byte[] buf = new byte[1024];
int len;
while ((len = input.read(buf)) > 0) {
output.write(buf, 0, len);
}
Toast.makeText(context, "Image Saved.", Toast.LENGTH_LONG).show();
} catch (Exception e) {
Log.e("Internal Image Save Error->", e.toString());
Toast.makeText(context,
"Couldn't Save Image.\nError:" + e.toString() + "",
Toast.LENGTH_LONG).show();
} finally {
try {
if (input != null) {
input.close();
}
if (output != null) {
output.close();
}
} catch (IOException ignored) {
Log.e("Internal Image Save Error->", ignored.toString());
Toast.makeText(
context,
"Couldn't Save Image.\nError:" + ignored.toString()
+ "", Toast.LENGTH_LONG).show();
}
}
}