zl程序教程

您现在的位置是:首页 >  移动开发

当前栏目

OpenCV4 Android 颜色空间转换

Android转换 空间 颜色
2023-09-11 14:22:54 时间

图像色彩模式

位图模式

位图模式是色彩模式中占有空间最小,包含信息量最小的一种模式。它只有黑白两种色素,因此也叫做黑白图。
从彩色图转为位图的过程被称为 “二值化”。彩色图不能直接转为位图,需要先将其转为灰度模式,然后才能转为位图。

灰度模式

灰度模式是采用单一色调来表示图像。1个像素点占1字节,即 8 bit,每个像素使用 0~255 来表示亮度,0为黑色,128为灰色,255为白色。

RGB模式

RGB模式是我们最常用到的,也被称为真色彩。RGB模式的图像有3个颜色通道,分别是 红(Red),绿(Green)和 蓝(Blue),每个通道占用 8 bit,值范围为 0~255

在这里插入图片描述

CMYK模式

CMYK也称作印刷色彩模式。它和RGB相比最大不同是,RGB模式是发光的色彩模式,你在一间黑暗的房间内仍然可以看见萤幕上的内容。

CMYK是一种依靠反光的色彩模式,我们能阅读报纸的内容是为什么呢?是因阳光或灯光照射到报纸上,再把内容反射到我们的眼中。CMYK是需要有外界光源的情况下才可以看到的。所以在黑暗房间内是无法阅读的。

从理论上讲,只需要CMY三种油墨就足够,把CMY加在一起就应该得到黑色。但是高纯度的油墨暂时还不能实现,CMY相加的结果是暗红色。因此,为了确保黑色的输出,还需要加入一种专门的黑墨来调和。
在这里插入图片描述

HSB 模式

在 HSB 模式中,H(Hues)表示色相(0 ~ 360),S(Saturation)表示饱和度(0 ~ 100%),B(brightness)表示亮度(0 ~ 100%)。
HSB模式对应的媒介是人眼。

HSB模式中S和B呈现的数值越高,饱和度明度越高,页面色彩强烈艳丽,对视觉刺激是迅速的,醒目的效果,但不益于长时间的观看。
在这里插入图片描述

YUV格式

YUV开始主要用于电视系统以及模拟视频领域,后面手机相机也延用了这一格式,因此大部分摄像头输出的图片格式都是 YUV 格式(安卓是NV12,YUV的一种)。YUV 中的 “Y” 表示亮度(Luminance 或 Luma)也就是灰度值,“U” 和 “V” 表示的则是色度(Chrominance或Chroma),作用是描述影像色彩及饱和度,用于指定像素的颜色。如果只用 “Y” 而不用 “UV” 也可也进行呈像,只不过是黑白的,这样就能很好的解决了黑白电视与彩色电视的兼容问题。此外,与RGB相比,YUV具有占用带宽更少的优势。

重要函数及类型

Mat

Mat本质上是由两个数据部分组成的类: (包含信息有矩阵的大小,用于存储的方法,矩阵存储的地址等) 的矩阵头和一个指针,指向包含了像素值的矩阵(可根据选择用于存储的方法采用任何维度存储数据)。矩阵头部的大小是恒定的。然而,矩阵本身的大小因图像的不同而不同,通常是较大的数量级。

注意到一点,Mat 对于无法保存透明度通道,PNG 中透明的像素点会将其转为黑色

Mat 是初始化和释放

Mat 是一个 C/C++ 的指针类型,因此它需要通过手动分配空间和释放空间,对应的函数为 new Mat()mat.release()

bitmap 转 map

/**
 * Short form of the bitmapToMat(bmp, mat, unPremultiplyAlpha=false).
 * @param bmp is a valid input Bitmap object of the type 'ARGB_8888' or 'RGB_565'.
 * @param mat is a valid output Mat object, it will be reallocated if needed, so Mat may be empty.
 */
public static void bitmapToMat(Bitmap bmp, Mat mat) {
    bitmapToMat(bmp, mat, false);
}

map 转回 bitmap

/**
 * Converts OpenCV Mat to Android Bitmap.
 * <p>
 * <br>This function converts an image in the OpenCV Mat representation to the Android Bitmap.
 * <br>The input Mat object has to be of the types 'CV_8UC1' (gray-scale), 'CV_8UC3' (RGB) or 'CV_8UC4' (RGBA).
 * <br>The output Bitmap object has to be of the same size as the input Mat and of the types 'ARGB_8888' or 'RGB_565'.
 * <br>This function throws an exception if the conversion fails.
 *
 * @param mat is a valid input Mat object of types 'CV_8UC1', 'CV_8UC3' or 'CV_8UC4'.
 * @param bmp is a valid Bitmap object of the same size as the Mat and of type 'ARGB_8888' or 'RGB_565'.
 * @param premultiplyAlpha is a flag, that determines, whether the Mat needs to be converted to alpha premultiplied format (like Android keeps 'ARGB_8888' bitmaps); the flag is ignored for 'RGB_565' bitmaps.
 */
public static void matToBitmap(Mat mat, Bitmap bmp, boolean premultiplyAlpha) {
    if (mat == null)
        throw new IllegalArgumentException("mat == null");
    if (bmp == null)
        throw new IllegalArgumentException("bmp == null");
    nMatToBitmap2(mat.nativeObj, bmp, premultiplyAlpha);
}

cvtColor

但在图像的处理过程中却很少使用 YUV 格式,一般都需要转成 RGB 格式 或者灰度图格式进行转换。

opencv 提供了 cvtColor 函数,用于在图像的不同色彩空间进行转换。

函数被定义在 Imgproc.java 中,
public static void cvtColor(Mat src, Mat dst, int code) { …… }

  • src 原始图像
  • dst 输出图像
  • code 由某种格式转为另一个格式的枚举

在这里插入图片描述

代码示例

下面的代码从网络上加载一幅图片,然后使用 opencv 的颜色空间转换函数 cvtColor,将其由 “彩色” 转为 “灰度图”

布局文件

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/opencvBtn"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="OpenCV"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/resetBtn"
        app:layout_constraintStart_toStartOf="parent" />

    <Button
        android:id="@+id/resetBtn"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Reset"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/opencvBtn" />

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:layout_marginTop="30dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/imageView2"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:layout_marginTop="30dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/imageView1" />

    <ImageView
        android:id="@+id/imageView3"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:layout_marginTop="30dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/imageView2" />
</androidx.constraintlayout.widget.ConstraintLayout>

在这里插入图片描述

完整代码如下

public class MainActivity extends AppCompatActivity {
    private String TAG = "CvtColor";
    private String imgUrl = "https://www.baidu.com/img/flexible/logo/pc/result.png";

    private ImageView imageView1;
    private ImageView imageView2;
    private ImageView imageView3;
    private Button opencvBtn;
    private Button resetBtn;
    private Bitmap bitmap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_gray);
        initView();
        resetImg();

		// OpenCVLoader.initDebug 是用来加载 OpenCV 的 so 库的,so库如果全部加载成功,则返回 true
        if (OpenCVLoader.initDebug()) {
			opencvBtn.setOnClickListener(v -> {
            	convert2Grey();
        	});
        	resetBtn.setOnClickListener(v -> {
            	resetImg();
        	});
        } else {
            Log.i(TAG, "OpenCVLoader.initDebug fail");
        }
    }

    private void initView() {
        imageView1 = findViewById(R.id.imageView1);
        imageView2 = findViewById(R.id.imageView2);
        imageView3 = findViewById(R.id.imageView3);
        opencvBtn = findViewById(R.id.opencvBtn);
        resetBtn = findViewById(R.id.resetBtn);
    }

    private void convert2Grey() {
        if(bitmap == null) return;

        // Mat是OpenCV的一种图像格式
        Mat src = new Mat();
        Mat dst1 = new Mat();
        Mat dst2 = new Mat();

		// 将 bitmap 进行深拷贝,格式为 ARGB,包含透明度通道
        Bitmap bitmap1 = bitmap.copy(Bitmap.Config.ARGB_8888, true),
        bitmap2 = bitmap.copy(Bitmap.Config.ARGB_8888, true),
        bitmap3 = bitmap.copy(Bitmap.Config.ARGB_8888, true);

		// 利用 OpenCV 的工具类,将 bitmap 转为 OpenCV 的 Mat 类
        Utils.bitmapToMat(bitmap1, src);
        // 使用 OpenCV 的 cvtColor 进行颜色空间转换,将 "红"、"蓝" 通道互换
        Imgproc.cvtColor(src, dst1, Imgproc.COLOR_RGBA2BGR);
        // 使用 OpenCV 的cvtColor 进行颜色空间转换,将 RGB 图转为灰度图
        Imgproc.cvtColor(src, dst2, Imgproc.COLOR_RGB2HSV);

		// 利用 OpenCV 的工具类,将 Mat 转为 bitmap
        Utils.matToBitmap(dst1, bitmap2);
        imageView2.setImageBitmap(bitmap2);
        Utils.matToBitmap(dst2, bitmap3);
        imageView3.setImageBitmap(bitmap3);

        src.release();
        dst1.release();
        dst2.release();
    }

	// 从网络中加载 bitmap 图像
    public void resetImg() {
        new Thread(() -> {
            HttpURLConnection httpURLConnection = null;
            try {
                URL url = new URL(imgUrl);
                httpURLConnection = ((HttpURLConnection)url.openConnection());
                int responseCode = httpURLConnection.getResponseCode();
                if (responseCode != 200) {
                    throw new IOException("图片文件不存在或路径错误,错误代码:" + responseCode);
                }
                bitmap = BitmapFactory.decodeStream(url.openStream());
                runOnUiThread(()-> {
                    imageView1.setImageBitmap(bitmap);
                    imageView2.setImageBitmap(null);
                    imageView3.setImageBitmap(null);
                });
            } catch (IOException e) {
                e.printStackTrace();
            }
            if(httpURLConnection != null) httpURLConnection.disconnect();
        }).start();
    }
}

在这里插入图片描述