我们通过一个例子来简单说明如何遍历图像:减少图像中的颜色数目 CV_8UC3的三通道单字节彩色图像的颜色空间为256*256*256>1600万个,为了降低分析的复杂度,降低颜色数目是有用的。一个简单的算法是把RGB空间划分为同等大小的格子,每个纬度的颜色降低为原来的1/8,那么总的颜色数为32*32*32,原始图像的每个颜色都替换为所在格子中心对应的颜色。
算法实现:
1、采用指针遍历图像,这种操作传入的是源图像的指针,直接对源图像进行修改了
void
colorReduce1(cv::
Mat
&
image
,
int
div
= 64)
{
int
nl =
image
.rows;//行数
int
nc =
image
.cols;//列数
for
(
int
j = 0; j < nl; j++)
{
for
(
int
i = 0; i < nc; i++)
{
image
.at<cv::
Vec3b
>(j, i)
[
0
]
=
image
.at<cv::
Vec3b
>(j, i)
[
0
]
/
div
*
div
+
div
/ 2;
image
.at<cv::
Vec3b
>(j, i)
[
1
]
=
image
.at<cv::
Vec3b
>(j, i)
[
1
]
/
div
*
div
+
div
/ 2;
image
.at<cv::
Vec3b
>(j, i)
[
2
]
=
image
.at<cv::
Vec3b
>(j, i)
[
2
]
/
div
*
div
+
div
/ 2;
}
}
}
为了进行深拷贝,我们可以用
Mat
imageClone = image.clone();
然后把imageClone的引用传入函数,就不会直接对源图像操作了。
当然我们也可以对一个矩阵重新分配确保和原图片一样:
result.create(image.rows, image.cols, image.type());
我们有另一个版本:
void
colorReduce2(
const
cv::
Mat
&
image
, cv::
Mat
&
result
,
int
div
= 64)
{
int
nl =
image
.rows;
int
nc =
image
.cols*
image
.channels();
for
(
int
j = 0; j < nl; j++)
{
const
uchar
* data_in =
image
.ptr<
uchar
>(j);
uchar
* data_out =
result
.ptr<
uchar
>(j);
for
(
int
i = 0; i < nc; i++)
{
data_out[i] = data_in[i] /
div
*
div
+
div
/ 2;
}
}
}
当然为了高效,图像有可能在行尾扩大了若干个像素,当没有填补时,可以视为W*H的一维数组。通过
image.isContinuous()来判断
void
colorReduce3(cv::
Mat
&
image
,
int
div
= 64)
{
//指针版本
int
nl =
image
.rows;
int
nc =
image
.cols*
image
.channels();
if
(
image
.isContinuous())
{
//没有进行填补像素
nc = nc*nl;
nl = 1;
//image.reshape(1,image.cols*image.rows);
}
for
(
int
j = 0; j < nl; j++)
{
uchar
* data =
image
.ptr<
uchar
>(j);//获取每一行行首的地址
for
(
int
i = 0; i < nc; i++)
{
data[i] = data[i] /
div
*
div
+
div
/ 2;
}
}
}
当然上面注释部分image.reshape(通道数,行数)也可以重新改变矩阵的维数
2、使用迭代器遍历图像
void
colorReduce4(cv::
Mat
&
image
, cv::
Mat
&
result
,
int
div
= 64)
{
//迭代器版本1
cv::
Mat_
<cv::
Vec3b
>::
iterator
it =
image
.begin<cv::
Vec3b
>();
cv::
Mat_
<cv::
Vec3b
>::
iterator
itend =
image
.end<cv::
Vec3b
>();
//迭代器版本2
cv::
MatIterator_
<cv::
Vec3b
> it1 =
image
.begin<cv::
Vec3b
>();
cv::
MatIterator_
<cv::
Vec3b
> it1end =
image
.end<cv::
Vec3b
>();
cv::
MatIterator_
<cv::
Vec3b
> out=
result
.begin<cv::
Vec3b
>();
for
(; it1
!=
it1end;
++
it1)
{
(
*
out)
[
0
]
= (
*
it)
[
0
]
/
div
*
div
+
div
/ 2;
(
*
out)
[
1
]
= (
*
it)
[
1
]
/
div
*
div
+
div
/ 2;
(
*
out)
[
2
]
= (
*
it)
[
2
]
/
div
*
div
+
div
/ 2;
}
}
注意一般图像作为输入时,不让修改,所以回家const修饰,此时迭代器版本也要修改
void
colorReduce5(
const
cv::
Mat
&
image
, cv::
Mat
&
result
,
int
div
= 64)
{
//迭代器版本1
cv::
Mat_
<cv::
Vec3b
>::
const_iterator
it =
image
.begin<cv::
Vec3b
>();
cv::
Mat_
<cv::
Vec3b
>::
const_iterator
itend =
image
.end<cv::
Vec3b
>();
//迭代器版本2
cv::
MatConstIterator_
<cv::
Vec3b
> it1 =
image
.begin<cv::
Vec3b
>();
cv::
MatConstIterator_
<cv::
Vec3b
> it1end =
image
.end<cv::
Vec3b
>();
cv::
MatIterator_
<cv::
Vec3b
> out =
result
.begin<cv::
Vec3b
>();
for
(; it1
!=
it1end;
++
it1)
{
(
*
out)
[
0
]
= (
*
it)
[
0
]
/
div
*
div
+
div
/ 2;
(
*
out)
[
1
]
= (
*
it)
[
1
]
/
div
*
div
+
div
/ 2;
(
*
out)
[
2
]
= (
*
it)
[
2
]
/
div
*
div
+
div
/ 2;
}
}
扩展:统计函数( 或一段代码)的耗费时间的方法
double
duration;
duration =
static_cast
<
double
>(cv::getTickCount());
{
//被测试的函数
}
duration =
static_cast
<
double
>(cv::getTickCount()) - duration;
duration /= cv::getTickFrequency();
//运行时间,以毫秒为单位