You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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中读取对应坐标的像素值:

  1. 添加全局变量保存当前帧:
private Mat currentFrame;
  1. 在onCameraFrame中更新当前帧:
@Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
    currentFrame = inputFrame.rgba();
    return currentFrame;
}
  1. 修改触摸事件处理逻辑,处理坐标映射并读取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;
    }
});
  1. 移除无用的DrawingCache代码:
    删除以下两行:
cameraBridgeViewBase.setDrawingCacheEnabled(true);
cameraBridgeViewBase.buildDrawingCache(true);

额外注意事项

  • 相机预览方向可能与屏幕方向不一致,需根据实际情况调整坐标映射逻辑(比如前置摄像头需镜像处理)。
  • 在onCameraViewStopped中释放Mat避免内存泄漏:
@Override
public void onCameraViewStopped() {
    if (currentFrame != null) {
        currentFrame.release();
        currentFrame = null;
    }
}

内容的提问来源于stack exchange,提问作者tlmary

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 11:22:48