图像在水平方向上的投影的定义为:每行的灰度累计值,这些值串起来构成了水平方向上的投影曲线;
图像在垂直方向上的投影的定义为:每列的灰度累计值,这些值串起来构成了垂直方向上的投影曲线。
在OpenCV中,可以使用函数reduce()快速求出图像在水平方向和垂直方向的投影,其C++原型如下:
void cv::reduce(InputArray src,
OutputArray dst,
int dim,
int rtype,
int dtype = -1 )
这个函数要求输入矩阵是二维矩阵。
参数dim表示是将矩阵减至一行还是一列。
当dim=0时,将矩阵减至一行;
当dim=1,将矩阵减至一列。
参数rtype表示在对矩阵作缩减操作的运算类型,有以下几种取值:
最后一个参数 dtype取负值时,输出向量的数据类型和输入矩阵一样。
图像在水平方向上和在垂直方向上的投影是一维离散序列,我们可以通过差分运算来找寻序列的下降沿、上升沿、极大值(波峰)、极小值(波谷),详细原理见我的另一篇博文,链接如下:
https://www.hhai.cc/thread-231-1-1.html
查找离散序列下降沿、上升沿、极大值(波峰)、极小值(波谷)的用途:
常用于图像分割、字符检测及提取、文本分类等领域。
为什么有这样的作用?因为查到上面这些特殊点就相当于是找到了字符的大概位置,大家可结合下面这个代码的运行结果进行理解。
下面这个代码通过寻找“图像在垂直方向上的投影”的下降沿,从而定位出了图像中字符的大致位置。
代码中用到的图的下载链接:
链接:https://pan.baidu.com/s/1WQtLtyPBPZgM1EpPOxXoLg 提取码:44fd
//出处:昊虹AI笔记网(hhai.cc)
//用心记录计算机视觉和AI技术
//博主微信/QQ 2487872782
//QQ群 271891601
//欢迎技术交流与咨询
//OpenCV版本 OpenCV3.0
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
// 计算图像垂直方向投影的下降沿,并标注在原图中
void findPeak(cv::Mat srcImage, vector<int>& resultVec)
{
cv::Mat verMat;
cv::Mat resMat = srcImage.clone();
// 阈值化操作
int thresh = 130;
int threshType = 0;
// 预设最大值
const int maxVal = 255;
// 通过预设的阈值对图像进行二值化处理
cv::threshold( srcImage, srcImage, thresh, maxVal , threshType );
// 计算垂直投影
srcImage.convertTo(srcImage, CV_32FC1);
cv::reduce(srcImage, verMat, 0, CV_REDUCE_SUM);
//std::cout << verMat << std::endl;
// 求序列的一阶前向差分
float* iptr = (float*)verMat.data;
vector<int> first_order_diff(verMat.cols-1 , 0);
for(int i=0;i<=verMat.cols-2;++i, ++iptr) //最后一个点的一阶前向差分值不存在,所以这里的范围是“0到verMat.cols-2”
{
first_order_diff[i]=*(iptr + 1) - *iptr;;
}
//找出序列的下降沿位置
for(int i =0;i<=verMat.cols-3;++i)
{
if(first_order_diff[i]==0&&first_order_diff[i+1]<0)
{
resultVec.push_back(i+1);
}
}
// 输出下降沿位置,并绘制在原图中
std::cout <<"下降沿位置 ";
for(int i = 0; i< resultVec.size(); i++)
{
std::cout << resultVec[i] << " ";
for (int ii = 0; ii < resMat.rows; ++ii)
{
resMat.at<uchar>(ii,resultVec[i])=255;
}
}
imshow("resMat", resMat);
}
int main()
{
cv::Mat image = imread("E:/material/images/P0035-Image-projection-02.jpg",0);
if(!image.data)
return 0;
imshow("Image", image);
vector<int> resultVec;
findPeak(image, resultVec);
cv::waitKey(0);
return 0;
}
运行结果如下图所示: