react-native-camera-vision-pixel-colors 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CameraVisionPixelColors.podspec +32 -0
- package/LICENSE +21 -0
- package/README.md +190 -0
- package/android/CMakeLists.txt +32 -0
- package/android/build.gradle +151 -0
- package/android/fix-prefab.gradle +51 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/cpp-adapter.cpp +6 -0
- package/android/src/main/java/com/cameravisionpixelcolors/CameraVisionPixelColorsPackage.kt +24 -0
- package/android/src/main/java/com/cameravisionpixelcolors/PixelAnalyzerEngine.kt +256 -0
- package/android/src/main/java/com/cameravisionpixelcolors/PixelColorsFrameProcessor.kt +40 -0
- package/android/src/main/java/com/cameravisionpixelcolors/YuvToBitmapConverter.kt +33 -0
- package/android/src/main/java/com/margelo/nitro/cameravisionpixelcolors/HybridCameraVisionPixelColors.kt +50 -0
- package/app.plugin.js +1 -0
- package/ios/Bridge.h +8 -0
- package/ios/HybridCameraVisionPixelColors.swift +53 -0
- package/ios/PixelAnalyzerEngine.swift +346 -0
- package/ios/PixelColorsFrameProcessor.m +5 -0
- package/ios/PixelColorsFrameProcessor.swift +50 -0
- package/lib/commonjs/index.js +28 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/specs/camera-vision-pixel-colors.nitro.js +6 -0
- package/lib/commonjs/specs/camera-vision-pixel-colors.nitro.js.map +1 -0
- package/lib/module/index.js +23 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/specs/camera-vision-pixel-colors.nitro.js +4 -0
- package/lib/module/specs/camera-vision-pixel-colors.nitro.js.map +1 -0
- package/lib/typescript/src/index.d.ts +6 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/specs/camera-vision-pixel-colors.nitro.d.ts +40 -0
- package/lib/typescript/src/specs/camera-vision-pixel-colors.nitro.d.ts.map +1 -0
- package/nitro.json +25 -0
- package/nitrogen/generated/.gitattributes +1 -0
- package/nitrogen/generated/android/CameraVisionPixelColors+autolinking.cmake +81 -0
- package/nitrogen/generated/android/CameraVisionPixelColors+autolinking.gradle +27 -0
- package/nitrogen/generated/android/CameraVisionPixelColorsOnLoad.cpp +44 -0
- package/nitrogen/generated/android/CameraVisionPixelColorsOnLoad.hpp +25 -0
- package/nitrogen/generated/android/c++/JHybridCameraVisionPixelColorsSpec.cpp +90 -0
- package/nitrogen/generated/android/c++/JHybridCameraVisionPixelColorsSpec.hpp +66 -0
- package/nitrogen/generated/android/c++/JImageData.hpp +66 -0
- package/nitrogen/generated/android/c++/JMotionResult.hpp +61 -0
- package/nitrogen/generated/android/c++/JPixelColorsResult.hpp +114 -0
- package/nitrogen/generated/android/c++/JRGBColor.hpp +65 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/cameravisionpixelcolors/CameraVisionPixelColorsOnLoad.kt +35 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/cameravisionpixelcolors/HybridCameraVisionPixelColorsSpec.kt +58 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/cameravisionpixelcolors/ImageData.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/cameravisionpixelcolors/MotionResult.kt +41 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/cameravisionpixelcolors/PixelColorsResult.kt +50 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/cameravisionpixelcolors/RGBColor.kt +44 -0
- package/nitrogen/generated/ios/CameraVisionPixelColors+autolinking.rb +60 -0
- package/nitrogen/generated/ios/CameraVisionPixelColors-Swift-Cxx-Bridge.cpp +49 -0
- package/nitrogen/generated/ios/CameraVisionPixelColors-Swift-Cxx-Bridge.hpp +162 -0
- package/nitrogen/generated/ios/CameraVisionPixelColors-Swift-Cxx-Umbrella.hpp +59 -0
- package/nitrogen/generated/ios/CameraVisionPixelColorsAutolinking.mm +33 -0
- package/nitrogen/generated/ios/CameraVisionPixelColorsAutolinking.swift +26 -0
- package/nitrogen/generated/ios/c++/HybridCameraVisionPixelColorsSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridCameraVisionPixelColorsSpecSwift.hpp +99 -0
- package/nitrogen/generated/ios/swift/Func_void_PixelColorsResult.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +47 -0
- package/nitrogen/generated/ios/swift/HybridCameraVisionPixelColorsSpec.swift +56 -0
- package/nitrogen/generated/ios/swift/HybridCameraVisionPixelColorsSpec_cxx.swift +146 -0
- package/nitrogen/generated/ios/swift/ImageData.swift +40 -0
- package/nitrogen/generated/ios/swift/MotionResult.swift +35 -0
- package/nitrogen/generated/ios/swift/PixelColorsResult.swift +81 -0
- package/nitrogen/generated/ios/swift/RGBColor.swift +40 -0
- package/nitrogen/generated/shared/c++/HybridCameraVisionPixelColorsSpec.cpp +21 -0
- package/nitrogen/generated/shared/c++/HybridCameraVisionPixelColorsSpec.hpp +67 -0
- package/nitrogen/generated/shared/c++/ImageData.hpp +91 -0
- package/nitrogen/generated/shared/c++/MotionResult.hpp +87 -0
- package/nitrogen/generated/shared/c++/PixelColorsResult.hpp +105 -0
- package/nitrogen/generated/shared/c++/RGBColor.hpp +91 -0
- package/package.json +143 -0
- package/plugin/withPixelColors.js +12 -0
- package/plugin/withPixelColorsAndroid.js +11 -0
- package/plugin/withPixelColorsIOS.js +11 -0
- package/src/index.ts +42 -0
- package/src/specs/camera-vision-pixel-colors.nitro.ts +40 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
package com.cameravisionpixelcolors
|
|
2
|
+
|
|
3
|
+
import android.graphics.Bitmap
|
|
4
|
+
import android.graphics.Rect
|
|
5
|
+
import java.nio.ByteBuffer
|
|
6
|
+
import java.util.concurrent.Executors
|
|
7
|
+
import java.util.concurrent.atomic.AtomicReference
|
|
8
|
+
import kotlin.math.abs
|
|
9
|
+
import kotlin.math.max
|
|
10
|
+
import kotlin.math.min
|
|
11
|
+
|
|
12
|
+
data class AnalysisOptions(
|
|
13
|
+
val enableMotionDetection: Boolean = false,
|
|
14
|
+
val motionThreshold: Float = 0.1f,
|
|
15
|
+
val roi: ROIConfig? = null
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
data class ROIConfig(
|
|
19
|
+
val x: Float,
|
|
20
|
+
val y: Float,
|
|
21
|
+
val width: Float,
|
|
22
|
+
val height: Float
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
object PixelAnalyzerEngine {
|
|
26
|
+
private const val BUCKETS = 32 * 32 * 32
|
|
27
|
+
private const val MAX_RAW_PIXEL_DIMENSION = 1920
|
|
28
|
+
private val executor = Executors.newSingleThreadExecutor()
|
|
29
|
+
private val cachedResult = AtomicReference<Map<String, Any>>(mapOf(
|
|
30
|
+
"uniqueColorCount" to 0,
|
|
31
|
+
"topColors" to emptyList<Map<String, Int>>(),
|
|
32
|
+
"brightestColors" to emptyList<Map<String, Int>>()
|
|
33
|
+
))
|
|
34
|
+
|
|
35
|
+
private val histogram = IntArray(BUCKETS)
|
|
36
|
+
private val brightnessSum = IntArray(BUCKETS)
|
|
37
|
+
|
|
38
|
+
// Motion detection state
|
|
39
|
+
private var previousGrayscale: IntArray? = null
|
|
40
|
+
private var previousWidth: Int = 0
|
|
41
|
+
private var previousHeight: Int = 0
|
|
42
|
+
|
|
43
|
+
fun analyzeAsync(bitmap: Bitmap, options: AnalysisOptions = AnalysisOptions()) {
|
|
44
|
+
executor.execute {
|
|
45
|
+
analyze(bitmap, options)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
fun analyzeSync(): Map<String, Any> {
|
|
50
|
+
return cachedResult.get()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Calculate ROI in pixel coordinates
|
|
54
|
+
private fun calculateROI(config: ROIConfig, width: Int, height: Int): Rect {
|
|
55
|
+
val x = (config.x * width).toInt()
|
|
56
|
+
val y = (config.y * height).toInt()
|
|
57
|
+
val w = max(1, (config.width * width).toInt())
|
|
58
|
+
val h = max(1, (config.height * height).toInt())
|
|
59
|
+
return Rect(x, y, min(x + w, width), min(y + h, height))
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Calculate motion score between current and previous frame
|
|
63
|
+
private fun calculateMotion(bitmap: Bitmap, threshold: Float): Map<String, Any> {
|
|
64
|
+
val width = bitmap.width
|
|
65
|
+
val height = bitmap.height
|
|
66
|
+
val totalPixels = width * height
|
|
67
|
+
val pixels = IntArray(totalPixels)
|
|
68
|
+
bitmap.getPixels(pixels, 0, width, 0, 0, width, height)
|
|
69
|
+
|
|
70
|
+
// Convert to grayscale
|
|
71
|
+
val currentGrayscale = IntArray(totalPixels) { i ->
|
|
72
|
+
val px = pixels[i]
|
|
73
|
+
val r = (px shr 16) and 0xFF
|
|
74
|
+
val g = (px shr 8) and 0xFF
|
|
75
|
+
val b = px and 0xFF
|
|
76
|
+
((0.299 * r + 0.587 * g + 0.114 * b).toInt())
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
val prev = previousGrayscale
|
|
80
|
+
if (prev == null || previousWidth != width || previousHeight != height) {
|
|
81
|
+
// First frame - save and return zero motion
|
|
82
|
+
previousGrayscale = currentGrayscale
|
|
83
|
+
previousWidth = width
|
|
84
|
+
previousHeight = height
|
|
85
|
+
return mapOf("score" to 0.0, "hasMotion" to false)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Count pixels exceeding threshold
|
|
89
|
+
val thresholdValue = (threshold * 255).toInt()
|
|
90
|
+
var changedCount = 0
|
|
91
|
+
for (i in 0 until totalPixels) {
|
|
92
|
+
if (abs(currentGrayscale[i] - prev[i]) > thresholdValue) {
|
|
93
|
+
changedCount++
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
val score = changedCount.toDouble() / totalPixels
|
|
98
|
+
|
|
99
|
+
// Swap buffers
|
|
100
|
+
previousGrayscale = currentGrayscale
|
|
101
|
+
previousWidth = width
|
|
102
|
+
previousHeight = height
|
|
103
|
+
|
|
104
|
+
return mapOf("score" to score, "hasMotion" to (score > threshold))
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Extract raw RGBA pixels with optional scaling
|
|
108
|
+
private fun extractRawPixels(bitmap: Bitmap, roi: Rect?): ByteBuffer? {
|
|
109
|
+
var workBitmap = bitmap
|
|
110
|
+
|
|
111
|
+
// Apply ROI if provided
|
|
112
|
+
if (roi != null) {
|
|
113
|
+
workBitmap = Bitmap.createBitmap(
|
|
114
|
+
bitmap,
|
|
115
|
+
roi.left,
|
|
116
|
+
roi.top,
|
|
117
|
+
roi.width(),
|
|
118
|
+
roi.height()
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
var width = workBitmap.width
|
|
123
|
+
var height = workBitmap.height
|
|
124
|
+
|
|
125
|
+
// Scale down if exceeds 1080p
|
|
126
|
+
if (width > MAX_RAW_PIXEL_DIMENSION || height > MAX_RAW_PIXEL_DIMENSION) {
|
|
127
|
+
val scale = min(
|
|
128
|
+
MAX_RAW_PIXEL_DIMENSION.toFloat() / width,
|
|
129
|
+
MAX_RAW_PIXEL_DIMENSION.toFloat() / height
|
|
130
|
+
)
|
|
131
|
+
val newWidth = (width * scale).toInt()
|
|
132
|
+
val newHeight = (height * scale).toInt()
|
|
133
|
+
workBitmap = Bitmap.createScaledBitmap(workBitmap, newWidth, newHeight, true)
|
|
134
|
+
width = newWidth
|
|
135
|
+
height = newHeight
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Extract RGBA data
|
|
139
|
+
val pixels = IntArray(width * height)
|
|
140
|
+
workBitmap.getPixels(pixels, 0, width, 0, 0, width, height)
|
|
141
|
+
|
|
142
|
+
val buffer = ByteBuffer.allocateDirect(width * height * 4)
|
|
143
|
+
for (px in pixels) {
|
|
144
|
+
// ARGB -> RGBA conversion
|
|
145
|
+
val a = (px shr 24) and 0xFF
|
|
146
|
+
val r = (px shr 16) and 0xFF
|
|
147
|
+
val g = (px shr 8) and 0xFF
|
|
148
|
+
val b = px and 0xFF
|
|
149
|
+
buffer.put(r.toByte())
|
|
150
|
+
buffer.put(g.toByte())
|
|
151
|
+
buffer.put(b.toByte())
|
|
152
|
+
buffer.put(a.toByte())
|
|
153
|
+
}
|
|
154
|
+
buffer.rewind()
|
|
155
|
+
|
|
156
|
+
return buffer
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
fun analyzeImageData(width: Int, height: Int, data: ByteArray): Map<String, Any> {
|
|
160
|
+
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
|
161
|
+
bitmap.copyPixelsFromBuffer(ByteBuffer.wrap(data))
|
|
162
|
+
return analyzeImmediate(bitmap)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private fun analyzeImmediate(bitmap: Bitmap, options: AnalysisOptions = AnalysisOptions()): Map<String, Any> {
|
|
166
|
+
var workBitmap = bitmap
|
|
167
|
+
var roiRect: Rect? = null
|
|
168
|
+
|
|
169
|
+
// Apply ROI if configured
|
|
170
|
+
if (options.roi != null) {
|
|
171
|
+
roiRect = calculateROI(options.roi, bitmap.width, bitmap.height)
|
|
172
|
+
workBitmap = Bitmap.createBitmap(
|
|
173
|
+
bitmap,
|
|
174
|
+
roiRect.left,
|
|
175
|
+
roiRect.top,
|
|
176
|
+
roiRect.width(),
|
|
177
|
+
roiRect.height()
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
val width = workBitmap.width
|
|
182
|
+
val height = workBitmap.height
|
|
183
|
+
val size = width * height
|
|
184
|
+
val pixels = IntArray(size)
|
|
185
|
+
workBitmap.getPixels(pixels, 0, width, 0, 0, width, height)
|
|
186
|
+
|
|
187
|
+
val localHistogram = IntArray(BUCKETS)
|
|
188
|
+
val localBrightnessSum = IntArray(BUCKETS)
|
|
189
|
+
|
|
190
|
+
for (px in pixels) {
|
|
191
|
+
val r = (px shr 16) and 0xFF
|
|
192
|
+
val g = (px shr 8) and 0xFF
|
|
193
|
+
val b = px and 0xFF
|
|
194
|
+
val rq = r shr 3
|
|
195
|
+
val gq = g shr 3
|
|
196
|
+
val bq = b shr 3
|
|
197
|
+
val idx = (rq shl 10) or (gq shl 5) or bq
|
|
198
|
+
localHistogram[idx]++
|
|
199
|
+
val brightness = (2126 * r + 7152 * g + 722 * b) / 10000
|
|
200
|
+
localBrightnessSum[idx] += brightness
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
val topColors = ArrayList<Pair<Int, Int>>(3)
|
|
204
|
+
val topBright = ArrayList<Pair<Int, Int>>(3)
|
|
205
|
+
var uniqueCount = 0
|
|
206
|
+
for (i in 0 until BUCKETS) {
|
|
207
|
+
val count = localHistogram[i]
|
|
208
|
+
if (count == 0) continue
|
|
209
|
+
uniqueCount++
|
|
210
|
+
insertTop(topColors, i, count)
|
|
211
|
+
val avgBrightness = localBrightnessSum[i] / max(count, 1)
|
|
212
|
+
insertTop(topBright, i, avgBrightness)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
fun decode(idx: Int): Map<String, Int> {
|
|
216
|
+
return mapOf(
|
|
217
|
+
"r" to ((idx shr 10) and 31 shl 3),
|
|
218
|
+
"g" to ((idx shr 5) and 31 shl 3),
|
|
219
|
+
"b" to ((idx and 31) shl 3)
|
|
220
|
+
)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
val result = mutableMapOf<String, Any>(
|
|
224
|
+
"uniqueColorCount" to uniqueCount,
|
|
225
|
+
"topColors" to topColors.map { decode(it.first) },
|
|
226
|
+
"brightestColors" to topBright.map { decode(it.first) }
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
// Add ROI applied flag
|
|
230
|
+
if (options.roi != null) {
|
|
231
|
+
result["roiApplied"] = true
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Add motion detection if enabled
|
|
235
|
+
if (options.enableMotionDetection) {
|
|
236
|
+
val motionResult = calculateMotion(bitmap, options.motionThreshold)
|
|
237
|
+
result["motion"] = motionResult
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return result
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
private fun analyze(bitmap: Bitmap, options: AnalysisOptions = AnalysisOptions()) {
|
|
244
|
+
val result = analyzeImmediate(bitmap, options)
|
|
245
|
+
cachedResult.set(result)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
private fun insertTop(list: MutableList<Pair<Int, Int>>, idx: Int, value: Int) {
|
|
249
|
+
var i = 0
|
|
250
|
+
while (i < list.size && value <= list[i].second) i++
|
|
251
|
+
if (i < 3) {
|
|
252
|
+
list.add(i, idx to value)
|
|
253
|
+
if (list.size > 3) list.removeAt(3)
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
package com.cameravisionpixelcolors
|
|
2
|
+
|
|
3
|
+
import com.mrousavy.camera.frameprocessors.Frame
|
|
4
|
+
import com.mrousavy.camera.frameprocessors.FrameProcessorPlugin
|
|
5
|
+
import com.mrousavy.camera.frameprocessors.VisionCameraProxy
|
|
6
|
+
|
|
7
|
+
class PixelColorsFrameProcessor(proxy: VisionCameraProxy, options: Map<String, Any>?) : FrameProcessorPlugin() {
|
|
8
|
+
override fun callback(frame: Frame, arguments: Map<String, Any>?): Any {
|
|
9
|
+
val image = frame.image ?: return emptyMap<String, Any>()
|
|
10
|
+
val bitmap = YuvToBitmapConverter.convert(image)
|
|
11
|
+
|
|
12
|
+
// Parse options from arguments
|
|
13
|
+
val analysisOptions = parseOptions(arguments)
|
|
14
|
+
|
|
15
|
+
PixelAnalyzerEngine.analyzeAsync(bitmap, analysisOptions)
|
|
16
|
+
return PixelAnalyzerEngine.analyzeSync()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
private fun parseOptions(arguments: Map<String, Any>?): AnalysisOptions {
|
|
20
|
+
val optionsMap = arguments?.get("options") as? Map<*, *> ?: return AnalysisOptions()
|
|
21
|
+
|
|
22
|
+
val enableMotionDetection = optionsMap["enableMotionDetection"] as? Boolean ?: false
|
|
23
|
+
val motionThreshold = (optionsMap["motionThreshold"] as? Number)?.toFloat() ?: 0.1f
|
|
24
|
+
|
|
25
|
+
val roiMap = optionsMap["roi"] as? Map<*, *>
|
|
26
|
+
val roi = if (roiMap != null) {
|
|
27
|
+
val x = (roiMap["x"] as? Number)?.toFloat() ?: 0f
|
|
28
|
+
val y = (roiMap["y"] as? Number)?.toFloat() ?: 0f
|
|
29
|
+
val width = (roiMap["width"] as? Number)?.toFloat() ?: 1f
|
|
30
|
+
val height = (roiMap["height"] as? Number)?.toFloat() ?: 1f
|
|
31
|
+
ROIConfig(x, y, width, height)
|
|
32
|
+
} else null
|
|
33
|
+
|
|
34
|
+
return AnalysisOptions(
|
|
35
|
+
enableMotionDetection = enableMotionDetection,
|
|
36
|
+
motionThreshold = motionThreshold,
|
|
37
|
+
roi = roi
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
package com.cameravisionpixelcolors
|
|
2
|
+
|
|
3
|
+
import android.graphics.Bitmap
|
|
4
|
+
import android.graphics.BitmapFactory
|
|
5
|
+
import android.graphics.ImageFormat
|
|
6
|
+
import android.graphics.Rect
|
|
7
|
+
import android.graphics.YuvImage
|
|
8
|
+
import android.media.Image
|
|
9
|
+
import java.io.ByteArrayOutputStream
|
|
10
|
+
|
|
11
|
+
object YuvToBitmapConverter {
|
|
12
|
+
fun convert(image: Image): Bitmap {
|
|
13
|
+
val yBuffer = image.planes[0].buffer
|
|
14
|
+
val uBuffer = image.planes[1].buffer
|
|
15
|
+
val vBuffer = image.planes[2].buffer
|
|
16
|
+
|
|
17
|
+
val ySize = yBuffer.remaining()
|
|
18
|
+
val uSize = uBuffer.remaining()
|
|
19
|
+
val vSize = vBuffer.remaining()
|
|
20
|
+
|
|
21
|
+
val nv21 = ByteArray(ySize + uSize + vSize)
|
|
22
|
+
|
|
23
|
+
yBuffer.get(nv21, 0, ySize)
|
|
24
|
+
vBuffer.get(nv21, ySize, vSize)
|
|
25
|
+
uBuffer.get(nv21, ySize + vSize, uSize)
|
|
26
|
+
|
|
27
|
+
val yuvImage = YuvImage(nv21, ImageFormat.NV21, image.width, image.height, null)
|
|
28
|
+
val out = ByteArrayOutputStream()
|
|
29
|
+
yuvImage.compressToJpeg(Rect(0, 0, image.width, image.height), 90, out)
|
|
30
|
+
val imageBytes = out.toByteArray()
|
|
31
|
+
return BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
package com.margelo.nitro.cameravisionpixelcolors
|
|
2
|
+
|
|
3
|
+
import com.cameravisionpixelcolors.PixelAnalyzerEngine
|
|
4
|
+
import com.margelo.nitro.core.Promise
|
|
5
|
+
import java.util.concurrent.Executors
|
|
6
|
+
|
|
7
|
+
class HybridCameraVisionPixelColors: HybridCameraVisionPixelColorsSpec() {
|
|
8
|
+
private val executor = Executors.newSingleThreadExecutor()
|
|
9
|
+
|
|
10
|
+
override fun analyzeImageAsync(image: ImageData): Promise<PixelColorsResult> {
|
|
11
|
+
return Promise.async {
|
|
12
|
+
val width = image.width.toInt()
|
|
13
|
+
val height = image.height.toInt()
|
|
14
|
+
val data = ByteArray(image.data.size.toInt())
|
|
15
|
+
image.data.getBuffer(false).get(data)
|
|
16
|
+
|
|
17
|
+
val result = PixelAnalyzerEngine.analyzeImageData(width, height, data)
|
|
18
|
+
|
|
19
|
+
val uniqueColorCount = result["uniqueColorCount"] as? Int ?: 0
|
|
20
|
+
@Suppress("UNCHECKED_CAST")
|
|
21
|
+
val topColorsMap = result["topColors"] as? List<Map<String, Int>> ?: emptyList()
|
|
22
|
+
@Suppress("UNCHECKED_CAST")
|
|
23
|
+
val brightestColorsMap = result["brightestColors"] as? List<Map<String, Int>> ?: emptyList()
|
|
24
|
+
|
|
25
|
+
val topColors = topColorsMap.map { map ->
|
|
26
|
+
RGBColor(
|
|
27
|
+
r = (map["r"] ?: 0).toDouble(),
|
|
28
|
+
g = (map["g"] ?: 0).toDouble(),
|
|
29
|
+
b = (map["b"] ?: 0).toDouble()
|
|
30
|
+
)
|
|
31
|
+
}.toTypedArray()
|
|
32
|
+
|
|
33
|
+
val brightestColors = brightestColorsMap.map { map ->
|
|
34
|
+
RGBColor(
|
|
35
|
+
r = (map["r"] ?: 0).toDouble(),
|
|
36
|
+
g = (map["g"] ?: 0).toDouble(),
|
|
37
|
+
b = (map["b"] ?: 0).toDouble()
|
|
38
|
+
)
|
|
39
|
+
}.toTypedArray()
|
|
40
|
+
|
|
41
|
+
PixelColorsResult(
|
|
42
|
+
uniqueColorCount = uniqueColorCount.toDouble(),
|
|
43
|
+
topColors = topColors,
|
|
44
|
+
brightestColors = brightestColors,
|
|
45
|
+
motion = null,
|
|
46
|
+
roiApplied = null
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
package/app.plugin.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./plugin/withPixelColors');
|
package/ios/Bridge.h
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
//
|
|
2
|
+
// HybridCameraVisionPixelColors.swift
|
|
3
|
+
// CameraVisionPixelColors
|
|
4
|
+
//
|
|
5
|
+
|
|
6
|
+
import Foundation
|
|
7
|
+
import NitroModules
|
|
8
|
+
|
|
9
|
+
class HybridCameraVisionPixelColors: HybridCameraVisionPixelColorsSpec {
|
|
10
|
+
func analyzeImageAsync(image: ImageData) throws -> Promise<PixelColorsResult> {
|
|
11
|
+
return Promise { resolve, reject in
|
|
12
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
13
|
+
let width = Int(image.width)
|
|
14
|
+
let height = Int(image.height)
|
|
15
|
+
let data = Data(image.data)
|
|
16
|
+
|
|
17
|
+
let result = PixelAnalyzerEngine.shared.analyzeImageData(
|
|
18
|
+
width: width,
|
|
19
|
+
height: height,
|
|
20
|
+
data: data
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
let uniqueColorCount = result["uniqueColorCount"] as? Int ?? 0
|
|
24
|
+
let topColorsDict = result["topColors"] as? [[String: Int]] ?? []
|
|
25
|
+
let brightestColorsDict = result["brightestColors"] as? [[String: Int]] ?? []
|
|
26
|
+
|
|
27
|
+
let topColors = topColorsDict.map { dict in
|
|
28
|
+
RGBColor(
|
|
29
|
+
r: Double(dict["r"] ?? 0),
|
|
30
|
+
g: Double(dict["g"] ?? 0),
|
|
31
|
+
b: Double(dict["b"] ?? 0)
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let brightestColors = brightestColorsDict.map { dict in
|
|
36
|
+
RGBColor(
|
|
37
|
+
r: Double(dict["r"] ?? 0),
|
|
38
|
+
g: Double(dict["g"] ?? 0),
|
|
39
|
+
b: Double(dict["b"] ?? 0)
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let pixelColorsResult = PixelColorsResult(
|
|
44
|
+
uniqueColorCount: Double(uniqueColorCount),
|
|
45
|
+
topColors: topColors,
|
|
46
|
+
brightestColors: brightestColors
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
resolve(pixelColorsResult)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|