gpt4 book ai didi

Memory Leaking Issue in Android Kotlin While Converting Bitmap Image(转换位图图像时Android Kotlin内存泄漏问题)

转载 作者:bug小助手 更新时间:2023-10-22 16:11:33 36 4
gpt4 key购买 nike



I have faced a memory leaking issue in android. I have checked profiler, it shows the native memory increasing at a high rate over time.

我在安卓系统中遇到了内存泄漏问题。我检查了profiler,它显示本机内存随着时间的推移以高速增长。


After checking a little bit time I found I am converting a proxyImage to bitmap image and those bitmap image doesn't destroy properly. The following code snippet I used to convert proxyImage to Bitmap image. The reason behind the converting proxyImage to bitmap image is that I want to show the converted image on the jetpack canvas. Please help me to get rid of this stuff or show me an efficient way so that I can show the imageProxy on canvas in jetpack compose. Thanks in advance.

经过一点时间的检查,我发现我正在将proxyImage转换为位图图像,而这些位图图像没有正确销毁。以下代码片段用于将proxyImage转换为位图图像。将proxyImage转换为位图图像的原因是我想在jetpack画布上显示转换后的图像。请帮我去掉这些东西,或者向我展示一种有效的方法,这样我就可以在jetpack compose中在画布上显示imageProxy。提前谢谢。


/**
* Utils functions for bitmap conversions.
*/
object BitmapUtils {
/**
* Converts NV21 format byte buffer to bitmap.
*/
fun getBitmap(data: ByteBuffer, metadata: FrameMetadata, isFlip: Boolean): Bitmap? {
data.rewind()
val imageInBuffer = ByteArray(data.limit())
data[imageInBuffer, 0, imageInBuffer.size]
try {
val image = YuvImage(
imageInBuffer, ImageFormat.NV21, metadata.width, metadata.height, null
)
val stream = ByteArrayOutputStream()
image.compressToJpeg(Rect(0, 0, metadata.width, metadata.height), 80, stream)
val bmp = BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size())
stream.close()
return rotateBitmap(bmp, metadata.rotation, flipX = isFlip, flipY = false)
} catch (e: Exception) {
Log.e("VisionProcessorBase", "Error: " + e.message)
}
return null
}

/**
* Converts a YUV_420_888 image from CameraX API to a bitmap.
*/
@ExperimentalGetImage
fun getBitmap(image: ImageProxy, isFlip: Boolean): Bitmap? {
val frameMetadata: FrameMetadata = FrameMetadata.Builder()
.setWidth(image.width)
.setHeight(image.height)
.setRotation(image.imageInfo.rotationDegrees)
.build()
val nv21Buffer = yuv420ThreePlanesToNV21(
image.image!!.planes, image.width, image.height
)
return getBitmap(nv21Buffer, frameMetadata, isFlip)
}

/**
* Rotates a bitmap if it is converted from a bytebuffer.
*/
private fun rotateBitmap(
bitmap: Bitmap,
rotationDegrees: Int,
flipX: Boolean,
flipY: Boolean
): Bitmap {
val matrix = Matrix()

// Rotate the image back to straight.
matrix.postRotate(rotationDegrees.toFloat())

// Mirror the image along the X or Y axis.
matrix.postScale(if (flipX) -1.0f else 1.0f, if (flipY) -1.0f else 1.0f)
val rotatedBitmap =
Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)

// Recycle the old bitmap if it has changed.
if (rotatedBitmap != bitmap) {
bitmap.recycle()
}
return rotatedBitmap
}

/**
* Converts YUV_420_888 to NV21 bytebuffer.
*
*
* The NV21 format consists of a single byte array containing the Y, U and V values. For an
* image of size S, the first S positions of the array contain all the Y values. The remaining
* positions contain interleaved V and U values. U and V are subSampled by a factor of 2 in both
* dimensions, so there are S/4 U values and S/4 V values. In summary, the NV21 array will contain
* S Y values followed by S/4 VU values: YYYYYYYYYYYYYY(...)YVUVUVUVU(...)VU
*
*
* YUV_420_888 is a generic format that can describe any YUV image where U and V are subSampled
* by a factor of 2 in both dimensions. [Image.getPlanes returns an array with the Y, U and
* V planes. The Y plane is guaranteed not to be interleaved, so we can just copy its values into
* the first part of the NV21 array. The U and V planes may already have the representation in the
* NV21 format. This happens if the planes share the same buffer, the V buffer is one position
* before the U buffer and the planes have a pixelStride of 2. If this is case, we can just copy
* them to the NV21 array.
*/
private fun yuv420ThreePlanesToNV21(
yuv420888planes: Array<Plane>, width: Int, height: Int
): ByteBuffer {
val imageSize = width * height
val out = ByteArray(imageSize + 2 * (imageSize / 4))
if (areUVPlanesNV21(yuv420888planes, width, height)) {
// Copy the Y values.
yuv420888planes[0].buffer[out, 0, imageSize]
val uBuffer = yuv420888planes[1].buffer
val vBuffer = yuv420888planes[2].buffer
// Get the first V value from the V buffer, since the U buffer does not contain it.
vBuffer[out, imageSize, 1]
// Copy the first U value and the remaining VU values from the U buffer.
uBuffer[out, imageSize + 1, 2 * imageSize / 4 - 1]
} else {
// Fallback to copying the UV values one by one, which is slower but also works.
// Unpack Y.
unpackPlane(
yuv420888planes[0], width, height, out, 0, 1
)
// Unpack U.
unpackPlane(
yuv420888planes[1], width, height, out, imageSize + 1, 2
)
// Unpack V.
unpackPlane(
yuv420888planes[2], width, height, out, imageSize, 2
)
}
return ByteBuffer.wrap(out)
}

/**
* Checks if the UV plane buffers of a YUV_420_888 image are in the NV21 format.
*/
private fun areUVPlanesNV21(planes: Array<Plane>, width: Int, height: Int): Boolean {
val imageSize = width * height
val uBuffer = planes[1].buffer
val vBuffer = planes[2].buffer

// Backup buffer properties.
val vBufferPosition = vBuffer.position()
val uBufferLimit = uBuffer.limit()

// Advance the V buffer by 1 byte, since the U buffer will not contain the first V value.
vBuffer.position(vBufferPosition + 1)
// Chop off the last byte of the U buffer, since the V buffer will not contain the last U value.
uBuffer.limit(uBufferLimit - 1)

// Check that the buffers are equal and have the expected number of elements.
val areNV21 =
vBuffer.remaining() == 2 * imageSize / 4 - 2 && vBuffer.compareTo(uBuffer) == 0

// Restore buffers to their initial state.
vBuffer.position(vBufferPosition)
uBuffer.limit(uBufferLimit)
return areNV21
}

/**
* Unpack an image plane into a byte array.
*
*
* The input plane data will be copied in 'out', starting at 'offset' and every pixel will be
* spaced by 'pixelStride'. Note that there is no row padding on the output.
*/
private fun unpackPlane(
plane: Plane, width: Int, height: Int, out: ByteArray, offset: Int, pixelStride: Int
) {
val buffer = plane.buffer
buffer.rewind()

// Compute the size of the current plane.
// We assume that it has the aspect ratio as the original image.
val numRow = (buffer.limit() + plane.rowStride - 1) / plane.rowStride
if (numRow == 0) {
return
}
val scaleFactor = height / numRow
val numCol = width / scaleFactor

// Extract the data in the output buffer.
var outputPos = offset
var rowStart = 0
for (row in 0 until numRow) {
var inputPos = rowStart
for (col in 0 until numCol) {
out[outputPos] = buffer[inputPos]
outputPos += pixelStride
inputPos += plane.pixelStride
}
rowStart += plane.rowStride
}
}
}

Edited: I have used bitmap?.asImageBitmap() to convert ImageBitmap type. It may cause the memory leaking. But what will be the overall solution?

编辑:我用过位图?。asImageBitmap()转换ImageBitmap类型。这可能会导致内存泄漏。但总体解决方案是什么?


更多回答
优秀答案推荐
更多回答

36 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com