完成多平台拍照软件
This commit is contained in:
+123
@@ -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()
|
||||
}
|
||||
+40
@@ -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()
|
||||
Reference in New Issue
Block a user