扫描图片数据
应用情景
图像数据扫描的难点
在上一篇文章《基本图像容器——Mat》中,已经详细描述了OpenCV储存图像数据的形式(图像的每个像素储存为一个矩阵中的数据项,矩阵的每个数据项包括各个颜色通道的值,如RGB3通道包含红、绿、蓝共3个通道的颜色值)。所以,矩阵的值的列数为矩阵的列数乘以颜色通道数,如下图所示,OpenCV默认的BGR格式的数据有3个颜色通道,所以实际有m*3列数值;行数则不变:
如果我们使用uchar(8bits)类型去储存每个像素的值,那么像素的每个颜色通道可以有256个可能的值( 2 8 = 256 2^8=256 28=256),这样的话如果是3通道的数据,那每个数据项就有( 25 6 3 = 16 , 777 , 216 256^3=16,777,216 2563=16,777,216)种可能的颜色值了。如果矩阵很大,那就会给算法的执行带来很大压力。
颜色空间缩减(color space reduction)
为了减轻扫描图像数据的算法压力,可以将现有的颜色值除以某个值,从而缩小颜色值的值域。例如,将所有0-9的颜色值都用0替代,将所有10-19的颜色值都用10替代,依此类推。用数学公式来表示:
I n e w = ( I o l d 10 ) ∗ 10 I_{new} = ( \frac{I_{old}} {10})*10 Inew=(10Iold)∗10
- I n e w I_{new} Inew:缩减之后的颜色值,以下简称缩减值
- I o l d I_{old} Iold:缩减之前的原始颜色值,以下简称原始值
推而广之,如果想要应用其他的缩减率,比如2,也就是说,0-1都用0来代替,2-3都用1来代替,那除数就会变成2;将这个除数用 d d d来表示,并称之为缩减因子,则公式会变成:
I n e w = ( I o l d d ) ∗ d I_{new} = ( \frac{I_{old}} {d})*d Inew=(dIold)∗d - I n e w I_{new} Inew:缩减之后的颜色值,以下简称缩减值
- I o l d I_{old} Iold:缩减之前的原始颜色值,以下简称原始值
- d d d:缩减因子
注意:uchar类型被整数除了之后,得出的结果依然是uchar类型
但是对每个颜色值都执行上述的除法和乘法运算,仍然会消耗很多算力。然而,由于每个颜色值的值域是有限的,比如uchar类型是[0, 256],所以如果能直接计算出所有可能的缩减结果,并进行赋值运算,会节省很多算力。所以,就产生了“查询表”
查询表
查询表就是储存与原始值一一对应的缩减值的数组(一维或多维)。一旦数据类型确定,查询表的大小就确定不变了。比如,uchar类型的查询表就只有256个缩减值,因为原始值总共就只有256种可能。然后运用这个查询表将每个原始值都替换成查询表中对应的值,就不必对每一个原始值都进行缩减计算了,而只是简单的查询和赋值,这样就能节省算力。而且,原始值越多,节省效果越好。这就是查询表的优势。
扫描算法
该实例将3种算法放在一个main函数中,main函数接收一个参数数组argv[],其中有可以有3个或4个参数:
- 默认参数:程序名(调试时不需要用户指定)
- 图片文件路径
- 缩减因子:即上述公式中的 d d d
- 图片读取格式:以灰度格式读取,则传递“G”;如不指定该参数,则默认采用BGR格式读取
静态函数help()详细描述了该方法的使用:
static void help()
{
std::cout
<< "\n--------------------------------------------------------------------------" << endl
<< "这个程序展示如何在OpenCV中扫描图片(cv::Mat)"
<< "根据输入图片路径以及缩减因子(大于0的整数)" << endl
<< "这个程序分别使用了C风格方法、迭代器方法、坐标方法和LUT方法进行扫描" << endl
<< "参数输入:" << endl
<< "程序名 <图片路径> <缩减因子> [G]" << endl
<< "如果加了参数G,则使用灰度格式读入图片" << endl
<< "--------------------------------------------------------------------------" << endl