文章目录
1 像素与颜色空间
存储像素值需要指定颜色空间和数据类型,其中颜色空间是指一个给定的颜色,如何组合颜色元素,及各元素如何编码。最简单的颜色空间时灰度空间,只处理黑色和白色,对它们进行编码组合,便可以产生不同程度的灰色。
对于彩色,则有多种不同类型的颜色空间,但不论哪种类型都是把颜色分成三个或四个基本元素,通过组合基本元素来表示和构成颜色空间。
例如 RGB 颜色空间就是最常用的一种颜色空间,它的基本元素是——红色,绿色,蓝色,alpha。
HSV 和 HLS 把颜色分解成色调、饱和度和亮度/明度。这种方式,更符合人类的视觉认知,比如调整图片的亮度,只需要操作 ‘亮度’ 元素的值就可以了,但是 RGB 方式,就需要三个元素一起调整。
CIE Lab 是一种在感知上均匀的颜色空间,它适合用来度量两个颜色之间的距离。
每个组成元素都有其自己的定义域,在定义域取决于其数据类型,如何存储一个元素决定了我们在其定义域上能够控制的精度。最小的数据类型是 char,占一个字节或者 8 位,可以是有符号或无符号,共 256 个值。使用 float 或 double 则能给出更加精细的颜色分辨能力。但是,增加像素元素的尺寸也会增加图像所占的内存空间。
2 图像在内存之中的存储方式
图像数据在内存中是以矩阵的形式存在,而矩阵的大小取决于所用的颜色空间模型和通道数。
对于多通道图像来说,矩阵中的列会包含多个子列,其子列个数与通道数相等。
单通道:
Mat mat = Mat.eye(new Size(3,4), CV_8UC1);
System.out.println(mat.dump());
双通道:
Mat mat = Mat.eye(new Size(3,4), CV_8UC2);
System.out.println(mat.dump());
三通道
Mat mat = Mat.eye(new Size(3,4), CV_8UC2);
System.out.println(mat.dump());
3 通过 Mat.ptr() 方法遍历像素
org.bytedeco.opencv.opencv_core.Mat
@Test
public void test() {
Mat mat = new Mat(new Size(3,3), CV_8UC3, new Scalar(5,4,3,0));
System.out.println("rows = " + mat.rows());
System.out.println("cols = " + mat.cols());
System.out.println("channels = " + mat.channels());
System.out.println("total = " + mat.total());
for (int i = 0; i < mat.rows(); i++) {
for (int j = 0; j < mat.cols(); j++) {
byte[] values = new byte[mat.channels()];
mat.ptr(i,j).get(values);
System.out.println(Arrays.toString(values));
}
System.out.println();
}
}
输出:
org.opencv.core.Mat
@Test
public void test() {
Mat mat = new Mat(new Size(3,3), CV_8UC3, new Scalar(5,4,3,0));
System.out.println("rows = " + mat.rows());
System.out.println("cols = " + mat.cols());
System.out.println("channels = " + mat.channels());
System.out.println("total = " + mat.total());
for (int i = 0; i < mat.rows(); i++) {
for (int j = 0; j < mat.cols(); j++) {
double[] values = mat.get(i,j);
System.out.print(Arrays.toString(values) + " ");
}
System.out.println();
}
}
输出:
4 LUT : 查找表映射
LUT 即 look up table 操作,OpenCV 官方文档推荐使用该方法进行像素操作,它用于批量进行图像元素查找、扫描、和操作。
函数原型:
/** 执行数组的查找表转换
@param src 包含 8 位元素的输入数组。
@param lut 256个元素的查找表;如果是多通道输入阵列,该表要么只有一个通道(在这种情况下,所有通道使用相同的映射),要么具有与输入数组相同的通道数。
@param dst 输出阵列,大小和通道数与 src 相同,深度与 lut 相同。
*/
@Namespace("cv") public static native void LUT(@ByVal Mat src, @ByVal Mat lut, @ByVal Mat dst);
函数 LUT 用查找表中的值对输入元素值映射,并填充输出数组。条目的索引取自输入数组。也就是说,该函数按如下方式处理src的每个元素:
其中:
- 当 src 元素类型为 CV_8U 时,d = 0;
- 当 src 元素类型为 CV_8S 时,d = 128;
下面通过示例来说明 LUT 的映射过程:
代码:
public void lutTest() {
// 1. 构建 5x5 单通道矩阵,元素初始值为 8
Mat mat = new Mat(new Size(5,5), CV_8U, new Scalar(8));
mat.row(3).setTo(new Mat(new Size(1, 1), CV_8U, new Scalar(9))); // 将第四行(索引为3)元素值,改为 9
mat.col(3).setTo(new Mat(new Size(1, 1), CV_8U, new Scalar(7))); // 将第四列元素值,改为 7
// 2. 构建 LUT 映射表: size = 256x1, 元素初始值为 1
Mat lut = new Mat(new Size(256,1), CV_8U, new Scalar(1));
lut.col(7).setTo(new Mat(new Size(1, 1), CV_8U, new Scalar(77))); // lut[7] == 77
lut.col(8).setTo(new Mat(new Size(1, 1), CV_8U, new Scalar(88))); // lut[8] == 88
lut.col(9).setTo(new Mat(new Size(1, 1), CV_8U, new Scalar(99))); // lut[9] == 99
Mat dst = new Mat();
// 3. 调用 LUT 函数,执行映射
opencv_core.LUT(mat, lut, dst);
for (int i = 0; i < mat.rows(); i++) {
for (int j = 0; j < mat.cols(); j++) {
byte[] values = new byte[mat.channels()];
dst.ptr(i,j).get(values);
System.out.print(Arrays.toString(values) + " ");
}
System.out.println();
}
}
mat 矩阵的内容:
dst 矩阵的内容: