Wiele aplikacji umożliwia udostępnianie obrazu, który jest pobierany z galerii.
Czy przesyłają oryginalny plik obrazu? Co jest jak 1-3 mb? A może przetwarzają?
W każdym razie, jak mogę pobrać obraz ze ścieżki pliku, zmniejszyć jego rozmiar, obniżając rozdzielczość i zapisać go w innym miejscu i spróbować przesłać?
Próbowałem:
Bitmap photo = decodeSampledBitmapFromFile(filePath, DESIRED_WIDTH,
DESIRED_HEIGHT);
FileOutputStream out = new FileOutputStream(filePath);
photo.compress(Bitmap.CompressFormat.JPEG, 100, out);
public static Bitmap decodeSampledBitmapFromFile(String path, int reqWidth,
int reqHeight) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
final int height = options.outHeight;
final int width = options.outWidth;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
int inSampleSize = 1;
if (height > reqHeight) {
inSampleSize = Math.round((float) height / (float) reqHeight);
}
int expectedWidth = width / inSampleSize;
if (expectedWidth > reqWidth) {
inSampleSize = Math.round((float) width / (float) reqWidth);
}
options.inSampleSize = inSampleSize;
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(path, options);
}
Ale czy to jest właściwy sposób? Ponieważ widziałem tu odpowiedzi sugerującecompression operation takes rather big amount of time
Odpowiedzi:
Używam tej funkcji, aby zmniejszyć rozmiar obrazu przed przesłaniem go, zmniejsza rozmiar obrazu do prawie 200 KB i utrzymuje stosunkowo dobrą jakość, możesz zmodyfikować go, aby spełnić swój cel, zmieniając REQUIRED_SIZE i inSampleSize:
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; } }
UWAGA: Zmieniam rozmiar i zastępuję oryginalny obraz pliku w tej funkcji, możesz zapisać go również w innym pliku.
Mam nadzieję, że może ci to pomóc.
źródło
To działa świetnie. Spróbuj tego
private String decodeFile(String path,int DESIREDWIDTH, int DESIREDHEIGHT) { String strMyImagePath = null; Bitmap scaledBitmap = null; try { // Part 1: Decode image Bitmap unscaledBitmap = ScalingUtilities.decodeFile(path, DESIREDWIDTH, DESIREDHEIGHT, ScalingLogic.FIT); if (!(unscaledBitmap.getWidth() <= DESIREDWIDTH && unscaledBitmap.getHeight() <= DESIREDHEIGHT)) { // Part 2: Scale image scaledBitmap = ScalingUtilities.createScaledBitmap(unscaledBitmap, DESIREDWIDTH, DESIREDHEIGHT, ScalingLogic.FIT); } else { unscaledBitmap.recycle(); return path; } // Store to tmp file String extr = Environment.getExternalStorageDirectory().toString(); File mFolder = new File(extr + "/TMMFOLDER"); if (!mFolder.exists()) { mFolder.mkdir(); } String s = "tmp.png"; File f = new File(mFolder.getAbsolutePath(), s); strMyImagePath = f.getAbsolutePath(); FileOutputStream fos = null; try { fos = new FileOutputStream(f); scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 75, fos); fos.flush(); fos.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } scaledBitmap.recycle(); } catch (Throwable e) { } if (strMyImagePath == null) { return path; } return strMyImagePath; }
ScalingUtilities.java
import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.BitmapFactory.Options; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; /** * Class containing static utility methods for bitmap decoding and scaling * * @author */ public class ScalingUtilities { /** * Utility function for decoding an image resource. The decoded bitmap will * be optimized for further scaling to the requested destination dimensions * and scaling logic. * * @param res The resources object containing the image data * @param resId The resource id of the image data * @param dstWidth Width of destination area * @param dstHeight Height of destination area * @param scalingLogic Logic to use to avoid image stretching * @return Decoded bitmap */ public static Bitmap decodeResource(Resources res, int resId, int dstWidth, int dstHeight, ScalingLogic scalingLogic) { Options options = new Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, options); options.inJustDecodeBounds = false; options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, dstWidth, dstHeight, scalingLogic); Bitmap unscaledBitmap = BitmapFactory.decodeResource(res, resId, options); return unscaledBitmap; } public static Bitmap decodeFile(String path, int dstWidth, int dstHeight, ScalingLogic scalingLogic) { Options options = new Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(path, options); options.inJustDecodeBounds = false; options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, dstWidth, dstHeight, scalingLogic); Bitmap unscaledBitmap = BitmapFactory.decodeFile(path, options); return unscaledBitmap; } /** * Utility function for creating a scaled version of an existing bitmap * * @param unscaledBitmap Bitmap to scale * @param dstWidth Wanted width of destination bitmap * @param dstHeight Wanted height of destination bitmap * @param scalingLogic Logic to use to avoid image stretching * @return New scaled bitmap object */ public static Bitmap createScaledBitmap(Bitmap unscaledBitmap, int dstWidth, int dstHeight, ScalingLogic scalingLogic) { Rect srcRect = calculateSrcRect(unscaledBitmap.getWidth(), unscaledBitmap.getHeight(), dstWidth, dstHeight, scalingLogic); Rect dstRect = calculateDstRect(unscaledBitmap.getWidth(), unscaledBitmap.getHeight(), dstWidth, dstHeight, scalingLogic); Bitmap scaledBitmap = Bitmap.createBitmap(dstRect.width(), dstRect.height(), Config.ARGB_8888); Canvas canvas = new Canvas(scaledBitmap); canvas.drawBitmap(unscaledBitmap, srcRect, dstRect, new Paint(Paint.FILTER_BITMAP_FLAG)); return scaledBitmap; } /** * ScalingLogic defines how scaling should be carried out if source and * destination image has different aspect ratio. * * CROP: Scales the image the minimum amount while making sure that at least * one of the two dimensions fit inside the requested destination area. * Parts of the source image will be cropped to realize this. * * FIT: Scales the image the minimum amount while making sure both * dimensions fit inside the requested destination area. The resulting * destination dimensions might be adjusted to a smaller size than * requested. */ public static enum ScalingLogic { CROP, FIT } /** * Calculate optimal down-sampling factor given the dimensions of a source * image, the dimensions of a destination area and a scaling logic. * * @param srcWidth Width of source image * @param srcHeight Height of source image * @param dstWidth Width of destination area * @param dstHeight Height of destination area * @param scalingLogic Logic to use to avoid image stretching * @return Optimal down scaling sample size for decoding */ public static int calculateSampleSize(int srcWidth, int srcHeight, int dstWidth, int dstHeight, ScalingLogic scalingLogic) { if (scalingLogic == ScalingLogic.FIT) { final float srcAspect = (float)srcWidth / (float)srcHeight; final float dstAspect = (float)dstWidth / (float)dstHeight; if (srcAspect > dstAspect) { return srcWidth / dstWidth; } else { return srcHeight / dstHeight; } } else { final float srcAspect = (float)srcWidth / (float)srcHeight; final float dstAspect = (float)dstWidth / (float)dstHeight; if (srcAspect > dstAspect) { return srcHeight / dstHeight; } else { return srcWidth / dstWidth; } } } /** * Calculates source rectangle for scaling bitmap * * @param srcWidth Width of source image * @param srcHeight Height of source image * @param dstWidth Width of destination area * @param dstHeight Height of destination area * @param scalingLogic Logic to use to avoid image stretching * @return Optimal source rectangle */ public static Rect calculateSrcRect(int srcWidth, int srcHeight, int dstWidth, int dstHeight, ScalingLogic scalingLogic) { if (scalingLogic == ScalingLogic.CROP) { final float srcAspect = (float)srcWidth / (float)srcHeight; final float dstAspect = (float)dstWidth / (float)dstHeight; if (srcAspect > dstAspect) { final int srcRectWidth = (int)(srcHeight * dstAspect); final int srcRectLeft = (srcWidth - srcRectWidth) / 2; return new Rect(srcRectLeft, 0, srcRectLeft + srcRectWidth, srcHeight); } else { final int srcRectHeight = (int)(srcWidth / dstAspect); final int scrRectTop = (int)(srcHeight - srcRectHeight) / 2; return new Rect(0, scrRectTop, srcWidth, scrRectTop + srcRectHeight); } } else { return new Rect(0, 0, srcWidth, srcHeight); } } /** * Calculates destination rectangle for scaling bitmap * * @param srcWidth Width of source image * @param srcHeight Height of source image * @param dstWidth Width of destination area * @param dstHeight Height of destination area * @param scalingLogic Logic to use to avoid image stretching * @return Optimal destination rectangle */ public static Rect calculateDstRect(int srcWidth, int srcHeight, int dstWidth, int dstHeight, ScalingLogic scalingLogic) { if (scalingLogic == ScalingLogic.FIT) { final float srcAspect = (float)srcWidth / (float)srcHeight; final float dstAspect = (float)dstWidth / (float)dstHeight; if (srcAspect > dstAspect) { return new Rect(0, 0, dstWidth, (int)(dstWidth / srcAspect)); } else { return new Rect(0, 0, (int)(dstHeight * srcAspect), dstHeight); } } else { return new Rect(0, 0, dstWidth, dstHeight); } } }
źródło
ten kod zmniejsza rozmiar obrazu
private Bitmap compressImage(Bitmap image) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//Compression quality, here 100 means no compression, the storage of compressed data to baos int options = 90; while (baos.toByteArray().length / 1024 > 400) { //Loop if compressed picture is greater than 400kb, than to compression baos.reset();//Reset baos is empty baos image.compress(Bitmap.CompressFormat.JPEG, options, baos);//The compression options%, storing the compressed data to the baos options -= 10;//Every time reduced by 10 } ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//The storage of compressed data in the baos to ByteArrayInputStream Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//The ByteArrayInputStream data generation return bitmap; }
źródło
Bitmap bitmap
była nadal tego samego rozmiaru co oryginalne dane wejścioweBitmap image
Oto rozwiązanie, które jest obsługiwane w pamięci i nie wymaga faktycznego generowania pliku w systemie plików.
Z jakiegoś fragmentu, po wybraniu przez użytkownika pliku obrazu:
@Override public void onActivityResult(int requestCode, int resultCode, Intent imageReturnedIntent) { super.onActivityResult(requestCode, resultCode, imageReturnedIntent); if (imageReturnedIntent == null || imageReturnedIntent.getData() == null) { return; } // aiming for ~500kb max. assumes typical device image size is around 2megs int scaleDivider = 4; try { // 1. Convert uri to bitmap Uri imageUri = imageReturnedIntent.getData(); Bitmap fullBitmap = MediaStore.Images.Media.getBitmap(getActivity().getContentResolver(), imageUri); // 2. Get the downsized image content as a byte[] int scaleWidth = fullBitmap.getWidth() / scaleDivider; int scaleHeight = fullBitmap.getHeight() / scaleDivider; byte[] downsizedImageBytes = getDownsizedImageBytes(fullBitmap, scaleWidth, scaleHeight); // 3. Upload the byte[]; Eg, if you are using Firebase StorageReference storageReference = FirebaseStorage.getInstance().getReference("/somepath"); storageReference.putBytes(downsizedImageBytes); } catch (IOException ioEx) { ioEx.printStackTrace(); } } public byte[] getDownsizedImageBytes(Bitmap fullBitmap, int scaleWidth, int scaleHeight) throws IOException { Bitmap scaledBitmap = Bitmap.createScaledBitmap(fullBitmap, scaleWidth, scaleHeight, true); // 2. Instantiate the downsized image content as a byte[] ByteArrayOutputStream baos = new ByteArrayOutputStream(); scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos); byte[] downsizedImageBytes = baos.toByteArray(); return downsizedImageBytes; }
Dzięki:
źródło
Oto moje rozwiązanie
/* * This procedure will replace the original image * So you need to do a tmp copy to send before reduce */ public static boolean reduceImage(String path, long maxSize) { File img = new File(path); boolean result = false; BitmapFactory.Options options = new BitmapFactory.Options(); Bitmap bitmap = null; options.inSampleSize=1; while (img.length()>maxSize) { options.inSampleSize = options.inSampleSize+1; bitmap = BitmapFactory.decodeFile(path, options); img.delete(); try { FileOutputStream fos = new FileOutputStream(path); img.compress(path.toLowerCase().endsWith("png")? Bitmap.CompressFormat.PNG: Bitmap.CompressFormat.JPEG, 100, fos); fos.close(); result = true; }catch (Exception errVar) { errVar.printStackTrace(); } }; return result; }
EDYCJA Usunięto inne wywołania procedur
źródło
oto metoda, której używam w kotlin :
Uwaga: wypróbowałem to z 3 obrazami, każdy z nich miał 6 MB i podczas jednego połączenia
private fun Bitmap.compress(cacheDir: File, f_name: String): File? { val f = File(cacheDir, "user$f_name.jpg") f.createNewFile() ByteArrayOutputStream().use { stream -> compress(Bitmap.CompressFormat.JPEG, 70, stream) val bArray = stream.toByteArray() FileOutputStream(f).use { os -> os.write(bArray) } }//stream return f }
źródło
Odpowiedź @ MBH ma dziwny i niepotrzebny sposób uzyskania wyniku, który może spowodować ominięcie pętli while, jeśli plik ma większą liczbę pikseli niż 6X
REQUIRED_SIZE = 75;
.o.inSampleSize = 6;
jest przyczyną tego i udaje mu się tylko zawinąć dozwoloną liczbę pikseli osiowych (6 * 75 = 450). Poniżej znajduje się odnowiona wersja odpowiedzi MBH, która obejmuje również opcję dostosowania nie tylko według pikseli, ale także rozmiaru pamięci://This is the number of pixels that the width and height of the image will be allotted //Note that the number of pixels implies (but does not exclusively determine) the file size of the image private int REQUIRED_SIZE = 720; //Method allows image files to be compressed to a standard maximum size public void saveBitmapToFile(){ try { //Initialize BitmapFactory options to downsize the image BitmapFactory.Options o = new BitmapFactory.Options(); //Image dimensions do not change with change in pixel density o.inJustDecodeBounds = true; //Retrieve file and decode into bitmap, then close the stream when decode complete FileInputStream inputStream = new FileInputStream(imageFile); BitmapFactory.decodeStream(inputStream, null, o); inputStream.close(); //Start with 1:1 scale size int scale = 1; while (o.outWidth / scale / 2 >= REQUIRED_SIZE && o.outHeight / scale / 2 >= REQUIRED_SIZE) { //Find the correct scale value. It should be the power of 2. scale *= 2; } //Create new Bitmap options instance to adjust the image scale...(1/2) BitmapFactory.Options o2 = new BitmapFactory.Options(); o2.inSampleSize = scale; //...then apply with new input stream inputStream = new FileInputStream(imageFile); Bitmap selectedBitmap = BitmapFactory.decodeStream(inputStream, null, o2); inputStream.close(); //Overwrite original file (type 'File') then compress the output file imageFile.createNewFile(); FileOutputStream outputStream = new FileOutputStream(imageFile); selectedBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream); //maxImageSize is arbitrary file size (byte) limit if(imageFile.length() < maxImageSize) { //Set image in place and get the uri imagePost.setImageURI(imageUri); //Reset in case decreased previously REQUIRED_SIZE = 720; } else { Toast.makeText(PostActivity.this, "Your image is " + (imageFile.length() - maxImageSize) / 1000 + "kb too large to upload. Reattempting...", Toast.LENGTH_LONG).show(); REQUIRED_SIZE = REQUIRED_SIZE - 240; //Loop method until goal file size achieved saveBitmapToFile(); } } catch (Exception ignored) { } }
źródło
Użyj tej metody, która zwraca obraz bitmapowy skompresowany do około 200 KB. Możesz go skonfigurować, aby uzyskać obraz bitmapowy o dowolnym rozmiarze.
public static Bitmap scaleImage(Context context, Uri photoUri) throws IOException { InputStream is = context.getContentResolver().openInputStream(photoUri); BitmapFactory.Options dbo = new BitmapFactory.Options(); dbo.inJustDecodeBounds = true; BitmapFactory.decodeStream(is, null, dbo); is.close(); int rotatedWidth, rotatedHeight; int orientation = getOrientation(context, photoUri); if (orientation == 90 || orientation == 270) { rotatedWidth = dbo.outHeight; rotatedHeight = dbo.outWidth; } else { rotatedWidth = dbo.outWidth; rotatedHeight = dbo.outHeight; } Bitmap srcBitmap; is = context.getContentResolver().openInputStream(photoUri); if (rotatedWidth > MAX_IMAGE_DIMENSION || rotatedHeight > MAX_IMAGE_DIMENSION) { float widthRatio = ((float) rotatedWidth) / ((float) MAX_IMAGE_DIMENSION); float heightRatio = ((float) rotatedHeight) / ((float) MAX_IMAGE_DIMENSION); float maxRatio = Math.max(widthRatio, heightRatio); // Create the bitmap from file BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = (int) maxRatio; srcBitmap = BitmapFactory.decodeStream(is, null, options); } else { srcBitmap = BitmapFactory.decodeStream(is); } is.close(); /* * if the orientation is not 0 (or -1, which means we don't know), we * have to do a rotation. */ if (orientation > 0) { Matrix matrix = new Matrix(); matrix.postRotate(orientation); srcBitmap = Bitmap.createBitmap(srcBitmap, 0, 0, srcBitmap.getWidth(), srcBitmap.getHeight(), matrix, true); } String type = context.getContentResolver().getType(photoUri); ByteArrayOutputStream baos = new ByteArrayOutputStream(); if (type.equals("image/png")) { srcBitmap.compress(Bitmap.CompressFormat.PNG, 100, baos); } else if (type.equals("image/jpg") || type.equals("image/jpeg")) { srcBitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos); } byte[] bMapArray = baos.toByteArray(); baos.close(); return BitmapFactory.decodeByteArray(bMapArray, 0, bMapArray.length); }
źródło