我正在使用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
。我正在拍摄图像,然后将其发送到后端。在理想情况下,我想捕获低质量的图像,然后尽可能将其保存到手机中。如果那不可能,那么我想至少将压缩的图像发送到后端。
当您从意图获得路径时,请使用它。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
}
}
}
}