将肿瘤区域提取,应用在camelyon16数据集中,具体如下图
漫水算法
https://stackoverflow.com/questions/11294859/how-to-define-the-markers-for-watershed-in-opencv/11438165#11438165
OpenCV 形状分析(上):计算轮廓中心
http://python.jobbole.com/85600/
用 Python 和 OpenCV 检测图片上的条形码
http://blog.jobbole.com/80448/
opencv3编程入门第6章
一定要看
根据轮廓画方框
http://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_contours/py_contour_features/py_contour_features.html#exercises
这里设计的是形态学部分:膨胀,腐蚀,开运算,闭运算
在opencv里经常是对灰度图像中的白色区域进行操作,顾名思义,dilate膨胀会使白色区域膨胀,erode腐蚀会减少白色区域.dilate是图像与一个核函数做求max运算,会使整体值更大,也就是更亮,erode则是求min运算,使整体值更小,也就是更暗,特别在黑白交界的地方,膨胀和腐蚀的现象非常直观
开运算=erode+dilate,先腐蚀,让黑色区域中的一些白色斑点消失,同时白色区域块也会暗一些,再通过膨胀补回来
闭运算=dilate+erode,先膨胀会将一些散落的白色小块通过扩增连接到一起,再腐蚀削减一点
个人觉得开运算和闭运算太固定了,不灵活,不如手动控制dilate和erode的次数高效.
因为需要在轮廓之外为黑色,不是简单的二值化就可以,需要先合理设置阈值二值化,然后通过腐蚀消除白色的噪声斑点,再通过膨胀适当扩张白色区域,以避免图像样本的损失,再找到轮廓,最后将轮廓之外的区域设为黑色
核心函数
img = cv2.threshold(blurred, thresh, 255, cv2.THRESH_BINARY_INV)[1]
中间的关键点是cv2.THRESH_BINARY_INV这个参数,因为原图中目标区域是比较暗的,周边背景很量,所以需要用这个参数,如果是目标区域量,周边暗就用cv2.THRESH_BINARY
整体函数如下
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2017-08-28T06:52:38.406Z
# @Author : CarryHJR
import cv2
def on_trace_bar_changed(args):
pass
img = cv2.imread('test.png')
cv2.namedWindow("real")
cv2.imshow("real", img)
cv2.namedWindow("Image")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
cv2.createTrackbar('thres', 'Image', 0, 255, on_trace_bar_changed)
while True:
cv2.imshow("Image", img)
thresh = cv2.getTrackbarPos('thres', 'Image')
img = cv2.threshold(blurred, thresh, 255, cv2.THRESH_BINARY_INV)[1]
# 键盘检测函数,0xFF是因为64位机器
# https: // stackoverflow.com / questions / 20539497 / opencv - python - waitkey d- dont - respond
k = cv2.waitKey(1) & 0xFF
# print k
if k == ord('q'):
break
cv2.destroyAllWindows()
经过测试后阈值设定在199比较合适
这个腐蚀和膨胀的次数和顺序得看自己手动调试了,写了个小文件,按e就进行erode,同时pring ‘erode’,便于统计,按d就就行dilate,按m恢复到二值图像
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2017-08-28T06:52:38.406Z
# @Author : CarryHJR
# @Link : https://github.com/CarryHJR
# @Version : $Id$
import cv2
# 真实图像
real = cv2.imread('test.png')
cv2.namedWindow("real")
cv2.imshow("real", real)
# 前景图像
cv2.namedWindow("Image")
gray = cv2.cvtColor(real, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
thresh = cv2.threshold(blurred, 199, 255, cv2.THRESH_BINARY_INV)[1]
cv2.imshow("Image", thresh)
cv2.namedWindow("My")
img = thresh
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
# 2次膨胀,3次腐蚀
while True:
cv2.imshow("My", img)
# 键盘检测函数,0xFF是因为64位机器
k = cv2.waitKey(1) & 0xFF
# print k
if k == ord('e'):
# 加上iterations是为了记住这个参数,不加也行
img = cv2.erode(img, kernel, iterations=1)
print 'erode'
if k == ord('d'):
img = cv2.dilate(img, kernel, iterations=1)
print 'dilate'
if k == ord('r'):
img = thresh
print 'return threshold image'
if k == ord('q'):
break
cv2.destroyAllWindows()
个人采用erode两次,dilate三次
这部分可以看看 http://blog.csdn.net/vicdd/article/details/60478975
对findContour讲解的非常详细
cnts = cv2.findContours(img.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0]
cv2.drawContours(real, cnts, -1, (0, 255, 0), 2)
cv2.imwrite('contours.png', real)
得到contours需要将轮廓以外的部分设为黑色,这个功能我搜索了很长时间,最后在这里发现了答案,关键是有个fillPoly函数我之前不知道
# 全黑
mask = np.zeros(real.shape).astype(real.dtype)
# 将contours里填充白色
color = [255, 255, 255]
cv2.fillPoly(mask, cnts, color)
# mask与real相与
result = cv2.bitwise_and(real, mask)
最后结果:
把代码整理一下
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2017-08-28T06:52:38.406Z
# @Author : CarryHJR
import numpy as np
import cv2
######################################################################
# 原始图像real
real = cv2.imread('test.png')
# cv2.namedWindow("real")
# cv2.imshow("real", real)
# 前景图像
# cv2.namedWindow("Image")
gray = cv2.cvtColor(real, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
img = cv2.threshold(blurred, 199, 255, cv2.THRESH_BINARY_INV)[1]
# cv2.imshow("Image", img)
# 2次腐蚀,3次膨胀
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
img = cv2.erode(img, kernel, iterations=2)
img = cv2.dilate(img, kernel, iterations=3)
cnts = cv2.findContours(img.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0]
# loop over the contours
# for c in cnts:
# cv2.drawContours(real, [c], -1, (0, 255, 0), 2)
# cv2.imshow("real", real)
# cv2.drawContours(real, cnts, -1, (0, 255, 0), 2)
# cv2.imwrite('contours.png', real
# https://stackoverflow.com/questions/37912928/fill-the-outside-of-contours-opencv
# 全黑
mask = np.zeros(real.shape).astype(real.dtype)
# 将contours里填充白色
color = [255, 255, 255]
cv2.fillPoly(mask, cnts, color)
# mask与real相与
result = cv2.bitwise_and(real, mask)
# 最后结果reult
cv2.imwrite("result.jpg", result)
###################################################################
这里的图片比较好,背景是全白,目标区域和背景区域的亮度差别很大,如果是像条形码那张图,就需要像文章里的那样进行sobel边缘检测了
中间的插图用python合成的
# 将两个图片并排显示
import matplotlib.pyplot as plt
import cv2
fig = plt.figure()
ax1 = plt.subplot(121)
img1 = cv2.imread('test.png')
ax1.imshow(img1)
ax1.set_title('input image')
ax1.axis('off')
ax2 = plt.subplot(122)
img2 = cv2.imread('2.png')
ax2.imshow(img2)
ax2.set_title('thresho image')
ax2.axis('off')
看到这的都是真爱,点赞私发参考文献中的5本书籍的百度云链接
发现这个固定阈值的方式泛化效果很差,
分水岭算法 主要适用于在前景分割之后对前景进一步分割,如多个硬币
http://www.jianshu.com/p/de81d6029235