您可以使用Otsu算法动态阈值化图像。之后,您可以通过在OpenCV轮廓上绘制来填充任意空洞。我不确定U2Net在掩膜时可以返回什么值范围,但您也可以手动设置一个小的阈值值~50。
import cv2
import numpy as np
# load image
img = cv2.imread("mask.jpg", cv2.IMREAD_GRAYSCALE);
# otsu thresholding
_, mask = cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU);
# show
cv2.imshow("Mask", mask);
cv2.waitKey(0);
# close everything inside
contour, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE);
# get the biggest contour # returns _, contours, _ if using OpenCV 3
biggest_area = -1;
biggest = None;
for con in contour:
area = cv2.contourArea(con);
if biggest_area < area:
biggest_area = area;
biggest = con;
# fill in the contour
cv2.drawContours(mask, [biggest], -1, 255, -1);
# show
cv2.imshow("Filled Mask", mask);
cv2.waitKey(0);
#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;
}
使用最小值滤波器(例如MinFilter(8))= 结果1
然后对结果1
应用相同值的最大值滤波器(例如MaxFilter(8))= 结果2
result 2
作为原始掩码的掩码,方法如下:
...
//assumptions: white=255, black=0
threshold = 128;// you can change it to give more weight for white or black
for(... loop all the the pixels)
for(...)
{
if((OrginalMask[x,y] > threshold) && (result2[x,y] < threshold))
finalResult[x,y] = result2[x,y];//to remove unwanted artifacts outside the mask
else if ((OrginalMask[x,y] < threshold) && (result2[x,y] > threshold))
finalResult[x,y] = result2[x,y];//to make the mask completely white inside
else
finalResult[x,y] = OrginalMask[x,y];
}
一种解决方案是使用二值阈值处理,使不是白色但接近白色的部分变为白色。
另一种解决方案是使用形态学运算符(morphological operators)来填充或删除任何间隙(请注意,形态学运算符如腐蚀/膨胀可能需要对图像进行多次处理才能获得所需的结果)。
两者都可以使用。
为了自动/自适应计算所需的阈值,我想到的过程类似于使用适当的大型结构元素执行形态学操作。也就是说,扫描图像的区域并使用该区域像素的多数表决来着色该区域的所有像素。