Android Studio+OpenCV触摸取色始终返回R(0)G(0)B(0)问题排查
问题描述
我在Android Studio中用OpenCV实现了相机实时预览功能,现在想要获取触摸像素点(x,y)对应的RGB颜色值,但当前代码始终返回R(0)G(0)B(0),代码如下:
import androidx.annotation.NonNonNull; import androidx.appcompat.app.AppCompatActivity; import android.Manifest; import android.annotation.SuppressLint; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.Color; import android.os.Bundle; import android.view.MotionEvent; import android.view.View; import android.widget.TextView; import android.widget.Toast; import org.opencv.android.CameraActivity; import org.opencv.android.CameraBridgeViewBase; import org.opencv.android.OpenCVLoader; import org.opencv.core.Mat; import java.util.Collections; import java.util.List; public class MainActivity extends CameraActivity { CameraBridgeViewBase cameraBridgeViewBase; TextView textView2, textView1; Bitmap bitmap; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getPermission(); cameraBridgeViewBase = findViewById(R.id.cameraView); textView2 = findViewById(R.id.textView2); textView1 = findViewById(R.id.textView); cameraBridgeViewBase.setCvCameraViewListener(new CameraBridgeViewBase.CvCameraViewListener2() { @Override public void onCameraViewStarted(int width, int height) { } @Override public void onCameraViewStopped() {} @Override public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) { return inputFrame.rgba(); } }); if(OpenCVLoader.initDebug()){ cameraBridgeViewBase.enableView(); } cameraBridgeViewBase.setDrawingCacheEnabled(true); cameraBridgeViewBase.buildDrawingCache(true); cameraBridgeViewBase.setOnTouchListener(new View.OnTouchListener() { @SuppressLint({"SetTextI18n", "ClickableViewAccessibility"}) @Override public boolean onTouch(View view, MotionEvent motionEvent) { bitmap = cameraBridgeViewBase.getDrawingCache(); int h =bitmap.getHeight(); int x = (int) motionEvent.getX(); int y = (int) motionEvent.getY(); int pixel = bitmap.getPixel(x, y); int r = Color.red(pixel); int g = Color.green(pixel); int b = Color.blue(pixel); textView2.setText("R("+r+")"+"G("+g+")"+"B("+b+")"); textView2.setTextColor(Color.WHITE); textView2.setBackgroundColor(Color.rgb(r,g,b)); return true; } }); } @Override protected void onResume() { super.onResume(); cameraBridgeViewBase.enableView(); } @Override protected void onDestroy() { super.onDestroy(); cameraBridgeViewBase.disableView(); } @Override protected void onPause() { super.onPause(); cameraBridgeViewBase.disableView(); } @Override protected List<? extends CameraBridgeViewBase> getCameraViewList() { return Collections.singletonList(cameraBridgeViewBase); } void getPermission(){ if(checkSelfPermission(Manifest.permission.CAMERA)!=PackageManager.PERMISSION_GRANTED){ requestPermissions(new String[]{Manifest.permission.CAMERA},101); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (grantResults.length > 0 && grantResults[0] != PackageManager.PERMISSION_GRANTED) { getPermission(); } } }
问题排查与修复
核心原因
你依赖CameraBridgeViewBase的DrawingCache获取像素,但OpenCV的相机预览是通过OpenGL渲染的,DrawingCache无法正确捕获实时相机帧数据;同时触摸坐标未与相机帧的实际尺寸、方向做映射,导致读取到空白区域的像素(全0)。
修复方案(推荐):直接从OpenCV的Mat中读取像素
放弃使用DrawingCache,改为在onCameraFrame回调中保存当前帧的Mat,触摸时直接从Mat中读取对应坐标的像素值:
- 添加全局变量保存当前帧:
private Mat currentFrame;
- 在
onCameraFrame中更新当前帧:
@Override public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) { currentFrame = inputFrame.rgba(); return currentFrame; }
- 修改触摸事件处理逻辑,处理坐标映射并读取Mat像素:
cameraBridgeViewBase.setOnTouchListener(new View.OnTouchListener() { @SuppressLint({"SetTextI18n", "ClickableViewAccessibility"}) @Override public boolean onTouch(View view, MotionEvent motionEvent) { if (currentFrame == null) { return true; } // 转换触摸坐标到相机帧坐标 float viewWidth = view.getWidth(); float viewHeight = view.getHeight(); float frameWidth = currentFrame.cols(); float frameHeight = currentFrame.rows(); // 计算缩放比例,竖屏预览时反转Y坐标(根据实际相机方向调整) float scaleX = frameWidth / viewWidth; float scaleY = frameHeight / viewHeight; int frameX = (int) (motionEvent.getX() * scaleX); int frameY = (int) ((viewHeight - motionEvent.getY()) * scaleY); // 确保坐标在帧范围内 if (frameX >= 0 && frameX < frameWidth && frameY >= 0 && frameY < frameHeight) { // RGBA格式Mat,像素数组顺序为[R, G, B, A] double[] pixel = currentFrame.get(frameY, frameX); int r = (int) pixel[0]; int g = (int) pixel[1]; int b = (int) pixel[2]; textView2.setText("R("+r+")"+"G("+g+")"+"B("+b+")"); textView2.setTextColor(Color.WHITE); textView2.setBackgroundColor(Color.rgb(r,g,b)); } else { Toast.makeText(MainActivity.this, "触摸位置超出预览范围", Toast.LENGTH_SHORT).show(); } return true; } });
- 移除无用的DrawingCache代码:
删除以下两行:
cameraBridgeViewBase.setDrawingCacheEnabled(true); cameraBridgeViewBase.buildDrawingCache(true);
额外注意事项
- 相机预览方向可能与屏幕方向不一致,需根据实际情况调整坐标映射逻辑(比如前置摄像头需镜像处理)。
- 在
onCameraViewStopped中释放Mat避免内存泄漏:
@Override public void onCameraViewStopped() { if (currentFrame != null) { currentFrame.release(); currentFrame = null; } }
内容的提问来源于stack exchange,提问作者tlmary
相关产品推荐
相关产品推荐

