I am facing a problem where when I try to crop a YUV_420_888
image, it gets messed up on some devices (Samsung S22 Ultra) but not on others (Samsung A14). Here are the sample images:
First Image
Cropped Image
Here is my code that receives frames from camera. I am trying to crop each frame here.
override fun analyzeImageProxy(imageProxy: ImageProxy) {
val mediaImage = imageProxy.image
if (mediaImage != null) {
val bitmap = ImageConvertUtils.getInstance().getUpRightBitmap(InputImage.fromMediaImage(mediaImage, 0))
println(bitmap) // AT THIS POINT, THE IMAGE IS FINE. SEE FIRST IMAGE.
val croppedImageByteArray = cropYUV420(
imageToByteArray(mediaImage),
mediaImage.width,
mediaImage.height,
imageProxy.cropRect.left,
imageProxy.cropRect.top,
imageProxy.cropRect.width(),
imageProxy.cropRect.height()
)
val inputImage = InputImage.fromByteArray(
croppedImageByteArray,
imageProxy.cropRect.width(),
imageProxy.cropRect.height(),
imageProxy.imageInfo.rotationDegrees,
InputImage.IMAGE_FORMAT_NV21
)
val bitmap2 = ImageConvertUtils.getInstance().getUpRightBitmap(inputImage)
println(bitmap2) // AT THIS POINT, THE IMAGE IS MESSED UP. SEE CROPPED IMAGE.
imageScanner.scanFrame(imageProxy, inputImage)
}
}
Following is the code that is being used for cropping. I don’t understand it completely, I just got it from some other answer here on SO.
private fun cropYUV420(
yuvData: ByteArray,
imageWidth: Int,
imageHeight: Int,
cropX: Int,
cropY: Int,
cropWidth: Int,
cropHeight: Int
): ByteArray {
val croppedYUV = ByteArray(cropWidth * cropHeight * 3 / 2)
// Crop the Y plane
for (row in 0 until cropHeight) {
val srcPos = (cropY + row) * imageWidth + cropX
val destPos = row * cropWidth
System.arraycopy(yuvData, srcPos, croppedYUV, destPos, cropWidth)
}
// Calculate the starting point for the UV planes
val uvHeight = imageHeight / 2
val uvCropHeight = cropHeight / 2
val uvImageWidth = imageWidth / 2
val uvCropWidth = cropWidth / 2
val uStart = imageWidth * imageHeight
val vStart = uStart + (uvHeight * uvImageWidth)
// Crop the U plane
for (row in 0 until uvCropHeight) {
val srcPos = uStart + (cropY / 2 + row) * uvImageWidth + cropX / 2
val destPos = cropWidth * cropHeight + row * uvCropWidth
System.arraycopy(yuvData, srcPos, croppedYUV, destPos, uvCropWidth)
}
// Crop the V plane
for (row in 0 until uvCropHeight) {
val srcPos = vStart + (cropY / 2 + row) * uvImageWidth + cropX / 2
val destPos = cropWidth * cropHeight + uvCropHeight * uvCropWidth + row * uvCropWidth
System.arraycopy(yuvData, srcPos, croppedYUV, destPos, uvCropWidth)
}
return croppedYUV
}
private fun imageToByteArray(image: Image): ByteArray {
val yBuffer = image.planes[0].buffer // Y
val uBuffer = image.planes[1].buffer // U
val vBuffer = image.planes[2].buffer // V
val ySize = yBuffer.remaining()
val uSize = uBuffer.remaining()
val vSize = vBuffer.remaining()
val nv21 = ByteArray(ySize + uSize + vSize)
// U and V are swapped in the YUV 420 format
yBuffer.get(nv21, 0, ySize)
vBuffer.get(nv21, ySize, vSize)
uBuffer.get(nv21, ySize + vSize, uSize)
return nv21
}
Any help or guidance will be highly appreciated.