在工作中需要将摄像头的摄像头的视频流数据转为YUV
格式,供算法去处理图像,为了方便调试就将每帧数据又转成了Bitmap
去显示,效果如下:
起初,看到画面正常显示后,就没太在意图像的内容,直到看见蓝色瓶的可口可乐(实际是红色的),事情好像不太对,后续发现所有红色的东西在画面中都变成了蓝色,然后算法识别的结果也不太好,才发现了问题的严重性。
Bitmap
转换问题?好像不是,yuv
数据有问题?是直接从摄像头视频流解码出来的啊!总不能是摄像头有问题吧!看了摄像头预览效果正常的。头大,直到在网上看到:
UV排列反了。
比如说,NV21和YUV420SP的Y排列相同,UV则相反。给你YUV420SP,你当作NV21保存JPG,就会 发生红蓝颠倒。
抱着试试的态度,那就把UV转换以下看看把,原神,启动!
正常了,好神奇,哈哈哈。
就简单记下吧,部分代码如下,从解码到转Bitmap
。
fun decode(data: ByteArray, offset: Int, length: Int) {
val inputBuffers = codec?.inputBuffers
val outputBuffers = codec?.outputBuffers
val inputBufferIndex = codec?.dequeueInputBuffer(10000) ?: -1
if (inputBufferIndex >= 0) {
val inputBuffer = inputBuffers?.get(inputBufferIndex)
inputBuffer?.clear()
inputBuffer?.put(data, offset, length)
codec?.queueInputBuffer(inputBufferIndex, 0, length, 0, 0)
}
val bufferInfo = MediaCodec.BufferInfo()
var outputBufferIndex = codec?.dequeueOutputBuffer(bufferInfo, 10000) ?: -1
while (outputBufferIndex >= 0) {
val outputBuffer = outputBuffers?.get(outputBufferIndex)
// 处理解码后的YUV数据
processYUVData(outputBuffer, bufferInfo)
codec?.releaseOutputBuffer(outputBufferIndex, false)
outputBufferIndex = codec?.dequeueOutputBuffer(bufferInfo, 0) ?: -1
}
}
private fun processYUVData(outputBuffer: ByteBuffer?, bufferInfo: MediaCodec.BufferInfo) {
val yuvData = ByteArray(bufferInfo.size)
outputBuffer?.get(yuvData)
bitmapCallback?.invoke(yuvToBitmap(yuvData, 640, 352))
}
private fun yuvToBitmap(yuvData: ByteArray, width: Int, height: Int): Bitmap? {
nv21ToYuv420sp(width, height, yuvData)//解决
val yuvImage = YuvImage(yuvData, ImageFormat.NV21, width, height, null)
val out = ByteArrayOutputStream()
yuvImage.compressToJpeg(android.graphics.Rect(0, 0, width, height), 100, out)
val imageBytes = out.toByteArray()
return BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
}
/**
* NV21转YUV420SP 解决红蓝色颠倒的问题
*/
private fun nv21ToYuv420sp(width: Int, height: Int, inArray: ByteArray) {
val pixels = width * height
val count = pixels / 2
for (i in 0 until count step 2) {
val s = inArray[pixels + i]
inArray[pixels + i] = inArray[pixels + i + 1]
inArray[pixels + i + 1] = s
}
}
当然,如果你要使用正确的yuvData
的话,就要在返回数据之前调用nv21ToYuv420sp
方法提前转化以下。