在WSI中大部分都是背景区域,所以一般都会提取组织区域,形成组织掩码图。一般都是基于HSV颜色空间的方法来提取前景的,这里记录一下基于面积的方式来提取组织区域,主要使用的包是opencv。
源码
get_infill_tissue_matrix
用于获取组织mask图。
参数:
im:RGB图像(传入这个格式的图像主要是因为从openslide获取的图像格式是RGBA)
binary_threshold:二值化阈值
contour_area_threshold:最小面积比例
mode:opencv的findContours函数中寻找轮廓的模式
erode_iter:腐蚀次数
dilate_iter:膨胀次数
def get_infill_tissue_matrix(im, binary_threshold=210, contour_area_threshold=0.001,
mode=0, erode_iter=0, dilate_iter=0):
im = cv2.cvtColor(im, cv2.COLOR_RGB2BGR)
cnts = find_tissue_contours(im, is_erode_dilate=True, thresh=binary_threshold, mode=mode, erode_iter=erode_iter, dilate_iter=dilate_iter)
# 获取当前区域面积
area_cnt_list = list(map(cv2.contourArea, cnts))
# 获取当前区域占矩阵区域的面积比率
rate_area = [area / im.size for area in area_cnt_list]
# 过滤当前区域占矩阵区域的面积比率小于等于filter_rate的区域
tissue_cnts = [cnts[i] for i in range(len(cnts)) if rate_area[i] >contour_area_threshold]
# initialize mask to zero
mask = np.zeros((im.shape[0], im.shape[1])).astype(im.dtype)
color = [1]
mask = cv2.fillPoly(mask, tissue_cnts, color)
return mask
find_contours
获取轮廓坐标
参数:
im:RGB图像
is_erode_dilate:是否进行腐蚀和膨胀
mode:opencv的findContours函数中寻找轮廓的模式
thresh:二值化阈值
erode_iter:腐蚀次数
dilate_iter:膨胀次数
def find_contours(im, is_erode_dilate = False, mode=3, thresh=210, erode_iter=3, dilate_iter=3):
"""
get the points of contours
:param morphology: output of erode_dilate
:return:
"""
morphology = gray_binary(im, thresh=thresh, show=False)
if is_erode_dilate:
morphology = erode_dilate(morphology, erode_iter=erode_iter, dilate_iter=dilate_iter, show=False)
morphology = cv2.copyMakeBorder(morphology,0,0,0,0,cv2.BORDER_CONSTANT,value=0)
if not isinstance(mode,int):
mode_dic = {'RETR_EXTERNAL':0, 'RETR_LIST':1, 'RETR_CCOMP':2, 'RETR_TREE':3, 'RETR_FLOODFILL':4}
mode = mode_dic[mode]
if cv2.__version__[0]=='4':
cnts, _ = cv2.findContours(morphology.copy(),mode=mode,method=cv2.CHAIN_APPROX_SIMPLE)
else:
_, cnts , _ = cv2.findContours(morphology.copy(),mode=mode,method=cv2.CHAIN_APPROX_SIMPLE)
return cnts
gray_binary
获取灰度图
参数:
im:RGB图像
thresh:二值化阈值
show:是否展示灰度图结果
def gray_binary(im, thresh=230, show=False):
"""
convert image to binary image after gray scale
:param thresh: binary image threshold
:param show: show the binary dynamically
:return:
"""
if len(im.shape) >2 :
gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
else:
gray = np.uint8(im)
blurred = cv2.GaussianBlur(gray, (5,5), 0)
binary = cv2.threshold(blurred, thresh, 255, cv2.THRESH_BINARY_INV)[1]
def binary_theshold(threshold):
binary = cv2.threshold(blurred, threshold, 255, cv2.THRESH_BINARY_INV)[1]
cv2.imshow("binary image", binary)
if show:
window_name = "binary image"
cv2.namedWindow(window_name, 0)
cv2.resizeWindow(window_name, 640, 480)
cv2.createTrackbar("binary threshold", window_name, 100, 255, binary_theshold)
binary_theshold(100)
if cv2.waitKey(0) == 27:
cv2.destroyAllWindows()
return binary
erode_dilate
腐蚀膨胀处理
binary:灰度图
erode_iter:腐蚀次数
dilate_iter:膨胀次数
kernel_size:核大小
show:是否展示腐蚀膨胀结果
def erode_dilate(binary, erode_iter=6, dilate_iter=9, kernel_size=(5, 5), show=False):
"""
errode and dilate the binary image dynamically
:param binary: binary image, output of gray_binary
:param erode_iter: erode iteration, default 2
:param dilate_iter: dilate iteration, default 3
:return:
"""
morphology = binary
if show:
window_name = "errode dilate"
cv2.namedWindow(window_name, 0)
cv2.resizeWindow(window_name, 640, 480)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, kernel_size)
while show:
cv2.imshow(window_name, morphology)
key = cv2.waitKey(1) & 0xFF
if ord('e') == key:
morphology = cv2.erode(morphology, kernel, iterations=1)
print('erode')
if ord('d') == key:
morphology = cv2.dilate(morphology, kernel, iterations=1)
print('dilate')
if ord('r') == key:
morphology = binary
print('reset threshold image')
if ord('q') == key:
break
cv2.destroyAllWindows()
morphology = cv2.erode(morphology, kernel, iterations=erode_iter)
morphology = cv2.dilate(morphology, kernel, iterations=dilate_iter)
return morphology