完成多平台拍照软件

This commit is contained in:
BBIT-Kai
2025-08-13 09:17:51 +08:00
parent 02262e3a96
commit 1aa67280fe
24 changed files with 845 additions and 265 deletions
@@ -0,0 +1,123 @@
package com.bbitcn.bbit_ai.plantform.CommonInterface
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asSkiaBitmap
import androidx.compose.ui.graphics.toComposeImageBitmap
import com.bbitcn.bbit_ai.model.SaveData
import com.bbitcn.bbit_ai.utils.DataFileUtil
import com.bbitcn.bbit_ai.utils.MyUtils
import kotlinx.coroutines.flow.MutableStateFlow
import org.bytedeco.javacv.Java2DFrameConverter
import org.bytedeco.javacv.OpenCVFrameGrabber
import org.bytedeco.opencv.opencv_videoio.VideoCapture
import org.jetbrains.skia.Image
import java.awt.image.BufferedImage
import java.io.ByteArrayOutputStream
import java.io.File
import javax.imageio.ImageIO
import kotlin.uuid.ExperimentalUuidApi
import kotlin.uuid.Uuid
actual class MyCamera {
val frameFlow = MutableStateFlow<ImageBitmap?>(null)
private var running = false
private var curCameraId = 0
actual suspend fun startCamera(index: Int) {
curCameraId = index
running = false
val grabber = OpenCVFrameGrabber(index)
try {
grabber.start()
val converter = Java2DFrameConverter()
running = true
while (running) {
val frame = grabber.grab() ?: continue
val image = converter.getBufferedImage(frame) ?: continue
frameFlow.value = image.toComposeImageBitmap()
}
} catch (e: Exception) {
println("Camera grab error: ${e.message}")
} finally {
grabber.stop()
grabber.release()
}
}
actual fun stopCamera() {
running = false
}
private fun BufferedImage.toComposeImageBitmap(): ImageBitmap {
val bytes = ByteArrayOutputStream().use {
ImageIO.write(this, "png", it)
it.toByteArray()
}
val bitmap = Image.makeFromEncoded(bytes).toComposeImageBitmap()
return bitmap
}
actual fun listAvailableCameras(): List<String> {
val availableCameras = mutableListOf<String>()
for (i in 0..3) {
val capture = VideoCapture(i)
if (capture.isOpened) {
availableCameras.add("Camera $i")
capture.release()
}
}
return availableCameras
}
actual suspend fun takePhoto(): ImageBitmap? {
return frameFlow.value
}
@OptIn(ExperimentalUuidApi::class)
actual fun saveImageBitmapToFile(image: ImageBitmap) {
val bufferedImage = image.toBufferedImage()
val imageName = "photo_${System.currentTimeMillis()}.png"
val uuid = Uuid.random().toString()
DataFileUtil.addData(
SaveData(
Uuid.random().toString(),
imageName,
MyUtils.getCurrentTime(),
DataFileUtil.rawImage.absolutePath + File.separator + imageName,
DataFileUtil.aiImage.absolutePath + File.separator + imageName,
uuid + "额外信息"
)
)
ImageIO.write(
bufferedImage,
"png",
File(DataFileUtil.rawImage, imageName)
)
ImageIO.write(
bufferedImage,
"png",
File(DataFileUtil.aiImage, imageName)
)
}
actual fun loadImage(filePath: String): ImageBitmap? {
return try {
val bytes = File(filePath).readBytes()
val skiaImage = Image.makeFromEncoded(bytes)
skiaImage.toComposeImageBitmap()
} catch (e: Exception) {
println("Image decode error: ${e.message}")
null
}
}
fun ImageBitmap.toBufferedImage(): BufferedImage {
val skiaImage = Image.makeFromBitmap(this.asSkiaBitmap())
val bytes = skiaImage.encodeToData()?.bytes ?: throw Exception("Encode failed")
return ImageIO.read(bytes.inputStream())
}
}
actual fun getMyCamera(): MyCamera {
return MyCamera()
}
@@ -0,0 +1,40 @@
package com.bbitcn.bbit_ai.ui.mainFunction
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import com.bbitcn.bbit_ai.M
import com.bbitcn.bbit_ai.plantform.CommonInterface.MyCamera
@Composable
actual fun CameraPreview(
modifier: Modifier,
controller: MyCamera
) {
val imageBitmap = controller.frameFlow.collectAsState()
// 显示图像
Box(
modifier = modifier
.border(
width = 1.dp,
color = androidx.compose.material3.MaterialTheme.colorScheme.primary
)
.fillMaxSize()
) {
imageBitmap.value?.let {
Image(
modifier = M.fillMaxSize(),
bitmap = it,
contentDescription = null,
contentScale = ContentScale.FillHeight
)
}
}
}
@@ -1,7 +0,0 @@
package com.bbitcn.bbit_ai
class JVMPlatform: Platform {
override val name: String = "Java ${System.getProperty("java.version")}"
}
actual fun getPlatform(): Platform = JVMPlatform()