参考了一篇论文的思想,主要思路就是通过分块求取各窗口内的梯度方向,利用密度阈值过滤。缺点是要手动定义窗口大小和密度值。
具体代码如下:
//窗口内计算边缘密度特征
struct EdgeDensity
{
int num; //边缘点数
float orient; //边缘梯度方向
};
//排序
bool pairSortFun3(EdgeDensity elem1, EdgeDensity elem2)
{
return elem1.num > elem2.num; //降序
}
//求取主梯度方向
float GetCodeRegionOrient(Mat MatGray, int w, int h, int thre_gradient_density)
{
//滤波
blur(MatGray, MatGray, Size(3, 3));
//求梯度
cv::Mat magX = cv::Mat(MatGray.rows, MatGray.cols, CV_32F);
cv::Mat magY = cv::Mat(MatGray.rows, MatGray.cols, CV_32F);
cv::Sobel(MatGray, magX, CV_32F, 1, 0, 3);
cv::Sobel(MatGray, magY, CV_32F, 0, 1, 3);
Mat magnitude = Mat(MatGray.size(), CV_32FC1); // 梯度幅值
Mat slopes = Mat(MatGray.size(), CV_32FC1); // 梯度方向
int i, j;
float v1, v2, v, direc;
for (i = 0; i < MatGray.rows; i++)
{
for (j = 0; j < MatGray.cols; j++)
{
v1 = magX.at<float>(i, j);
v2 = magY.at<float>(i, j);
v = sqrt(v1*v1 + v2*v2); //求出梯度
magnitude.at<float>(i, j) = v;
if (v1 == 0)
{
if (v2 != 0)
{
direc = 90;
}
else
{
direc = 255;
}
}
else
{
direc = atan(v2 / v1)* (180 / CV_PI);
if (direc < 0)
{
direc += 180;
}
int t = direc / 2;
if (direc - t * 2 < (t + 1) * 2 - direc)
{
direc = t * 2;
}
else
{
direc = (t + 1) * 2;
}
}
slopes.at<float>(i, j) = direc;
}
}
Mat magtmp;
magnitude.convertTo(magnitude, CV_8U);
cv::threshold(magnitude, magnitude, 30, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
magnitude.convertTo(magtmp, CV_32F);
//矩形窗口
Mat dstM = Mat::zeros(magnitude.size(), CV_8UC1);
for (i = 1; i < magnitude.rows - 1; i++)
{
for (j = 1; j < magnitude.cols - 1; j++)
{
for (int m = -1; m <= 1; m++) // 3*3
{
for (int n = -1; n <= 1; n++)
{
if ((magtmp.at<float>(i, j) == 255) && ((slopes.at<float>(i + m, j + n) == slopes.at<float>(i, j)) && slopes.at<float>(i, j) != 255))
{
if (m != 0 && n != 0)
{
dstM.at<uchar>(i + m, j + n) = 255;
dstM.at<uchar>(i, j) = 255;
}
}
}
}
}
}
//分块计算主方向
vector<EdgeDensity> dirVec;
dirVec.clear();
for (i = h; i < dstM.rows - h;)
{
for (j = w; j < dstM.cols - w;)
{
//计算块的主方向
vector<float> d;
d.clear();
for (int m = -h; m <= h; m++)
{
for (int n = -w; n <= w; n++)
{
if (dstM.at<uchar>(i + m, j + n) == 255)
{
float ori = slopes.at<float>(i + m, j + n);
d.push_back(ori);
}
}
}
if (d.size() < 2)
{
j += (2 * w + 1);
continue;
}
std::sort(d.begin(), d.end());
int index = 0, seqIndex = 0;
int index_begin, seqIndex_begin = 0;
for (int q = 0; q < d.size(); q++)
{
if (q == d.size() - 1)
{
if (d[q] == d[q - 1] && index != 0)
{
if (index > seqIndex)
{
seqIndex = index;
seqIndex_begin = index_begin;
}
}
break;
}
if (d[q + 1] == d[q])
{
if (index == 0)
{
index_begin = q;
}
index++;
}
else
{
if (index > seqIndex)
{
seqIndex = index;
seqIndex_begin = index_begin;
}
index = 0;
}
}
int len = d.size();
if (seqIndex_begin < len)
{
EdgeDensity ey;
ey.num = seqIndex + 1;
ey.orient = d[seqIndex_begin];
dirVec.push_back(ey);
}
j += (2 * w + 1);
}
i += (2 * h + 1);
}
//找出主方向
vector<float> orientVec;
orientVec.clear();
std::sort(dirVec.begin(), dirVec.end(), pairSortFun3);
for (int c = 0; c < dirVec.size(); c++)
{
if (dirVec[c].num > thre_gradient_density) // 剔除密度低的块
{
orientVec.push_back(dirVec[c].orient);
}
}
if (orientVec.size() < 1)
{
return -1;
}
std::sort(orientVec.begin(), orientVec.end());
//标记seqIndex_begin 为主方向的索引
int cnt = 0, seqCnt = 0;
int index_begin, seqIndex_begin = 0;
if (orientVec.size() == 1)
{
seqIndex_begin = 0;
seqCnt = 1;
}
else
{
for (int q = 0; q < orientVec.size(); q++)
{
if (q == orientVec.size() - 1)
{
if (orientVec[q] == orientVec[q - 1] && cnt != 0)
{
if (cnt > seqCnt)
{
seqCnt = cnt;
seqIndex_begin = index_begin;
}
}
break;
}
if (orientVec[q + 1] == orientVec[q])
{
if (cnt == 0)
{
index_begin = q;
}
cnt++;
}
else
{
if (cnt > seqCnt)
{
seqCnt = cnt;
seqIndex_begin = index_begin;
}
cnt = 0;
}
}
}
return orientVec[seqIndex_begin];
}
效果图如下: