改一个版

This commit is contained in:
2026-05-13 20:50:55 +08:00
parent 7998428587
commit 1170e092f7
8 changed files with 343 additions and 124 deletions

View File

@@ -1,63 +1,72 @@
package com.example.studentfaceregistry
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.ImageFormat
import android.graphics.Matrix
import android.graphics.Rect
import android.graphics.YuvImage
import android.media.Image
import androidx.camera.core.ExperimentalGetImage
import androidx.camera.core.ImageProxy
import java.io.ByteArrayOutputStream
import kotlin.math.min
@ExperimentalGetImage
object ImageUtils {
fun imageProxyToBitmap(imageProxy: ImageProxy): Bitmap {
val image = imageProxy.image ?: error("ImageProxy does not contain an image.")
val nv21 = yuv420ToNv21(image)
val yuvImage = YuvImage(nv21, ImageFormat.NV21, image.width, image.height, null)
val output = ByteArrayOutputStream()
yuvImage.compressToJpeg(Rect(0, 0, image.width, image.height), 90, output)
val bitmap = BitmapFactory.decodeByteArray(output.toByteArray(), 0, output.size())
val bitmap = yuv420ToBitmap(image)
if (imageProxy.imageInfo.rotationDegrees == 0) {
return bitmap
}
val matrix = Matrix().apply {
postRotate(imageProxy.imageInfo.rotationDegrees.toFloat())
}
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
}
private fun yuv420ToNv21(image: Image): ByteArray {
private fun yuv420ToBitmap(image: Image): Bitmap {
val width = image.width
val height = image.height
val ySize = width * height
val chromaSize = width * height / 4
val nv21 = ByteArray(ySize + chromaSize * 2)
copyPlane(image.planes[0], width, height, nv21, 0, 1)
copyPlane(image.planes[2], width / 2, height / 2, nv21, ySize, 2)
copyPlane(image.planes[1], width / 2, height / 2, nv21, ySize + 1, 2)
return nv21
}
val yPlane = image.planes[0]
val uPlane = image.planes[1]
val vPlane = image.planes[2]
private fun copyPlane(
plane: Image.Plane,
width: Int,
height: Int,
output: ByteArray,
offset: Int,
outputPixelStride: Int
) {
val buffer = plane.buffer
val row = ByteArray(plane.rowStride)
var outputIndex = offset
for (rowIndex in 0 until height) {
val bytesToRead = min(plane.rowStride, buffer.remaining())
buffer.get(row, 0, bytesToRead)
for (colIndex in 0 until width) {
val inputIndex = colIndex * plane.pixelStride
if (inputIndex < bytesToRead && outputIndex < output.size) {
output[outputIndex] = row[inputIndex]
}
outputIndex += outputPixelStride
val yBuffer = yPlane.buffer
val uBuffer = uPlane.buffer
val vBuffer = vPlane.buffer
val pixels = IntArray(width * height)
val yRowStride = yPlane.rowStride
val yPixelStride = yPlane.pixelStride
val uRowStride = uPlane.rowStride
val uPixelStride = uPlane.pixelStride
val vRowStride = vPlane.rowStride
val vPixelStride = vPlane.pixelStride
var pixelIndex = 0
for (row in 0 until height) {
val yRow = row * yRowStride
val uvRow = (row shr 1) * uRowStride
val vvRow = (row shr 1) * vRowStride
for (col in 0 until width) {
val y = (yBuffer.get(yRow + col * yPixelStride).toInt() and 0xFF) - 16
val u = (uBuffer.get(uvRow + (col shr 1) * uPixelStride).toInt() and 0xFF) - 128
val v = (vBuffer.get(vvRow + (col shr 1) * vPixelStride).toInt() and 0xFF) - 128
val y1192 = 1192 * y.coerceAtLeast(0)
var r = y1192 + 1634 * v
var g = y1192 - 833 * v - 400 * u
var b = y1192 + 2066 * u
r = r.coerceIn(0, 262143)
g = g.coerceIn(0, 262143)
b = b.coerceIn(0, 262143)
pixels[pixelIndex++] = -0x1000000 or
((r shl 6) and 0x00FF0000) or
((g shr 2) and 0x0000FF00) or
((b shr 10) and 0x000000FF)
}
}
return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).apply {
setPixels(pixels, 0, width, 0, 0, width, height)
}
}
}