Android图像捕获,如何获得质量最低的图片以占用更少的空间

问题描述 投票:0回答:1

我正在使用Android's默认相机来捕捉我的意图。出来的图像质量非常好,我似乎找不到降低图像质量的方法。

即使不实现自定义Camera也是可能的吗?

是否可以将最大大小限制设置为2MB或类似的大小?或仅以可能的最低质量拍摄图像,因为我的应用程序中的图像不需要是高质量的。

public class ImageCaptureIntent {
    public interface ImageCaptureResultListener {
        void onImageCaptured(File image);

        void onImageCaptureError(Exception exception);
    }

    static final int IMAGE_CAPTURE_REQUEST = 1;

    private enum BundleKeys {
        IMAGE_FILE
    }

    private File imageFile;

    public void onSaveInstanceState(@NonNull Bundle outState) {
        if (imageFile != null) {
            outState.putString(BundleKeys.IMAGE_FILE.name(), imageFile.getAbsolutePath());
        }
    }

    public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
        if (savedInstanceState.containsKey(BundleKeys.IMAGE_FILE.name())) {
            imageFile = new File(savedInstanceState.getString(BundleKeys.IMAGE_FILE.name()));
        }
    }

    private static File createTempFile(File directory) throws IOException {
        String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        String filePrefix = "IMG_" + timestamp + "_";

        File file = File.createTempFile(filePrefix,".jpg", directory);
        if (file == null) {
            throw new IOException("Could not create a temp file");
        }

        return file;
    }

    public boolean initiateImageCapture(ImageCaptureResultListener listener, Activity activity, File directory) {
        if (listener == null) {
            return false;
        }

        Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

        if (captureIntent.resolveActivity(activity.getPackageManager()) == null) {
            listener.onImageCaptureError(new ActivityNotFoundException("No app for ACTION_IMAGE_CAPTURE"));
            return false;
        }

        try {
            this.imageFile = createTempFile(directory);
        } catch (IOException e) {
            listener.onImageCaptureError(e);
            return false;
        }

        Uri imageUri = FileProvider.getUriForFile(activity,activity.getPackageName() + ".fileprovider", this.imageFile);
        captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);

        activity.startActivityForResult(captureIntent, IMAGE_CAPTURE_REQUEST);
        return true;
    }

    public boolean parseActivityResult(ImageCaptureResultListener listener, int requestCode, int resultCode, Intent data) {
        if (requestCode != IMAGE_CAPTURE_REQUEST) {
            return false;
        }

        if (listener == null) {
            return false;
        }

        if (resultCode == Activity.RESULT_OK) {
            listener.onImageCaptured(imageFile);
        } else {
            listener.onImageCaptureError(new RuntimeException("Image capturing was cancelled"));
        }

        return true;
    }
}

编辑

我没有在我的应用程序中使用Bitmaps。我正在拍摄图像,然后将其发送到后端。在理想情况下,我想捕获低质量的图像,然后尽可能将其保存到手机中。如果那不可能,那么我想至少将压缩的图像发送到后端。

android android-camera android-camera-intent mediastore android-image-capture
1个回答
0
投票

当您从意图获得路径时,请使用它。CompressBitMap()。execute(Uri.fromFile(File(mImagePath)))

inner class CompressBitMap : AsyncTask<Uri, Int, File>() {
    override fun doInBackground(vararg p0: Uri?): File? {
        val bitmap: Bitmap?
        val filename = "${Date().time}profile.png"
        val fileDir = File(Environment.getExternalStorageDirectory(), getString(R.string.app_name))
        if (!fileDir.exists()) {
            fileDir.mkdir()
        }

        val destPath = File(fileDir, filename)
        val outPutStream = FileOutputStream(destPath)
        try {
            bitmap = ScaledPicture(p0[0], activity.contentResolver).getBitmap(400, 400)
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, outPutStream)
            outPutStream.flush()
            outPutStream.close()
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return destPath
    }

    override fun onPostExecute(result: File?) {
        super.onPostExecute(result)
        result?.let {
            mImagePath = result.absolutePath
            setProfileImage(mImagePath, image_circle, null)
        }
    }
}

ScaledPicture和ImageScalingUtil是两个用于减小图像大小的重要类。

ScalePicture:=>

import android.content.ContentResolver
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Matrix
import android.graphics.RectF
import android.media.ExifInterface
import android.net.Uri
import com.silverskysoft.skysalon.imageUtils.ImageScalingUtils
import java.io.FileNotFoundException
import java.io.IOException
import java.io.InvalidObjectException


class ScaledPicture(private var uri: Uri?, private var resolver: ContentResolver) {
private var path: String? = null
private var orientation: Matrix? = null
private var storedHeight: Int = 0
private var storedWidth: Int = 0

@Throws(IOException::class)
private fun getInformation(): Boolean {
    /*if (getInformationFromMediaDatabase())
        return true;*/
    return getInformationFromFileSystem()
}

/* Support for file managers and dropbox */
@Throws(IOException::class)
private fun getInformationFromFileSystem(): Boolean {
    path = uri?.path
    if (path == null)
        return false
    val exif = ExifInterface(path.toString())
    val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
            ExifInterface.ORIENTATION_NORMAL)
    this.orientation = Matrix()
    when (orientation) {
        ExifInterface.ORIENTATION_NORMAL -> {
        }
        ExifInterface.ORIENTATION_FLIP_HORIZONTAL -> this.orientation?.setScale(-1f, 1f)
        ExifInterface.ORIENTATION_ROTATE_180 -> this.orientation?.setRotate(180f)
        ExifInterface.ORIENTATION_FLIP_VERTICAL -> this.orientation?.setScale(1f, -1f)
        ExifInterface.ORIENTATION_TRANSPOSE -> {
            this.orientation?.setRotate(90f)
            this.orientation?.postScale(-1f, 1f)
        }
        ExifInterface.ORIENTATION_ROTATE_90 -> this.orientation?.setRotate(90f)
        ExifInterface.ORIENTATION_TRANSVERSE -> {
            this.orientation?.setRotate(-90f)
            this.orientation?.postScale(-1f, 1f)
        }
        ExifInterface.ORIENTATION_ROTATE_270 -> this.orientation?.setRotate(-90f)
    }/* Identity matrix */
    return true
}

@Throws(IOException::class)
private fun getStoredDimensions(): Boolean {
    val input = resolver.openInputStream(uri)
    val options = BitmapFactory.Options()
    options.inJustDecodeBounds = true
    BitmapFactory.decodeStream(resolver.openInputStream(uri), null, options)
    /* The input stream could be reset instead of closed and reopened if it were possible
       to reliably wrap the input stream on a buffered stream, but it's not possible because
       decodeStream() places an upper read limit of 1024 bytes for a reset to be made (it calls
       mark(1024) on the stream). */

    input?.close()
    if (options.outHeight <= 0 || options.outWidth <= 0)
        return false
    storedHeight = options.outHeight
    storedWidth = options.outWidth
    return true
}

@Throws(IOException::class)
fun getBitmap(reqWidth: Int, reqHeight: Int): Bitmap {
    val heightWidth = 1000
    if (!getInformation())
        throw FileNotFoundException()
    if (!getStoredDimensions())
        throw InvalidObjectException(null)
    val rect = RectF(0f, 0f, storedWidth.toFloat(), storedHeight.toFloat())
    orientation?.mapRect(rect)
    var width = rect.width().toInt()
    var height = rect.height().toInt()
    var subSample = 1
    while (width > heightWidth || height > heightWidth) {
        width /= 2
        height /= 2
        subSample *= 2
    }
    if (width == 0 || height == 0)
        throw InvalidObjectException(null)
    val options = BitmapFactory.Options()
    options.inSampleSize = subSample
    val subSampled = BitmapFactory.decodeStream(resolver.openInputStream(uri), null, options)
    val picture: Bitmap
    if (orientation?.isIdentity == false) {
        picture = Bitmap.createBitmap(subSampled, 0, 0, options.outWidth, options.outHeight,
                orientation, false)
        subSampled.recycle()
    } else

        picture = subSampled

    return ImageScalingUtils.decodeBitmap(picture, reqWidth, reqHeight, ImageScalingUtils.ScalingLogic.CROP)
 }

}

ImageScalingUtils:=>

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import java.io.ByteArrayOutputStream

   /**
  * Created by Avinash on 7/8/19.
     * ImageScalingUtils responsible for compressing the bitmap    efficiently
*/
 object ImageScalingUtils {

/**
 * 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 dstWidth Width of destination area
 * @param dstHeight Height of destination area
 * @param scalingLogic Logic to use to avoid image stretching
 * @return Decoded bitmap
 */


fun decodeBitmap(bm: Bitmap, dstWidth: Int, dstHeight: Int,
                 scalingLogic: ScalingLogic): Bitmap {
    val stream = ByteArrayOutputStream()
    bm.compress(Bitmap.CompressFormat.PNG, 100, stream)
    val byteArray = stream.toByteArray()
    val options = BitmapFactory.Options()
    options.inJustDecodeBounds = true
    BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size, options)
    options.inJustDecodeBounds = false
    options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, dstWidth,
            dstHeight, scalingLogic)
    return BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size, options)
}

/**
 * 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.
 */
enum class 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
 */
private fun calculateSampleSize(srcWidth: Int, srcHeight: Int, dstWidth: Int, dstHeight: Int,
                                scalingLogic: ScalingLogic): Int {
    if (scalingLogic == ScalingLogic.FIT) {
        val srcAspect = srcWidth.toFloat() / srcHeight.toFloat()
        val dstAspect = dstWidth.toFloat() / dstHeight.toFloat()

        return if (srcAspect > dstAspect) {
            srcWidth / dstWidth
        } else {
            srcHeight / dstHeight
        }
    } else {
        val srcAspect = srcWidth.toFloat() / srcHeight.toFloat()
        val dstAspect = dstWidth.toFloat() / dstHeight.toFloat()

        return if (srcAspect > dstAspect) {
            srcHeight / dstHeight
        } else {
            srcWidth / dstWidth
        }
    }
  }


}
© www.soinside.com 2019 - 2024. All rights reserved.