summaryrefslogtreecommitdiff
path: root/tflite/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'tflite/src/main/java')
-rw-r--r--tflite/src/main/java/com/example/android/camerax/tflite/CameraActivity.kt281
1 files changed, 281 insertions, 0 deletions
diff --git a/tflite/src/main/java/com/example/android/camerax/tflite/CameraActivity.kt b/tflite/src/main/java/com/example/android/camerax/tflite/CameraActivity.kt
new file mode 100644
index 0000000..cde9778
--- /dev/null
+++ b/tflite/src/main/java/com/example/android/camerax/tflite/CameraActivity.kt
@@ -0,0 +1,281 @@
1/*
2 * Copyright 2020 Google LLC
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.example.android.camerax.tflite
18
19import android.Manifest
20import android.annotation.SuppressLint
21import android.content.Context
22import android.content.pm.PackageManager
23import android.graphics.Bitmap
24import android.graphics.Color
25import android.graphics.Matrix
26import android.graphics.RectF
27import android.os.Bundle
28import android.util.Log
29import android.util.Size
30import android.view.View
31import android.view.ViewGroup
32import androidx.appcompat.app.AppCompatActivity
33import androidx.camera.core.AspectRatio
34import androidx.camera.core.CameraSelector
35import androidx.camera.core.ImageAnalysis
36import androidx.camera.core.Preview
37import androidx.camera.lifecycle.ProcessCameraProvider
38import androidx.core.app.ActivityCompat
39import androidx.core.content.ContextCompat
40import androidx.lifecycle.LifecycleOwner
41import com.android.example.camerax.tflite.databinding.ActivityCameraBinding
42import java.net.DatagramPacket
43import java.net.DatagramSocket
44import java.net.InetSocketAddress
45import java.util.concurrent.Executors
46import java.util.concurrent.TimeUnit
47import kotlin.random.Random
48
49
50/** Activity that displays the camera and performs object detection on the incoming frames */
51class CameraActivity : AppCompatActivity() {
52
53 private lateinit var activityCameraBinding: ActivityCameraBinding
54
55 private lateinit var bitmapBuffer: Bitmap
56
57 private val executor = Executors.newSingleThreadExecutor()
58 private val permissions = listOf(Manifest.permission.CAMERA)
59 private val permissionsRequestCode = Random.nextInt(0, 10000)
60
61 private var lensFacing: Int = CameraSelector.LENS_FACING_BACK
62 private val isFrontFacing get() = lensFacing == CameraSelector.LENS_FACING_FRONT
63
64 private var pauseAnalysis = false
65 private var imageRotationDegrees: Int = 0
66 private var socket: DatagramSocket = DatagramSocket()
67
68 override fun onCreate(savedInstanceState: Bundle?) {
69 super.onCreate(savedInstanceState)
70 activityCameraBinding = ActivityCameraBinding.inflate(layoutInflater)
71 setContentView(activityCameraBinding.root)
72 }
73
74 override fun onDestroy() {
75
76 // Terminate all outstanding analyzing jobs (if there is any).
77 executor.apply {
78 shutdown()
79 awaitTermination(1000, TimeUnit.MILLISECONDS)
80 }
81 super.onDestroy()
82 }
83
84 /** Declare and bind preview and analysis use cases */
85 @SuppressLint("UnsafeExperimentalUsageError")
86 private fun bindCameraUseCases() = activityCameraBinding.viewFinder.post {
87
88 val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
89 cameraProviderFuture.addListener ({
90
91 // Camera provider is now guaranteed to be available
92 val cameraProvider = cameraProviderFuture.get()
93
94 // Set up the view finder use case to display camera preview
95 val preview = Preview.Builder()
96 .setTargetAspectRatio(AspectRatio.RATIO_4_3)
97 .setTargetRotation(activityCameraBinding.viewFinder.display.rotation)
98 .build()
99
100 // Set up the image analysis use case which will process frames in real time
101 val imageAnalysis = ImageAnalysis.Builder()
102 .setTargetResolution(Size(448, 236))
103 .setTargetRotation(activityCameraBinding.viewFinder.display.rotation)
104 .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
105 .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
106 .build()
107
108 var frameCounter = 0
109 var lastFpsTimestamp = System.currentTimeMillis()
110
111 imageAnalysis.setAnalyzer(executor, ImageAnalysis.Analyzer { image ->
112 if (!::bitmapBuffer.isInitialized) {
113 // The image rotation and RGB image buffer are initialized only once
114 // the analyzer has started running
115 imageRotationDegrees = image.imageInfo.rotationDegrees
116 bitmapBuffer = Bitmap.createBitmap(
117 image.width, image.height, Bitmap.Config.ARGB_8888)
118 }
119
120 // Early exit: image analysis is in paused state
121 if (pauseAnalysis) {
122 image.close()
123 return@Analyzer
124 }
125
126 // Copy out RGB bits to our shared buffer
127 image.use { bitmapBuffer.copyPixelsFromBuffer(image.planes[0].buffer) }
128
129// val CAM_WIDTH = 858
130// val CAM_HEIGHT = 480
131 val CAM_WIDTH = 480
132 val CAM_HEIGHT = 360
133
134 val DISPLAY_WIDTH = 448
135 val DISPLAY_HEIGHT = 160
136 val DISPLAY_VHEIGHT = 236
137
138 val scaledBitmap = Bitmap.createScaledBitmap(bitmapBuffer, DISPLAY_WIDTH, DISPLAY_VHEIGHT, true)
139
140 val scratch = IntArray((2 + DISPLAY_VHEIGHT) * DISPLAY_WIDTH)
141 val output = UByteArray(10 + DISPLAY_HEIGHT * DISPLAY_WIDTH / 8)
142 output[1] = 0x12u
143 output[4] = 0x23u
144 var offset = 10
145/*
146 for ( row in 0..DISPLAY_VHEIGHT - 1) {
147 val zeile = (row * bitmapBuffer.height ) / DISPLAY_VHEIGHT
148 for (column in 0..DISPLAY_WIDTH - 1) {
149 val spalte = (column * bitmapBuffer.width ) / DISPLAY_WIDTH
150 val pixel = bitmapBuffer.getPixel(spalte, zeile)
151 scratch[row * DISPLAY_WIDTH + column] = Color.red(pixel) * 19535 + Color.green(pixel) * 38470 + Color.blue(pixel) * 7448
152 }
153 }
154*/
155 scaledBitmap.getPixels(scratch, 0, DISPLAY_WIDTH, 0, 0, DISPLAY_WIDTH, DISPLAY_VHEIGHT)
156 for (off in 0..DISPLAY_VHEIGHT * DISPLAY_WIDTH)
157 scratch[off] = Color.red(scratch[off]) * 19535 + Color.green(scratch[off]) * 38470 + Color.blue(scratch[off]) * 7448
158
159 var acc = 0
160 var accv = 0
161
162 for ( row in 0.. DISPLAY_VHEIGHT - 1)
163 for ( column in 0..DISPLAY_WIDTH - 1) {
164 val pixel = scratch[row * DISPLAY_WIDTH + column]
165 val bwpixel = if (pixel < 0x810000) 0 else 0xFFFFFF
166
167 if (row % 12 < 8) {
168 acc = (acc shl 1) + (bwpixel shr 23)
169 if (++accv == 8) {
170 output[offset++] = acc.toUByte()
171 acc = 0
172 accv = 0
173 }
174 }
175
176 val err = (pixel - bwpixel) / 42
177 fun AddSatShift(xoff : Int, yoff: Int, shift : Int) {
178 val pixelold = scratch[(row + yoff) * DISPLAY_WIDTH + column + xoff ]
179 var r = pixelold + (err shl (16 - shift))
180 if ( r < 0 ) r = 0
181 if ( r > 0xFFFFFF) r = 0xFFFFFF
182 scratch[(row + yoff) * DISPLAY_WIDTH + column + xoff ] = r
183 }
184
185 AddSatShift(0, 1, 13)
186 AddSatShift(0, 2, 14)
187 if (column > 0) {
188 AddSatShift(-1, 1, 14)
189 AddSatShift(-1, 2, 15)
190 }
191
192 if (column > 1) {
193 AddSatShift(-2, 1, 15)
194 AddSatShift(-2, 2, 16)
195 }
196
197 if (column < DISPLAY_WIDTH - 1) {
198 AddSatShift( 1, 0, 13)
199 AddSatShift( 1, 1, 14)
200 AddSatShift( 1, 2, 15)
201 }
202
203 if (column < DISPLAY_WIDTH - 2) {
204 AddSatShift( 2, 0, 14)
205 AddSatShift( 2, 1, 15)
206 AddSatShift( 2, 2, 16)
207 }
208 }
209 val address = InetSocketAddress("172.23.42.29", 2342 )
210// val address = InetSocketAddress("192.168.178.69", 2342 )
211 try {
212 socket.send(DatagramPacket(output.toByteArray(), offset, address))
213 } catch (e: Exception) {
214 // Ignore network exceptions
215 }
216
217 // Compute the FPS of the entire pipeline
218 val frameCount = 10
219 if (++frameCounter % frameCount == 0) {
220 frameCounter = 0
221 val now = System.currentTimeMillis()
222 val delta = now - lastFpsTimestamp
223 val fps = 1000 * frameCount.toFloat() / delta
224// Log.d(TAG, "FPS: ${"%.02f".format(fps)} " + bitmapBuffer.width + " x " + bitmapBuffer.height)
225
226 activityCameraBinding.viewFinder.post {
227 activityCameraBinding.textPrediction.text = "FPS: ${"%.02f".format(fps)}"
228 }
229
230 lastFpsTimestamp = now
231 }
232 })
233
234 // Create a new camera selector each time, enforcing lens facing
235 val cameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()
236
237 // Apply declared configs to CameraX using the same lifecycle owner
238 cameraProvider.unbindAll()
239 cameraProvider.bindToLifecycle(
240 this as LifecycleOwner, cameraSelector, preview, imageAnalysis)
241
242 // Use the camera object to link our preview use case with the view
243 preview.setSurfaceProvider(activityCameraBinding.viewFinder.surfaceProvider)
244
245 }, ContextCompat.getMainExecutor(this))
246 }
247
248 override fun onResume() {
249 super.onResume()
250
251 // Request permissions each time the app resumes, since they can be revoked at any time
252 if (!hasPermissions(this)) {
253 ActivityCompat.requestPermissions(
254 this, permissions.toTypedArray(), permissionsRequestCode)
255 } else {
256 bindCameraUseCases()
257 }
258 }
259
260 override fun onRequestPermissionsResult(
261 requestCode: Int,
262 permissions: Array<out String>,
263 grantResults: IntArray
264 ) {
265 super.onRequestPermissionsResult(requestCode, permissions, grantResults)
266 if (requestCode == permissionsRequestCode && hasPermissions(this)) {
267 bindCameraUseCases()
268 } else {
269 finish() // If we don't have the required permissions, we can't run
270 }
271 }
272
273 /** Convenience method used to check if all permissions required by this app are granted */
274 private fun hasPermissions(context: Context) = permissions.all {
275 ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
276 }
277
278 companion object {
279 private val TAG = CameraActivity::class.java.simpleName
280 }
281}