11package co.stonephone.stonecamera.utils
22
33import android.content.Context
4- import android.graphics.Bitmap
5- import android.graphics.Matrix
6- import android.os.SystemClock
74import android.util.Log
8- import androidx.camera.core.ImageProxy
9- import com.google.mediapipe.framework.image.BitmapImageBuilder
10- import com.google.mediapipe.framework.image.ByteBufferExtractor
115import com.google.mediapipe.framework.image.MPImage
126import com.google.mediapipe.tasks.core.BaseOptions
137import com.google.mediapipe.tasks.core.Delegate
@@ -18,10 +12,8 @@ import java.nio.ByteBuffer
1812
1913class ImageSegmenterHelper (
2014 var currentDelegate : Int = DELEGATE_CPU ,
21- var runningMode : RunningMode = RunningMode .IMAGE ,
2215 var currentModel : Int = MODEL_DEEPLABV3 ,
2316 val context : Context ,
24- var imageSegmenterListener : SegmenterListener ? = null
2517) {
2618
2719 // For this example this needs to be a var so it can be reset on changes. If the Imagesegmenter
@@ -32,26 +24,6 @@ class ImageSegmenterHelper(
3224 setupImageSegmenter()
3325 }
3426
35- // Segmenter must be closed when creating a new one to avoid returning results to a
36- // non-existent object
37- fun clearImageSegmenter () {
38- imagesegmenter?.close()
39- imagesegmenter = null
40- }
41-
42- fun setListener (listener : SegmenterListener ) {
43- imageSegmenterListener = listener
44- }
45-
46- fun clearListener () {
47- imageSegmenterListener = null
48- }
49-
50- // Return running status of image segmenter helper
51- fun isClosed (): Boolean {
52- return imagesegmenter == null
53- }
54-
5527 // Initialize the image segmenter using current settings on the
5628 // thread that is using it. CPU can be used with detectors
5729 // that are created on the main thread and used on a background thread, but
@@ -83,139 +55,36 @@ class ImageSegmenterHelper(
8355 }
8456 }
8557
86- // if (imageSegmenterListener == null) {
87- // throw IllegalStateException(
88- // "ImageSegmenterListener must be set."
89- // )
90- // }
91-
9258 try {
9359 val baseOptions = baseOptionsBuilder.build()
9460 val optionsBuilder = ImageSegmenter .ImageSegmenterOptions .builder()
95- .setRunningMode(runningMode )
61+ .setRunningMode(RunningMode . IMAGE )
9662 .setBaseOptions(baseOptions)
9763 .setOutputCategoryMask(true )
9864 .setOutputConfidenceMasks(false )
9965
100- if (runningMode == RunningMode .LIVE_STREAM ) {
101- optionsBuilder.setResultListener(this ::returnSegmentationResult)
102- .setErrorListener(this ::returnSegmentationHelperError)
103- }
104-
10566 val options = optionsBuilder.build()
10667 imagesegmenter = ImageSegmenter .createFromOptions(context, options)
10768 } catch (e: IllegalStateException ) {
108- imageSegmenterListener?.onError(
109- " Image segmenter failed to initialize. See error logs for details"
110- )
11169 Log .e(
11270 TAG ,
11371 " Image segmenter failed to load model with error: " + e.message
11472 )
11573 } catch (e: RuntimeException ) {
11674 // This occurs if the model being used does not support GPU
117- imageSegmenterListener?.onError(
118- " Image segmenter failed to initialize. See error logs for " + " details" ,
119- GPU_ERROR
120- )
12175 Log .e(
12276 TAG ,
12377 " Image segmenter failed to load model with error: " + e.message
12478 )
12579 }
12680 }
12781
128- // Runs image segmentation on live streaming cameras frame-by-frame and
129- // returns the results asynchronously to the caller.
130- fun segmentLiveStreamFrame (imageProxy : ImageProxy , isFrontCamera : Boolean ) {
131- if (runningMode != RunningMode .LIVE_STREAM ) {
132- throw IllegalArgumentException (
133- " Attempting to call segmentLiveStreamFrame" + " while not using RunningMode.LIVE_STREAM"
134- )
135- }
136-
137- val frameTime = SystemClock .uptimeMillis()
138- val bitmapBuffer = Bitmap .createBitmap(
139- imageProxy.width, imageProxy.height, Bitmap .Config .ARGB_8888
140- )
141-
142- imageProxy.use {
143- bitmapBuffer.copyPixelsFromBuffer(imageProxy.planes[0 ].buffer)
144- }
145-
146- // Used for rotating the frame image so it matches our models
147- val matrix = Matrix ().apply {
148- postRotate(imageProxy.imageInfo.rotationDegrees.toFloat())
149-
150- if (isFrontCamera) {
151- postScale(
152- - 1f ,
153- 1f ,
154- imageProxy.width.toFloat(),
155- imageProxy.height.toFloat()
156- )
157- }
158- }
159-
160- imageProxy.close()
161-
162- val rotatedBitmap = Bitmap .createBitmap(
163- bitmapBuffer,
164- 0 ,
165- 0 ,
166- bitmapBuffer.width,
167- bitmapBuffer.height,
168- matrix,
169- true
170- )
171-
172- val mpImage = BitmapImageBuilder (rotatedBitmap).build()
173-
174- imagesegmenter?.segmentAsync(mpImage, frameTime)
175- }
176-
17782 // Runs image segmentation on single image and
17883 // returns the results asynchronously to the caller.
179-
18084 fun segmentImageFile (mpImage : MPImage ): ImageSegmenterResult ? {
181- if (runningMode != RunningMode .IMAGE ) {
182- throw IllegalArgumentException (
183- " Attempting to call segmentImageFile" + " while not using RunningMode.IMAGE"
184- )
185- }
18685 return imagesegmenter?.segment(mpImage)
18786 }
18887
189- // MPImage isn't necessary for this example, but the listener requires it
190- private fun returnSegmentationResult (
191- result : ImageSegmenterResult , image : MPImage
192- ) {
193- val finishTimeMs = SystemClock .uptimeMillis()
194-
195- val inferenceTime = finishTimeMs - result.timestampMs()
196-
197- // We only need the first mask for this sample because we are using
198- // the OutputType CATEGORY_MASK, which only provides a single mask.
199- val mpImage = result.categoryMask().get()
200-
201- imageSegmenterListener?.onResults(
202- ResultBundle (
203- ByteBufferExtractor .extract(mpImage),
204- mpImage.width,
205- mpImage.height,
206- inferenceTime
207- )
208- )
209- }
210-
211- // Return errors thrown during segmentation to this
212- // ImageSegmenterHelper's caller
213- private fun returnSegmentationHelperError (error : RuntimeException ) {
214- imageSegmenterListener?.onError(
215- error.message ? : " An unknown error has occurred"
216- )
217- }
218-
21988 // Wraps results from inference, the time it takes for inference to be
22089 // performed.
22190 data class ResultBundle (
@@ -228,8 +97,6 @@ class ImageSegmenterHelper(
22897 companion object {
22998 const val DELEGATE_CPU = 0
23099 const val DELEGATE_GPU = 1
231- const val OTHER_ERROR = 0
232- const val GPU_ERROR = 1
233100
234101 const val MODEL_DEEPLABV3 = 0
235102 const val MODEL_HAIR_SEGMENTER = 1
@@ -243,36 +110,6 @@ class ImageSegmenterHelper(
243110
244111 private const val TAG = " ImageSegmenterHelper"
245112
246- val labelColors = listOf (
247- - 16777216 ,
248- - 8388608 ,
249- - 16744448 ,
250- - 8355840 ,
251- - 16777088 ,
252- - 8388480 ,
253- - 16744320 ,
254- - 8355712 ,
255- - 12582912 ,
256- - 4194304 ,
257- - 12550144 ,
258- - 4161536 ,
259- - 12582784 ,
260- - 4194176 ,
261- - 12550016 ,
262- - 4161408 ,
263- - 16760832 ,
264- - 8372224 ,
265- - 16728064 ,
266- - 8339456 ,
267- - 16760704
268- )
269-
270113 }
271114
272-
273-
274- interface SegmenterListener {
275- fun onError (error : String , errorCode : Int = OTHER_ERROR )
276- fun onResults (resultBundle : ResultBundle )
277- }
278115}
0 commit comments