一、背景介绍
实际应用场景中,图片或视频中可能会存在黑边,影响美观度,不适合推荐给用户,本文主要是介绍如何去除黑边和蒙版。
(限于篇幅,以图像蒙版去除为例子,进行讲解)
(1) 先看下效果
A图和C图是带有蒙版的的视频帧(图像),B图和D图是检测蒙版并进行裁剪后的图像,可以看出,该算法可以完美去除蒙版。【黑边去除更易】
下面的分数表示基于蒙版的遮盖程度,对该图片进行打分,分数越高,说明视频被遮挡的区域越小
(2)算法流程
(a)先用candy算子进行边缘检测
(b)使用霍夫曼直线检测,检测直线
(c)NMS非极大值抑制去除多余直线
(d)按照一定规则选取最终的2条直线
对每一步进行可视化:
上图是有蒙版的,下图是无蒙版的
二、代码
#coding:utf-8
import numpy as np
import matplotlib.pyplot as plt
import os
import cv2
from math import cos,sin
def lines_detector_hough(edge,ThetaDim = None,DistStep = None,threshold = None,halfThetaWindowSize = 2,halfDistWindowSize = None):
'''
:return: 返回检测出的所有直线的参数(theta,dist)
参考了:https://github.com/o0o0o0o0o0o0o/image-processing-from-scratch,欢迎给他点小星星
'''
imgsize = edge.shape
if ThetaDim == None:
ThetaDim = 90
if DistStep == None:
DistStep = 1
MaxDist = np.sqrt(imgsize[0]**2 + imgsize[1]**2)
DistDim = int(np.ceil(MaxDist/DistStep))
if halfDistWindowSize == None:
halfDistWindowSize = int(DistDim/50)
accumulator = np.zeros((ThetaDim,DistDim)) # theta的范围是[0,pi). 在这里将[0,pi)进行了线性映射.类似的,也对Dist轴进行了线性映射
sinTheta = [np.sin(t*np.pi/ThetaDim) for t in range(ThetaDim)]
cosTheta = [np.cos(t*np.pi/ThetaDim) for t in range(ThetaDim)]
for i in range(imgsize[0]):
for j in range(imgsize[1]):
if not edge[i,j] == 0:
for k in range(ThetaDim):
accumulator[k][int(round((i*cosTheta[k]+j*sinTheta[k])*DistDim/MaxDist))] += 1
M = accumulator.max()
if threshold == None:
threshold = int(M*0.45)
result = np.array(np.where(accumulator > threshold)) # 阈值化
temp = [[],[]]
for i in range(result.shape[1]):
eight_neiborhood = accumulator[max(0, result[0,i] - halfThetaWindowSize + 1):min(result[0,i] + halfThetaWindowSize, accumulator.shape[0]), max(0, result[1,i] - halfDistWindowSize + 1):min(result[1,i] + halfDistWindowSize, accumulator.shape[1])]
if (accumulator[result[0,i],result[1,i]] >= eight_neiborhood).all():
temp[0].append(result[0,i])
temp[1].append(result[1,i])
result = np.array(temp) # 非极大值抑制
result = result.astype(np.float64)
result[0] = result[0]*np.pi/ThetaDim
result[1] = result[1]*MaxDist/DistDim
return result
def drawLines(lines,edge,color = (255,0,0),err = 3):
if len(edge.shape) == 2:
result = np.dstack((edge,edge,edge))
else:
result = edge
#import pdb;pdb.set_trace()
Cos = np.cos(lines[0])
Sin = np.sin(lines[0])
#import pdb;pdb.set_trace()
for i in range(edge.shape[0]):
for j in range(edge.shape[1]):
e = np.abs(lines[1] - i*Cos - j*Sin)
if (e < err).any():
result[i,j] = color
import pdb;pdb.set_trace()
return result
def ImageScore(image, lines):
# 保证角度为0 或者为垂直
h,w,_ = image.shape
selectA = [0,90,180,270,360]
print ("lines is:",lines)
newline = []
for idx,theta in enumerate(lines[0]):
if theta in selectA:
if theta==0 or theta==360 or theta==180:
flag = 0
else:
flag = 1
newline.append([theta,lines[1][idx]])
#import pdb;pdb.set_trace()
if len(newline) == 2:
if flag == 0:
y1 = int(newline[0][1]*cos(newline[0][0]))
y2 = int(newline[1][1]*cos(newline[1][0]))
min_ = min(y1,y2)
max_ = max(y1,y2)
image_patch = image[min_:max_,0:w]
score = ((max_ - min_) * w) / (h*w)
else:# flag == 1:
x1 = int(newline[0][1]*sin(newline[0][0]))
x2 = int(newline[1][1]*sin(newline[1][0]))
min_ = min(x1,x2)
max_ = max(x1,x2)
image_patch = image[0:h,min_:max_]
score = ((max_ - min_) * h) / (h*w)
cv2.imwrite("demohh22.jpg",image_patch)
print ('score is:',score)
return score
else:
return 1
if __name__=='__main__':
pic_path = 'test_img/'
pics = os.listdir(pic_path)
for i in pics:
if i[-5:] == '.jpeg' or i[-4:] == '.jpg':
img = plt.imread(pic_path+i)
image = cv2.imread(pic_path + i)
blurred = cv2.GaussianBlur(img, (3, 3), 0)
plt.imshow(blurred,cmap='gray')
plt.axis('off')
plt.show()
if not len(blurred.shape) == 2:
gray = cv2.cvtColor(blurred, cv2.COLOR_RGB2GRAY)
else:
gray = blurred
edge = cv2.Canny(gray, 50, 150) # 二值图 (0 或 255) 得到 canny边缘检测的结果
lines = lines_detector_hough(edge)
#score = ImageScore(image, lines) //给该图片打分
#print ("score is:",score)
final_img = drawLines(lines,blurred)
plt.imshow(final_img,cmap='gray')
plt.axis('off')
plt.show()