opencv inpaint
原文链接: opencv inpaint
图像修复
import cv2
import numpy as np
import os
def deal(img_path,out_path,img_name):
print("deal",img_path)
img = cv2.imread(img_path)
hight, width, depth = img.shape[0:3]
# 图片二值化处理,把[240, 240, 240]~[255, 255, 255]以外的颜色变成0
thresh = cv2.inRange(img, np.array([0, 0, 130]), np.array([50, 50, 255]))
# 创建形状和尺寸的结构元素
# kernel = np.ones((7, 7), np.uint8)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
# 膨胀待修复区域
hi_mask = cv2.dilate(thresh, kernel, iterations=1)
specular = cv2.inpaint(img, hi_mask, 5, flags=cv2.INPAINT_TELEA)
out_path=os.path.join(out_dir,img_name)
cv2.imwrite(out_path,specular)
# cv2.namedWindow("Image", 0)
# cv2.resizeWindow("Image", int(width / 2), int(hight / 2))
# cv2.imshow("MASK", thresh)
#
# cv2.namedWindow("newImage", 0)
# cv2.resizeWindow("newImage", int(width / 2), int(hight / 2))
# cv2.imshow("newImage", specular)
# cv2.waitKey(0)
# cv2.destroyAllWindows()
if __name__ == '__main__':
"""注意:图片加载和保存路径不要出现任何汉字"""
img_dir='.' #图片存储路径 ,注意分隔符用‘/’
out_dir='out' #修复后图片保存路径,必须和img_dir不同
img_names=os.listdir(img_dir)
# print(img_dir)
for item in img_names:
img_path=os.path.join(img_dir,item)
# print(os.path.splitext(item)[1])
if os.path.splitext(item)[1] != '.jpg':
continue
print(item)
deal(img_path,out_dir,item)
print('All Work Done!')
https://github.com/yoyoyohamapi/matching/tree/master/scripts
# coding: utf-8
import cv2
import os
import numpy as np
def _noop(*args):
return None
def _findMaxContour(edges):
"""
获得边缘灰度图的最大轮廓
Args:
edges 边缘图像
Return:
maxContour zui'da
"""
contours, hierachy = cv2.findContours(
edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[-2:]
if len(contours) <= 0:
return None
maxContour = contours[0]
for contour in contours:
if(cv2.contourArea(contour) > cv2.contourArea(maxContour)):
maxContour = contour
return maxContour
def _createDebug(dp, filename):
"""
创建debug函数
Args:
dp 文件目录
filename 文件名
Return:
_debug 调试函数
"""
def _debug(suffix, image):
dstPath = os.path.join(dp, "%s_%s.jpg" % (filename, suffix))
cv2.imwrite(dstPath, image)
return _debug
def _optimizeClothingMask(mask, debug):
"""
优化上装分割掩膜
Args:
mask 待优化掩膜
debug 函数
Return:
dstMask 优化后掩膜
"""
# Step 4.1: 形态学闭操作:填充衣物mask中的洞,修复那些被误认为是背景的衣物上的图案
debug('4.1_before_close', mask)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel, iterations=2)
debug('4.1_after_close', mask)
# Step 4.2: 形态学开操作去除衣物四周的吊饰
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, iterations=2)
debug('4.1_after_open', mask)
# Step 4.3: 填充最大轮廓,
maxContour = _findMaxContour(mask)
if maxContour is not None:
cv2.drawContours(mask, [maxContour], 0, (255, 255, 255), cv2.FILLED)
# Step 4.4: 进行一定的腐蚀操作,去除背景边界
debug('4.4_before_erode', mask)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
mask = cv2.erode(mask, kernel)
debug('4.4_after_erode', mask)
return mask
def _optimizePantsMask(mask, debug):
"""
优化下装分割掩膜
Args:
mask 待优化掩膜
debug debug函数
Return:
dstMask 优化后掩膜
"""
# Step 4.1: 形态学开操作去除衣物四周的吊饰
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
debug('4.1_after_open', mask)
# Step 4.2: 进行一定的腐蚀操作,去除背景边界
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
mask = cv2.erode(mask, kernel)
return mask
def _cutClothing(image, gray, edges, debug):
"""
上装分割
Args:
image 原图像
gray 灰度图像
edges 边缘灰度图像
debug debug函数
Return:
mask 分割掩膜
"""
rows, cols = edges.shape
# Step 3.1: 头部检测,应用grabcut圈定矩形框的时候需要去除头部
withModel = False
# Step 3.2: 查找最大外轮廓
# cv2.findContours是原地的
maxContour = _findMaxContour(edges)
boundingRect = cv2.boundingRect(maxContour)
# 情境判断,下方含有较大面积时考虑为Model情境
bottomRoi = edges[rows-10:rows, :]
maxContourInBottom = _findMaxContour(bottomRoi)
if maxContourInBottom is not None and cv2.contourArea(maxContourInBottom) > 150:
withModel = True
if withModel:
cv2.drawContours(edges, [maxContour], 0, (255, 255, 255), 3)
# Step 3.3.1: 一定程度的形态学腐蚀操作,去除衣物的粘连
debug('3.3.1_before_erode', edges)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
edges = cv2.erode(edges, kernel)
debug('3.3.1_after_erode', edges)
# Step 3.3.2: 均值滤波,抹除形态学操作后分散的盐噪声
edges = cv2.medianBlur(edges, 5)
debug('3.3.2_after_median_blur', edges)
# 进行缺陷检测,确定头部和衣物的分界
hull = cv2.convexHull(maxContour, returnPoints=False)
hullForDraw = cv2.convexHull(maxContour, returnPoints=True)
defects = cv2.convexityDefects(maxContour, hull)
hullDrawing = np.zeros((rows, cols,3), np.uint8)
cv2.drawContours(hullDrawing, [hullForDraw], 0, (208,188,44), 3)
cv2.drawContours(hullDrawing, [maxContour], 0, (255,255,255), 2)
head = [0, 0, cols, 0]
for defect in defects:
s, e, f, d = defect[0]
cv2.circle(hullDrawing, tuple(maxContour[f][0]), 8, (0,255,255), -1)
if d > 6000:
farW, farD = maxContour[f][0]
if farD < rows / 2 and farW > 100 and farW < cols - 100:
height = farD
if head[3] < height:
head[3] = height
head[3] = head[3] - 5
debug('hull', hullDrawing)
else:
head = None
contourDrawing = np.zeros((rows, cols), np.uint8)
cv2.drawContours(contourDrawing, [maxContour],
0, (255, 255, 255), cv2.FILLED)
debug('max_contour', contourDrawing)
# 最终返回的mask
dstMask = None
# Step 3.3: 获得分割掩膜
if head is None:
# 如果不存在头部,则填充最大外轮廓即可
cv2.drawContours(edges, [maxContour], 0, (255, 255, 255), cv2.FILLED)
_, dstMask = cv2.threshold(edges, 0, 255, cv2.THRESH_BINARY)
else:
# Step 3.3.3: 框定grabcut需要的矩形框
# 获得最小外接矩形
drawing = np.zeros((rows, cols), np.uint8)
colorDrawing = np.zeros((rows, cols, 3), np.uint8)
boundingRect = cv2.boundingRect(maxContour)
# 直接grabcut查看结果
grabMask = np.zeros((rows, cols), np.uint8)
cv2.grabCut(image, grabMask, (20, 20, rows - 40, cols - 40), None,
None, 5, cv2.GC_INIT_WITH_RECT)
dstMask = np.where((grabMask == 2) | (
grabMask == 0), 0, 255).astype('uint8')
debug('mask_without_opt', dstMask)
dst = image * (dstMask[:, :, np.newaxis] / 255)
debug('without_opt', dst)
cv2.rectangle(drawing, (boundingRect[0], boundingRect[1]),
(boundingRect[0] + boundingRect[2],
boundingRect[1] + boundingRect[3]),
(255, 0, 0), 2)
# 利用多边形拟合外轮廓的形状,并获得外轮廓的凸包
# 绘制出凸包包住拟合多边形的区域
epsilon = 0.01 * cv2.arcLength(maxContour, True)
approx = cv2.approxPolyDP(maxContour, epsilon, True)
approxDrawing = np.zeros((rows, cols, 3), np.uint8)
cv2.drawContours(approxDrawing, [maxContour], 0, (255, 255, 255), 3)
cv2.drawContours(approxDrawing, [approx], 0, (199, 138, 208), 3)
debug("approx", approxDrawing)
hull = cv2.convexHull(maxContour)
cv2.drawContours(colorDrawing, [hull], 0, (208,188,44), cv2.FILLED)
cv2.drawContours(drawing, [hull], 0, (255, 255, 255), cv2.FILLED)
cv2.drawContours(colorDrawing, [approx], 0, (199, 138, 208), cv2.FILLED)
cv2.drawContours(drawing, [approx], 0, (0, 0, 255), cv2.FILLED)
cv2.drawContours(colorDrawing, [maxContour], 0, (255, 255, 255), 3)
# 获得这些区域的外轮廓
contours, hierachy = cv2.findContours(
drawing, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[-2:]
# 遍历各个轮廓,获得各个轮廓的最小外接矩形
# 进而获得最深的外接矩形,最深的外接矩形往往反映了与上装黏着的下装部分
deepestRect = boundingRect
for contour in contours:
rect = cv2.boundingRect(contour)
if rect[3] > 5:
x, y, w, h = rect
if y > deepestRect[1]:
deepestRect = rect
# cv2.rectangle(
# colorDrawing,
# (boundingRect[0], boundingRect[1]),
# (boundingRect[0] + boundingRect[2],
# boundingRect[1] + boundingRect[3]),
# (0, 255, 0),
# 2
# )
# cv2.rectangle(
# colorDrawing,
# (head[0], head[1]),
# (head[0] + head[2], head[1] + head[3]),
# (255, 0, 255),
# 2
# )
cv2.rectangle(
colorDrawing,
(deepestRect[0], deepestRect[1]),
(deepestRect[0] + deepestRect[2], deepestRect[1] + deepestRect[3]),
(255, 0, 0),
3
)
debug('finding_rectangle', colorDrawing)
# 最终用于grabcut的矩形框将排除模特头部及裤装(最深矩形
x, y, w, h = boundingRect
y = head[1] + head[3]
h = h - deepestRect[3] - head[3] - head[1]
# grabcut需要的mask
grabMask = np.zeros((rows, cols), np.uint8)
drawing = np.zeros((rows, cols, 3), np.uint8)
cv2.drawContours(drawing, [maxContour], 0, (255,255,255), 3)
cv2.rectangle(drawing, (x, y), (x + w, y + h), (0, 255, 0), 3)
debug('rectangle_for_grabcut', drawing)
try:
cv2.grabCut(image, grabMask, (x, y, w, h), None,
None, 5, cv2.GC_INIT_WITH_RECT)
except:
try:
cv2.grabCut(image, grabMask, boundingRect, None,
None, 5, cv2.GC_INIT_WITH_RECT)
except:
cv2.grabCut(image, grabMask, (20, 20, rows - 40, cols - 40), None,
None, 5, cv2.GC_INIT_WITH_RECT)
# 将背景颜色设置为0,前景置为255
dstMask = np.where((grabMask == 2) | (
grabMask == 0), 0, 255).astype('uint8')
debug('mask', dstMask)
return dstMask
def _cutPants(image, gray, edges, debug):
"""
下装分割
Args:
image 原图像
gray 灰度图像
edges 边缘灰度图像
debug debug函数
Return:
mask 分割掩膜
"""
rows, cols = edges.shape
withModel = False
# Step 3.1: 获得最大轮廓及其外接矩形
maxContour = _findMaxContour(edges)
boundingRect = cv2.boundingRect(maxContour)
# 情境判断,上方含有较大面积时考虑为Model情境
topRoi = edges[rows-10:rows, :]
maxContourInTop = _findMaxContour(topRoi)
if maxContourInTop is not None and cv2.contourArea(maxContourInTop) > 150:
withModel = True
# 最终返回的mask
dstMask = None
colorDrawing = np.zeros((rows, cols, 3), np.uint8)
contourDrawing = np.zeros((rows, cols), np.uint8)
if not withModel:
# 如果不存在头部,则填充最大外轮廓即可
dstMask = contourDrawing
debug('pure_mask', contourDrawing)
else:
# 轮廓增强
contours, hierachy = cv2.findContours(
edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2:]
for contour in contours:
cv2.drawContours(edges, [contour],
0, (255, 255, 255), 3)
debug('fill_contour', edges)
maxContour = _findMaxContour(edges)
boundingRect = cv2.boundingRect(maxContour)
epsilon = 10
approx = cv2.approxPolyDP(maxContour, epsilon, False)
hull = cv2.convexHull(approx)
# 蓝色描述凸包
cv2.drawContours(colorDrawing, [hull], 0, (255, 0, 0), 3)
# 红色描述多边形拟合
cv2.drawContours(colorDrawing, [approx], 0, (0, 0, 255), 3)
# approx = _findMaxContour(cv2.cvtColor(colorDrawing.copy(), cv2.COLOR_BGR2GRAY))
approxImage = np.zeros((rows,cols), np.uint8)
cv2.drawContours(approxImage, [approx], 0, 255, -1)
debug('approx_image', approxImage)
# 四个ROI进行特征点查找
x,y,w,h = boundingRect
leftEnd = x + w*2/5
rightBegin = x + w*3/5
topEnd = y + h*2/5
bottomBegin = y + h*3/5
leftClothingRoi = approxImage[y:topEnd, x:leftEnd].copy()
rightClothingRoi = approxImage[y:topEnd, rightBegin:x+w].copy()
leftShoesRoi = approxImage[bottomBegin:y+h, x:leftEnd].copy()
rightShoesRoi = approxImage[bottomBegin:y+h, rightBegin:x+w].copy()
leftClothingContour = _findMaxContour(leftClothingRoi)
rightClothingContour = _findMaxContour(rightClothingRoi)
leftShoesContour = _findMaxContour(leftShoesRoi)
rightShoesContour = _findMaxContour(leftShoesRoi)
# 初始化衣物分界线两端
leftClothingBoundary = [x, y]
rightClothingBoundary = [x+w, y]
# 初始化鞋子分界线两端
leftShoesBoundary = [x, y+h]
rightShoesBoundary = [x+w, y+h]
# 初始化最终用于grabCut的矩形
grabRect = list((boundingRect))
# 找到上衣和下装的分界线
hull = cv2.convexHull(leftClothingContour, returnPoints=False)
defects = cv2.convexityDefects(leftClothingContour, hull)
for defect in defects:
s, e, f, d = defect[0]
farW, farD = leftClothingContour[f][0]
farW = farW + x
cv2.circle(colorDrawing, (farW, farD), 5, (255,255,255), -1)
# 选择最深的
if leftClothingBoundary[1] < farD:
leftClothingBoundary = [farW, farD]
hull = cv2.convexHull(rightClothingContour, returnPoints=False)
defects = cv2.convexityDefects(rightClothingContour, hull)
for defect in defects:
s, e, f, d = defect[0]
farW, farD = rightClothingContour[f][0]
farW = farW + rightBegin
cv2.circle(colorDrawing, (farW, farD), 5, (255,255,255), 2)
# 选择最深的
if rightClothingBoundary[1] < farD:
cv2.circle(colorDrawing, (farW, farD), 5, (255,255,255), -1)
rightClothingBoundary = [farW, farD]
grabRect[0] = leftClothingBoundary[0]
grabRect[2] = rightClothingBoundary[0] - leftClothingBoundary[0]
if leftClothingBoundary[1] > rightClothingBoundary[1]:
grabRect[1] = rightClothingBoundary[1]
else:
grabRect[1] = leftClothingBoundary[1]
# 找到下装和鞋子的分界线
hull = cv2.convexHull(leftShoesContour, returnPoints=False)
defects = cv2.convexityDefects(leftShoesContour, hull)
for defect in defects:
s, e, f, d = defect[0]
farW, farD = leftShoesContour[f][0]
farW = farW + x
farD = farD + bottomBegin
cv2.circle(colorDrawing, (farW, farD), 5, (255,255,255), -1)
# 找寻较浅的
if leftShoesBoundary[1] > farD:
leftShoesBoundary = [farW, farD]
hull = cv2.convexHull(rightClothingContour, returnPoints=False)
defects = cv2.convexityDefects(rightClothingContour, hull)
for defect in defects:
s, e, f, d = defect[0]
farW, farD = rightClothingContour[f][0]
farW = farW + rightBegin
farD = farD + bottomBegin
cv2.circle(colorDrawing, (farW, farD), 5, (255,255,255), -1)
# 找寻较浅的
if rightShoesBoundary[1] > farD:
rightShoesBoundary = [farW, farD]
if leftShoesBoundary[1] > rightShoesBoundary[1]:
grabRect[3] = leftShoesBoundary[1] - grabRect[1]
else:
grabRect[3] = rightShoesBoundary[1] - grabRect[1]
# 稍微放缩矩形
grabRect[0] = grabRect[0] - 20
grabRect[2] = grabRect[2] + 40
grabRect[1] = grabRect[1] - 20
grabRect[3] = grabRect[3] + 40
# 绘制原外接矩形及最终用于grabCut的矩形
cv2.rectangle(
colorDrawing,
(boundingRect[0], boundingRect[1]),
(boundingRect[0] + boundingRect[2], boundingRect[1] + boundingRect[3]),
(255, 0, 0),
2
)
cv2.rectangle(
colorDrawing,
(grabRect[0], grabRect[1]),
(grabRect[0] + grabRect[2], grabRect[1] + grabRect[3]),
(0, 255, 0),
2
)
debug("finding_rectangle", colorDrawing)
# 用于grabcut的mask
grabMask = np.zeros((rows, cols), np.uint8)
try:
cv2.grabCut(image, grabMask, tuple(grabRect), None,
None, 5, cv2.GC_INIT_WITH_RECT)
except:
cv2.grabCut(image, grabMask, boundingRect, None,
None, 5, cv2.GC_INIT_WITH_RECT)
dstMask = np.where((grabMask == 2) | (
grabMask == 0), 0, 255).astype('uint8')
cv2.drawContours(contourDrawing, [maxContour],
0, (255, 255, 255), cv2.FILLED)
debug('max_contour', contourDrawing)
return dstMask
def _clothingEdgeDetect(gray, debug):
"""
边缘提取
Args:
gray 灰度图像
debug debug函数
Return:
edges 边缘图像
"""
# Canny边缘检测
edges = cv2.Canny(gray, 1, 20, apertureSize=3)
# 边缘增强
x = cv2.Sobel(edges, cv2.CV_16S,1,0)
y = cv2.Sobel(edges, cv2.CV_16S,0,1)
absX = cv2.convertScaleAbs(x) # 转回uint8
absY = cv2.convertScaleAbs(y)
edges = cv2.addWeighted(absX,0.5,absY,0.5,0)
_, edges = cv2.threshold(edges, 50, 255, cv2.THRESH_BINARY)
# 形态学闭操作修复断线
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
edges = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel)
debug('edges', edges)
return edges
def _pantsEdgeDetect(gray, debug):
"""
边缘提取
Args:
gray 灰度图像
debug debug函数
Return:
edges 边缘图像
"""
# Canny边缘检测
edges = cv2.Canny(gray, 1, 20, apertureSize = 3)
# 边缘增强
x = cv2.Sobel(edges, cv2.CV_16S,1,0)
y = cv2.Sobel(edges, cv2.CV_16S,0,1)
absX = cv2.convertScaleAbs(x) # 转回uint8
absY = cv2.convertScaleAbs(y)
edges = cv2.addWeighted(absX,0.5,absY,0.5,0)
_, edges = cv2.threshold(edges, 50, 255, cv2.THRESH_BINARY)
# 形态学闭操作修复断线
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
edges = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel)
debug('edges', edges)
return edges
def _preProcess(image, debug):
"""
图像预处理
Args:
image 原图像
debug debug函数
Return:
gray 预处理后的灰度图
"""
# 高斯模糊进行降噪
image = cv2.GaussianBlur(image, (3, 3), 0)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
debug('after_gaussian_blur', gray)
return gray
def cut(image, isClothing, dp, filename, isDebug=False):
"""
分割
Args:
image 原图像
dp 当前文件所在路径
filename 当前处理文件名
isDebug 是否debug模式
Return:
dst 分割后的图像
mask 掩膜
"""
if isDebug:
debug = _createDebug(dp, filename)
else:
debug = _noop
# Step 1: 图像预处理
grayImage = _preProcess(image.copy(), debug)
if isClothing:
# Step 2: 边缘提取
edges = _clothingEdgeDetect(grayImage.copy(), debug)
# Step 3: 分割,并获得分割掩模
mask = _cutClothing(image.copy(), grayImage.copy(),
edges.copy(), debug)
# Step 4: 优化上衣掩膜
mask = _optimizeClothingMask(mask.copy(), debug)
else:
# Step 2: 边缘提取
edges = _pantsEdgeDetect(grayImage.copy(), debug)
# Step 3: 分割,并获得分割掩模
mask = _cutPants(image.copy(), grayImage.copy(), edges.copy(), debug)
# Step 4: 优化下装掩膜
mask = _optimizePantsMask(mask.copy(), debug)
# Step 5: 生成最终图像
dst = image * (mask[:, :, np.newaxis] / 255)
return mask, dst