如何解决Android CameraX YuvToRgbConverter 人工制品
我正在尝试将 CameraX 实施到 APP 中。到目前为止,我已经设法做到了这一点,但我有两个问题。
- 我使用了来自 this 官方存储库的 YuvToRgbConverter,但是,我得到了带有一些工件的图像
- 您在右下角看到的处理后图像的方向与预览中的方向不同。
在 ImageAnalysis
上,我尝试放置不同的 .setTargetrotation
,但无论我如何更改,结果都是一样的。
val imageAnalyzer = ImageAnalysis.Builder()
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.setTargetRotation(Surface.ROTATION_0)
.build()
任何帮助将不胜感激!
处理转换的代码如下所示:
class YuvToRgbConverterServiceImpl @Inject constructor(
@ApplicationContext val context: Context
) : YuvToRgbConverterService {
private val rs = RenderScript.create(context)
private val scriptYuvToRgb = ScriptIntrinsicYuvToRGB.create(rs,Element.U8_4(rs))
private var pixelCount: Int = -1
private lateinit var yuvBuffer: ByteBuffer
private lateinit var inputAllocation: Allocation
private lateinit var outputAllocation: Allocation
override fun convertYuvToRgb(image: Image): Bitmap {
val bitmap = Bitmap.createBitmap(image.width,image.height,Bitmap.Config.RGB_565)
yuvToRgb(image,bitmap)
return bitmap
}
@Synchronized
private fun yuvToRgb(image: Image,output: Bitmap) {
// Ensure that the intermediate output byte buffer is allocated
if (!::yuvBuffer.isInitialized) {
pixelCount = image.cropRect.width() * image.cropRect.height()
// Bits per pixel is an average for the whole image,so it's useful to compute the size
// of the full buffer but should not be used to determine pixel offsets
val pixelSizeBits = ImageFormat.getBitsPerPixel(ImageFormat.YUV_420_888)
yuvBuffer = ByteBuffer.allocateDirect(pixelCount * pixelSizeBits / 8)
}
// Rewind the buffer; no need to clear it since it will be filled
yuvBuffer.rewind()
// Get the YUV data in byte array form using NV21 format
imagetoByteBuffer(image,yuvBuffer.array())
// Ensure that the RenderScript inputs and outputs are allocated
if (!::inputAllocation.isInitialized) {
// Explicitly create an element with type NV21,since that's the pixel format we use
val elemType = Type.Builder(rs,Element.YUV(rs)).setYuvFormat(ImageFormat.NV21).create()
inputAllocation = Allocation.createSized(rs,elemType.element,yuvBuffer.array().size)
}
if (!::outputAllocation.isInitialized) {
outputAllocation = Allocation.createFromBitmap(rs,output)
}
// Convert NV21 format YUV to RGB
inputAllocation.copyFrom(yuvBuffer.array())
scriptYuvToRgb.setInput(inputAllocation)
scriptYuvToRgb.forEach(outputAllocation)
outputAllocation.copyTo(output)
}
private fun imagetoByteBuffer(image: Image,outputBuffer: ByteArray) {
assert(image.format == ImageFormat.YUV_420_888)
val imageCrop = image.cropRect
val imagePlanes = image.planes
imagePlanes.forEachIndexed { planeIndex,plane ->
// How many values are read in input for each output value written
// Only the Y plane has a value for every pixel,U and V have half the resolution i.e.
//
// Y Plane U Plane V Plane
// =============== ======= =======
// Y Y Y Y Y Y Y Y U U U U V V V V
// Y Y Y Y Y Y Y Y U U U U V V V V
// Y Y Y Y Y Y Y Y U U U U V V V V
// Y Y Y Y Y Y Y Y U U U U V V V V
// Y Y Y Y Y Y Y Y
// Y Y Y Y Y Y Y Y
// Y Y Y Y Y Y Y Y
val outputStride: Int
// The index in the output buffer the next value will be written at
// For Y it's zero,for U and V we start at the end of Y and interleave them i.e.
//
// First chunk Second chunk
// =============== ===============
// Y Y Y Y Y Y Y Y V U V U V U V U
// Y Y Y Y Y Y Y Y V U V U V U V U
// Y Y Y Y Y Y Y Y V U V U V U V U
// Y Y Y Y Y Y Y Y V U V U V U V U
// Y Y Y Y Y Y Y Y
// Y Y Y Y Y Y Y Y
// Y Y Y Y Y Y Y Y
var outputOffset: Int
when (planeIndex) {
0 -> {
outputStride = 1
outputOffset = 0
}
1 -> {
outputStride = 2
// For NV21 format,U is in odd-numbered indices
outputOffset = pixelCount + 1
}
2 -> {
outputStride = 2
// For NV21 format,V is in even-numbered indices
outputOffset = pixelCount
}
else -> {
// Image contains more than 3 planes,something strange is going on
return@forEachIndexed
}
}
val planeBuffer = plane.buffer
val rowStride = plane.rowStride
val pixelStride = plane.pixelStride
// We have to divide the width and height by two if it's not the Y plane
val planeCrop = if (planeIndex == 0) {
imageCrop
} else {
Rect(
imageCrop.left / 2,imageCrop.top / 2,imageCrop.right / 2,imageCrop.bottom / 2
)
}
val planeWidth = planeCrop.width()
val planeHeight = planeCrop.height()
// Intermediate buffer used to store the bytes of each row
val rowBuffer = ByteArray(plane.rowStride)
// Size of each row in bytes
val rowLength = if (pixelStride == 1 && outputStride == 1) {
planeWidth
} else {
// Take into account that the stride may include data from pixels other than this
// particular plane and row,and that Could be between pixels and not after every
// pixel:
//
// |---- Pixel stride ----| Row ends here --> |
// | Pixel 1 | Other Data | Pixel 2 | Other Data | ... | Pixel N |
//
// We need to get (N-1) * (pixel stride bytes) per row + 1 byte for the last pixel
(planeWidth - 1) * pixelStride + 1
}
for (row in 0 until planeHeight) {
// Move buffer position to the beginning of this row
planeBuffer.position(
(row + planeCrop.top) * rowStride + planeCrop.left * pixelStride)
if (pixelStride == 1 && outputStride == 1) {
// When there is a single stride value for pixel and output,we can just copy
// the entire row in a single step
planeBuffer.get(outputBuffer,outputOffset,rowLength)
outputOffset += rowLength
} else {
// When either pixel or output have a stride > 1 we must copy pixel by pixel
planeBuffer.get(rowBuffer,rowLength)
for (col in 0 until planeWidth) {
outputBuffer[outputOffset] = rowBuffer[col * pixelStride]
outputOffset += outputStride
}
}
}
}
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。