我有一张从相机胶卷或任何其他来源(通常是本地来源)加载的图像。
如何访问其像素数据图来执行一些计算或测量?
有一种方法可以使用本机模块获取此信息,但我目前只有 Android 实现。在 RN 0.42.3 上测试。首先,您需要在应用程序中创建一个本机模块。假设应用程序使用名称
SampleApp
进行初始化,请在 React Native 项目 android/app/src/main/java/com/sampleapp/bitmap
中创建新目录,其中包含两个文件:
android/app/src/main/java/com/sampleapp/bitmap/BitmapReactPackage.java
package com.sampleapp;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class BitmapReactPackage implements ReactPackage {
@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new BitmapModule(reactContext));
return modules;
}
}
android/app/src/main/java/com/sampleapp/bitmap/BitmapModule.java
package com.sampleapp;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.WritableNativeArray;
import com.facebook.react.bridge.WritableNativeMap;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import java.io.IOException;
public class BitmapModule extends ReactContextBaseJavaModule {
public BitmapModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "Bitmap";
}
@ReactMethod
public void getPixels(String filePath, final Promise promise) {
try {
WritableNativeMap result = new WritableNativeMap();
WritableNativeArray pixels = new WritableNativeArray();
Bitmap bitmap = BitmapFactory.decodeFile(filePath);
if (bitmap == null) {
promise.reject("Failed to decode. Path is incorrect or image is corrupted");
return;
}
int width = bitmap.getWidth();
int height = bitmap.getHeight();
boolean hasAlpha = bitmap.hasAlpha();
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int color = bitmap.getPixel(x, y);
String hex = Integer.toHexString(color);
pixels.pushString(hex);
}
}
result.putInt("width", width);
result.putInt("height", height);
result.putBoolean("hasAlpha", hasAlpha);
result.putArray("pixels", pixels);
promise.resolve(result);
} catch (Exception e) {
promise.reject(e);
}
}
}
正如您在第二个文件中看到的,有一个方法
getPixels
,它将作为 Bitmap
本机模块的一部分从 JS 中使用。它接受图像文件的路径,将图像转换为内部位图类型,从而允许读取图像像素。所有图像像素都被一一读取并以十六进制字符串的形式保存到像素数组中(因为React Native不允许通过桥传递十六进制值)。这些十六进制字符串有 8 个字符,每个 ARGB 通道 2 个字符:前两个字符是 alpha 通道的十六进制值,后两个字符 - 红色,第三两个 - 绿色,最后两个 - 蓝色通道。例如,值 ffffffff
- 是白色,而 ff0000ff
- 是蓝色。为了方便起见,图像宽度、高度和 Alpha 通道的存在与像素数组一起返回。方法返回一个带有对象的承诺:
{
width: 1200,
height: 800,
hasAlpha: false,
pixels: ['ffffffff', 'ff00ffff', 'ffff00ff', ...]
}
原生模块也必须在应用程序中注册,修改
android/app/src/main/java/com/sampleapp/MainApplication.java
并在其中添加新模块:
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new BitmapReactPackage() // <---
);
}
如何从JS使用:
import { NativeModules } from 'react-native';
const imagePath = '/storage/emulated/0/Pictures/blob.png';
NativeModules.Bitmap.getPixels(imagePath)
.then((image) => {
console.log(image.width);
console.log(image.height);
console.log(image.hasAlpha);
for (let x = 0; x < image.width; x++) {
for (let y = 0; y < image.height; y++) {
const offset = image.width * y + x;
const pixel = image.pixels[offset];
}
}
})
.catch((err) => {
console.error(err);
});
我需要提到,它的执行速度非常慢,很可能是因为通过桥传输了一个巨大的数组。
谢谢迈克尔。 看来今天容易多了
import { RNCamera } from 'react-native-camera';
<RNCamera ref={ref => {this.camera = ref;}} style={styles.preview}></RNCamera>
//Set the options for the camera
const options = {
base64: true
};
// Get the base64 version of the image
const data = await this.camera.takePictureAsync(options)
我将 byes 数组缓冲区转换为 unit8array,这样就可以将 ArrayBuffer 中的二进制数据作为字节数组(值从 0 到 255)进行交互。 这使得处理帧的原始二进制数据(例如提取像素值)变得更加容易。 它不需要时间就可以计算出我的设备的所有像素亮度的总和(以毫秒为单位)。我使用 110 作为亮度阈值。
const onLightDetected = Worklets.createRunOnJS((luminanceState) => {
setIsBrightnessFeedbackVisible(!luminanceState);
});
const frameProcessor = useSkiaFrameProcessor(frame => {
'worklet';
runAtTargetFps(1, () => {
const buffer = frame.toArrayBuffer();
const data = new Uint8Array(buffer);
console.log('pixelcount', data.length);
let luminanceSum = 0;
const sampleSize =data.length;
for (let i = 0; i < sampleSize; i++) {
luminanceSum += data[i];
}
const averageBrightness = luminanceSum / sampleSize;
console.log('avgbrightness', averageBrightness);
const luminanceState = averageBrightness > BRIGHTNESS_THRESHOLD;
void onLightDetected(luminanceState);
});
const faces = detectFaces(frame);
frame.render();
});