note
步骤:
1.滤波(使用高斯核对原图卷积)
2.取梯度(使用sobel核对步骤1之后的图卷积得到x,y两个方向的梯度分量)
3.合成梯度图(x,y两个方向的梯度分量相加)
4.取梯度平方和矩阵(x*x + y*y)
5.取八邻域掩膜(3x3矩阵)
6.根据掩膜,合成梯度图,梯度平方和矩阵确定轮廓图中锚点位置的灰度值(保留梯度平方和最大的锚点灰度值为合成梯度图的对应点灰度值)
7.二值化(otsu)
8.膨胀(十字架膨胀)
code
// 轮廓提取
void MyContour(Mat& src, Mat& res) {
Mat sobelX, sobelY; // sobel梯度卷积核
GetSobelMat(sobelX, sobelY);
// 1.高斯滤波
GaussianBlur(src, res, Size(3,3), 1);
// 2.soble算子求梯度
Mat gradX, gradY;
filter2D(res, gradX, src.type(), sobelX);
filter2D(res, gradY, src.type(), sobelY);
Mat grad; // 梯度合成图
add(gradX, gradY, grad);
// 3.求梯度幅值矩阵
gradX.convertTo(gradX, CV_64FC1);
gradY.convertTo(gradY, CV_64FC1);
Mat gradAmplitude(gradX.rows, gradX.cols, gradX.type());
multiply(gradX, gradX, gradX);
multiply(gradY, gradY, gradY);
add(gradX, gradY, gradAmplitude);
// 4.从梯度合成图筛选出轮廓(若锚点位置梯度幅值最大则保留)
Mat contour(src.rows, src.cols, src.type(), Scalar(0)); // 轮廓
// Mat neiberHood = (Mat_<double>(3,3) << 1,1,1,1,0,1,1,1,1); // 8邻域矩阵,掩膜
Rect roi;
roi.width = 3;
roi.height = 3;
int anchor = roi.width / 2;
for (int i = 0; i + roi.height <= gradAmplitude.rows; ++i) {
for (int j = 0; j + roi.width <= gradAmplitude.cols; ++j) {
roi.x = j;
roi.y = i;
double max = 0;
Mat tmp = gradAmplitude(roi);
int x = 0;
int y = 0;
for (int p = 0; p < tmp.rows; p++) {
for (int q = 0; q < tmp.cols; q++) {
if (max < tmp.at<double>(p,q)) {
max = tmp.at<double>(p,q);
x = q;
y = p;
}
}
}
if (x == anchor) {
contour.at<uchar>(i,j) = grad.at<uchar>(i,j);
}
}
}
// 5.膨胀
Mat dilateKernel = (Mat_<uchar>(3,3) << 0,255,0,255,255,255,0,255,0); // 十字架膨胀
dilate(contour, contour, dilateKernel);
contour.copyTo(res);
res.convertTo(res, CV_8UC1);
threshold(res, res, 128, 255, THRESH_BINARY|THRESH_OTSU);
}
test