虽然我认为这可以通过放置良好的二进制阈值来解决(如另一个答案中所述),但添加基本形态的附加级别应该使其对明显更脏的图像更加稳健。 (对不起,它是在 C++ 中)
我使用任意二进制阈值来演示这个概念,但如果可用,我建议使用基于统计的阈值(例如 otsu 方法)。
稍微解释一下代码:threshold将灰度图像转换为二进制。打开去除阈值 op 留下的外部噪声(任何小条、块或像素噪声)。闭合填充任何内部孔。 “打开”和“关闭”只是扩张和侵蚀组合的名称,以特定顺序实现预期效果,而不改变底层对象的大小/形状。
#include <stdio.h>
#include <opencv2/opencv.hpp>
#include <Windows.h>
#include <string>
using namespace cv;
int main(int argc, char** argv)
{
//C:/Local Software/voyDICOM/resources/images/oXsnC.jpg
std::string fileName = "C:/Local Software/voyDICOM/resources/images/oXsnC.jpg";
Mat tempImage = imread(fileName, cv::IMREAD_GRAYSCALE);
Mat bwImg;
//binary thresh (both of these work, otsu just gets a "smarter" threshold value rather than a hardcoded one)
cv::threshold(tempImage, bwImg, 150, 255, cv::THRESH_BINARY);
//cv::threshold(tempImage, bwImg, 0, 255, cv::THRESH_OTSU);
Mat openedImage;
//opening
cv::erode(bwImg, openedImage, cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(3, 3)), cv::Point(-1, -1), 2);
cv::dilate(openedImage, openedImage, cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(3, 3)), cv::Point(-1, -1), 2);
Mat closedImg;
//closing
cv::dilate(openedImage, closedImg, cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(3, 3)),cv::Point(-1,-1),5);
cv::erode(closedImg, closedImg, cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(3, 3)), cv::Point(-1, -1), 5);
namedWindow("Original", WINDOW_AUTOSIZE);
imshow("Original", tempImage);
namedWindow("Thresh", WINDOW_AUTOSIZE);
imshow("Thresh", bwImg);
namedWindow("Opened", WINDOW_AUTOSIZE);
imshow("Opened", openedImage);
namedWindow("Closed", WINDOW_AUTOSIZE);
imshow("Closed", closedImg);
waitKey(0);
system("pause");
return 0;
}
结果: