Spaces:
Running
Running
import cv2 | |
import numpy as np | |
from hivisionai.hycv.utils import get_box_pro | |
from hivisionai.hycv.vision import cover_image, draw_picture_dots | |
from math import fabs, sin, radians, cos | |
def opencv_rotate(img, angle): | |
h, w = img.shape[:2] | |
center = (w / 2, h / 2) | |
scale = 1.0 | |
# 2.1获取M矩阵 | |
""" | |
M矩阵 | |
[ | |
cosA -sinA (1-cosA)*centerX+sinA*centerY | |
sinA cosA -sinA*centerX+(1-cosA)*centerY | |
] | |
""" | |
M = cv2.getRotationMatrix2D(center, angle, scale) | |
# 2.2 新的宽高,radians(angle) 把角度转为弧度 sin(弧度) | |
new_H = int(w * fabs(sin(radians(angle))) + h * fabs(cos(radians(angle)))) | |
new_W = int(h * fabs(sin(radians(angle))) + w * fabs(cos(radians(angle)))) | |
# 2.3 平移 | |
M[0, 2] += (new_W - w) / 2 | |
M[1, 2] += (new_H - h) / 2 | |
rotate = cv2.warpAffine(img, M, (new_W, new_H), borderValue=(0, 0, 0)) | |
return rotate | |
def transformationNeck2(image:np.ndarray, per_to_side:float=0.8)->np.ndarray: | |
""" | |
透视变换脖子函数,输入图像和四个点(矩形框) | |
矩形框内的图像可能是不完整的(边角有透明区域) | |
我们将根据透视变换将矩形框内的图像拉伸成和矩形框一样的形状. | |
算法分为几个步骤: 选择脖子的四个点 -> 选定这四个点拉伸后的坐标 -> 透视变换 -> 覆盖原图 | |
""" | |
_, _, _, a = cv2.split(image) # 这应该是一个四通道的图像 | |
height, width = a.shape | |
def locate_side(image_:np.ndarray, x_:int, y_max:int) -> int: | |
# 寻找x=y, 且 y <= y_max 上从下往上第一个非0的点,如果没找到就返回0 | |
y_ = 0 | |
for y_ in range(y_max - 1, -1, -1): | |
if image_[y_][x_] != 0: | |
break | |
return y_ | |
def locate_width(image_:np.ndarray, y_:int, mode, left_or_right:int=None): | |
# 从y=y这个水平线上寻找两边的非零点 | |
# 增加left_or_right的原因在于为下面check_jaw服务 | |
if mode==1: # 左往右 | |
x_ = 0 | |
if left_or_right is None: | |
left_or_right = 0 | |
for x_ in range(left_or_right, width): | |
if image_[y_][x_] != 0: | |
break | |
else: # 右往左 | |
x_ = width | |
if left_or_right is None: | |
left_or_right = width - 1 | |
for x_ in range(left_or_right, -1, -1): | |
if image_[y_][x_] != 0: | |
break | |
return x_ | |
def check_jaw(image_:np.ndarray, left_, right_): | |
""" | |
检查选择的点是否与截到下巴,如果截到了,就往下平移一个单位 | |
""" | |
f= True # True代表没截到下巴 | |
# [x, y] | |
for x_cell in range(left_[0] + 1, right_[0]): | |
if image_[left_[1]][x_cell] == 0: | |
f = False | |
break | |
if f is True: | |
return left_, right_ | |
else: | |
y_ = left_[1] + 2 | |
x_left_ = locate_width(image_, y_, mode=1, left_or_right=left_[0]) | |
x_right_ = locate_width(image_, y_, mode=2, left_or_right=right_[0]) | |
left_, right_ = check_jaw(image_, [x_left_, y_], [x_right_, y_]) | |
return left_, right_ | |
# 选择脖子的四个点,核心在于选择上面的两个点,这两个点的确定的位置应该是"宽出来的"两个点 | |
_, _ ,_, a = cv2.split(image) # 这应该是一个四通道的图像 | |
ret,a_thresh = cv2.threshold(a,127,255,cv2.THRESH_BINARY) | |
y_high, y_low, x_left, x_right = get_box_pro(image=image, model=1) # 直接返回矩阵信息 | |
y_left_side = locate_side(image_=a_thresh, x_=x_left, y_max=y_low) # 左边的点的y轴坐标 | |
y_right_side = locate_side(image_=a_thresh, x_=x_right, y_max=y_low) # 右边的点的y轴坐标 | |
y = min(y_left_side, y_right_side) # 将两点的坐标保持相同 | |
cell_left_above, cell_right_above = check_jaw(a_thresh,[x_left, y], [x_right, y]) | |
x_left, x_right = cell_left_above[0], cell_right_above[0] | |
# 此时我们寻找到了脖子的"宽出来的"两个点,这两个点作为上面的两个点, 接下来寻找下面的两个点 | |
if per_to_side >1: | |
assert ValueError("per_to_side 必须小于1!") | |
# 在后面的透视变换中我会把它拉成矩形, 在这里我先获取四个点的高和宽 | |
height_ = 150 # 这个值应该是个变化的值,与拉伸的长度有关,但是现在先规定为150 | |
width_ = x_right - x_left # 其实也就是 cell_right_above[1] - cell_left_above[1] | |
y = int((y_low - y)*per_to_side + y) # 定位y轴坐标 | |
cell_left_below, cell_right_bellow = ([locate_width(a_thresh, y_=y, mode=1), y], [locate_width(a_thresh, y_=y, mode=2), y]) | |
# 四个点全齐,开始透视变换 | |
# 寻找透视变换后的四个点,只需要变换below的两个点即可 | |
# cell_left_below_final, cell_right_bellow_final = ([cell_left_above[1], y_low], [cell_right_above[1], y_low]) | |
# 需要变换的四个点为 cell_left_above, cell_right_above, cell_left_below, cell_right_bellow | |
rect = np.array([cell_left_above, cell_right_above, cell_left_below, cell_right_bellow], | |
dtype='float32') | |
# 变化后的坐标点 | |
dst = np.array([[0, 0], [width_, 0], [0 , height_], [width_, height_]], | |
dtype='float32') | |
# 计算变换矩阵 | |
M = cv2.getPerspectiveTransform(rect, dst) | |
warped = cv2.warpPerspective(image, M, (width_, height_)) | |
final = cover_image(image=warped, background=image, mode=3, x=cell_left_above[0], y=cell_left_above[1]) | |
# tmp = np.zeros(image.shape) | |
# final = cover_image(image=warped, background=tmp, mode=3, x=cell_left_above[0], y=cell_left_above[1]) | |
# final = cover_image(image=image, background=final, mode=3, x=0, y=0) | |
return final | |
def transformationNeck(image:np.ndarray, cutNeckHeight:int, neckBelow:int, | |
toHeight:int,per_to_side:float=0.75) -> np.ndarray: | |
""" | |
脖子扩充算法, 其实需要输入的只是脖子扣出来的部分以及需要被扩充的高度/需要被扩充成的高度. | |
""" | |
height, width, channels = image.shape | |
_, _, _, a = cv2.split(image) # 这应该是一个四通道的图像 | |
ret, a_thresh = cv2.threshold(a, 127, 255, cv2.THRESH_BINARY) # 将透明图层二值化 | |
def locate_width(image_:np.ndarray, y_:int, mode, left_or_right:int=None): | |
# 从y=y这个水平线上寻找两边的非零点 | |
# 增加left_or_right的原因在于为下面check_jaw服务 | |
if mode==1: # 左往右 | |
x_ = 0 | |
if left_or_right is None: | |
left_or_right = 0 | |
for x_ in range(left_or_right, width): | |
if image_[y_][x_] != 0: | |
break | |
else: # 右往左 | |
x_ = width | |
if left_or_right is None: | |
left_or_right = width - 1 | |
for x_ in range(left_or_right, -1, -1): | |
if image_[y_][x_] != 0: | |
break | |
return x_ | |
def check_jaw(image_:np.ndarray, left_, right_): | |
""" | |
检查选择的点是否与截到下巴,如果截到了,就往下平移一个单位 | |
""" | |
f= True # True代表没截到下巴 | |
# [x, y] | |
for x_cell in range(left_[0] + 1, right_[0]): | |
if image_[left_[1]][x_cell] == 0: | |
f = False | |
break | |
if f is True: | |
return left_, right_ | |
else: | |
y_ = left_[1] + 2 | |
x_left_ = locate_width(image_, y_, mode=1, left_or_right=left_[0]) | |
x_right_ = locate_width(image_, y_, mode=2, left_or_right=right_[0]) | |
left_, right_ = check_jaw(image_, [x_left_, y_], [x_right_, y_]) | |
return left_, right_ | |
x_left = locate_width(image_=a_thresh, mode=1, y_=cutNeckHeight) | |
x_right = locate_width(image_=a_thresh, mode=2, y_=cutNeckHeight) | |
# 在这里我们取消了对下巴的检查,原因在于输入的imageHeight并不能改变 | |
# cell_left_above, cell_right_above = check_jaw(a_thresh, [x_left, imageHeight], [x_right, imageHeight]) | |
cell_left_above, cell_right_above = [x_left, cutNeckHeight], [x_right, cutNeckHeight] | |
toWidth = x_right - x_left # 矩形宽 | |
# 此时我们寻找到了脖子的"宽出来的"两个点,这两个点作为上面的两个点, 接下来寻找下面的两个点 | |
if per_to_side >1: | |
assert ValueError("per_to_side 必须小于1!") | |
y_below = int((neckBelow - cutNeckHeight) * per_to_side + cutNeckHeight) # 定位y轴坐标 | |
cell_left_below = [locate_width(a_thresh, y_=y_below, mode=1), y_below] | |
cell_right_bellow = [locate_width(a_thresh, y_=y_below, mode=2), y_below] | |
# 四个点全齐,开始透视变换 | |
# 需要变换的四个点为 cell_left_above, cell_right_above, cell_left_below, cell_right_bellow | |
rect = np.array([cell_left_above, cell_right_above, cell_left_below, cell_right_bellow], | |
dtype='float32') | |
# 变化后的坐标点 | |
dst = np.array([[0, 0], [toWidth, 0], [0 , toHeight], [toWidth, toHeight]], | |
dtype='float32') | |
M = cv2.getPerspectiveTransform(rect, dst) | |
warped = cv2.warpPerspective(image, M, (toWidth, toHeight)) | |
# 将变换后的图像覆盖到原图上 | |
final = cover_image(image=warped, background=image, mode=3, x=cell_left_above[0], y=cell_left_above[1]) | |
return final | |
def bestJunctionCheck_beta(image:np.ndarray, stepSize:int=4, if_per:bool=False): | |
""" | |
最优衔接点检测算法, 去寻找脖子的"拐点" | |
""" | |
point_k = 1 | |
_, _, _, a = cv2.split(image) # 这应该是一个四通道的图像 | |
height, width = a.shape | |
ret, a_thresh = cv2.threshold(a, 127, 255, cv2.THRESH_BINARY) # 将透明图层二值化 | |
y_high, y_low, x_left, x_right = get_box_pro(image=image, model=1) # 直接返回矩阵信息 | |
def scan(y_:int, max_num:int=2): | |
num = 0 | |
left = False | |
right = False | |
for x_ in range(width): | |
if a_thresh[y_][x_] != 0: | |
if x_ < width // 2 and left is False: | |
num += 1 | |
left = True | |
elif x_ > width // 2 and right is False: | |
num += 1 | |
right = True | |
return True if num >= max_num else False | |
def locate_neck_above(): | |
""" | |
定位脖子的尖尖脚 | |
""" | |
for y_ in range( y_high - 2, height): | |
if scan(y_): | |
return y_, y_ | |
y_high_left, y_high_right = locate_neck_above() | |
def locate_width_pro(image_:np.ndarray, y_:int, mode): | |
""" | |
这会是一个生成器,用于生成脖子两边的轮廓 | |
x_, y_ 是启始点的坐标,每一次寻找都会让y_+1 | |
mode==1说明是找左边的边,即,image_[y_][x_] == 0 且image_[y_][x_ + 1] !=0 时跳出; | |
否则 当image_[y_][x_] != 0 时, x_ - 1; 当image_[y_][x_] == 0 且 image_[y_][x_ + 1] ==0 时x_ + 1 | |
mode==2说明是找右边的边,即,image_[y_][x_] == 0 且image_[y_][x_ - 1] !=0 时跳出 | |
否则 当image_[y_][x_] != 0 时, x_ + 1; 当image_[y_][x_] == 0 且 image_[y_][x_ - 1] ==0 时x_ - 1 | |
""" | |
y_ += 1 | |
if mode == 1: | |
x_ = 0 | |
while 0 <= y_ < height and 0 <= x_ < width: | |
while image_[y_][x_] != 0 and x_ >= 0: | |
x_ -= 1 | |
while image_[y_][x_] == 0 and image_[y_][x_ + 1] == 0 and x_ < width - 2: | |
x_ += 1 | |
yield [y_, x_] | |
y_ += 1 | |
elif mode == 2: | |
x_ = width-1 | |
while 0 <= y_ < height and 0 <= x_ < width: | |
while image_[y_][x_] != 0 and x_ < width - 2: x_ += 1 | |
while image_[y_][x_] == 0 and image_[y_][x_ - 1] == 0 and x_ >= 0: x_ -= 1 | |
yield [y_, x_] | |
y_ += 1 | |
yield False | |
def kGenerator(image_:np.ndarray, mode): | |
""" | |
导数生成器,用来生成每一个点对应的导数 | |
""" | |
y_ = y_high_left if mode == 1 else y_high_right | |
c_generator = locate_width_pro(image_=image_, y_=y_, mode=mode) | |
for cell in c_generator: | |
nc = locate_width_pro(image_=image_, y_=cell[0] + stepSize, mode=mode) | |
nextCell = next(nc) | |
if nextCell is False: | |
yield False, False | |
else: | |
k = (cell[1] - nextCell[1]) / stepSize | |
yield k, cell | |
def findPt(image_:np.ndarray, mode): | |
k_generator = kGenerator(image_=image_, mode=mode) | |
k, cell = next(k_generator) | |
k_next, cell_next = next(k_generator) | |
if k is False: | |
raise ValueError("无法找到拐点!") | |
while k_next is not False: | |
k_next, cell_next = next(k_generator) | |
if (k_next < - 1 / stepSize) or k_next > point_k: | |
break | |
cell = cell_next | |
# return int(cell[0] + stepSize / 2) | |
return cell[0] | |
# 先找左边的拐点: | |
pointY_left = findPt(image_=a_thresh, mode=1) | |
# 再找右边的拐点: | |
pointY_right = findPt(image_=a_thresh, mode=2) | |
point = (pointY_left + pointY_right) // 2 | |
if if_per is True: | |
point = (pointY_left + pointY_right) // 2 | |
return point / (y_low - y_high) | |
pointX_left = next(locate_width_pro(image_=a_thresh, y_= point - 1, mode=1))[1] | |
pointX_right = next(locate_width_pro(image_=a_thresh, y_=point- 1, mode=2))[1] | |
return [pointX_left, point], [pointX_right, point] | |
def bestJunctionCheck(image:np.ndarray, offset:int, stepSize:int=4): | |
""" | |
最优点检测算算法输入一张脖子图片(无论这张图片是否已经被二值化,我都认为没有被二值化),输出一个小数(脖子最上方与衔接点位置/脖子图像长度) | |
与beta版不同的是它新增了一个阈值限定内容. | |
对于脖子而言,我我们首先可以定位到上面的部分,然后根据上面的这个点向下进行遍历检测. | |
与beta版类似,我们使用一个stepSize来用作斜率的检测 | |
但是对于遍历检测而言,与beta版不同的是,我们需要对遍历的地方进行一定的限制. | |
限制的标准是,如果当前遍历的点的横坐标和起始点横坐标的插值超过了某个阈值,则认为是越界. | |
""" | |
point_k = 1 | |
_, _, _, a = cv2.split(image) # 这应该是一个四通道的图像 | |
height, width = a.shape | |
ret, a_thresh = cv2.threshold(a, 127, 255, cv2.THRESH_BINARY) # 将透明图层二值化 | |
# 直接返回脖子的位置信息, 修正系数为0, get_box_pro内部也封装了二值化,所以直接输入原图 | |
y_high, y_low, _, _ = get_box_pro(image=image, model=1, correction_factor=0) | |
# 真正有用的只有上下y轴的两个值... | |
# 首先当然是确定起始点的位置,我们用同样的scan扫描函数进行行遍历. | |
def scan(y_:int, max_num:int=2): | |
num = 0 | |
# 设定两个值,分别代表脖子的左边和右边 | |
left = False | |
right = False | |
for x_ in range(width): | |
if a_thresh[y_][x_] != 0: | |
# 检测左边 | |
if x_ < width // 2 and left is False: | |
num += 1 | |
left = True | |
# 检测右边 | |
elif x_ > width // 2 and right is False: | |
num += 1 | |
right = True | |
return True if num >= max_num else False | |
def locate_neck_above(): | |
""" | |
定位脖子的尖尖脚 | |
""" | |
# y_high就是脖子的最高点 | |
for y_ in range(y_high, height): | |
if scan(y_): | |
return y_ | |
y_start = locate_neck_above() # 得到遍历的初始高度 | |
if y_low - y_start < stepSize: assert ValueError("脖子太小!") | |
# 然后获取一下初始的坐标点 | |
x_left, x_right = 0, width | |
for x_left_ in range(0, width): | |
if a_thresh[y_start][x_left_] != 0: | |
x_left = x_left_ | |
break | |
for x_right_ in range(width -1 , -1, -1): | |
if a_thresh[y_start][x_right_] != 0: | |
x_right = x_right_ | |
break | |
# 接下来我定义两个生成器,首先是脖子轮廓(向下寻找的)生成器,每进行一次next,生成器会返回y+1的脖子轮廓点 | |
def contoursGenerator(image_:np.ndarray, y_:int, mode): | |
""" | |
这会是一个生成器,用于生成脖子两边的轮廓 | |
y_ 是启始点的y坐标,每一次寻找都会让y_+1 | |
mode==1说明是找左边的边,即,image_[y_][x_] == 0 且image_[y_][x_ + 1] !=0 时跳出; | |
否则 当image_[y_][x_] != 0 时, x_ - 1; 当image_[y_][x_] == 0 且 image_[y_][x_ + 1] ==0 时x_ + 1 | |
mode==2说明是找右边的边,即,image_[y_][x_] == 0 且image_[y_][x_ - 1] !=0 时跳出 | |
否则 当image_[y_][x_] != 0 时, x_ + 1; 当image_[y_][x_] == 0 且 image_[y_][x_ - 1] ==0 时x_ - 1 | |
""" | |
y_ += 1 | |
try: | |
if mode == 1: | |
x_ = 0 | |
while 0 <= y_ < height and 0 <= x_ < width: | |
while image_[y_][x_] != 0 and x_ >= 0: x_ -= 1 | |
# 这里其实会有bug,不过可以不管 | |
while x_ < width and image_[y_][x_] == 0 and image_[y_][x_ + 1] == 0: x_ += 1 | |
yield [y_, x_] | |
y_ += 1 | |
elif mode == 2: | |
x_ = width-1 | |
while 0 <= y_ < height and 0 <= x_ < width: | |
while x_ < width and image_[y_][x_] != 0: x_ += 1 | |
while x_ >= 0 and image_[y_][x_] == 0 and image_[y_][x_ - 1] == 0: x_ -= 1 | |
yield [y_, x_] | |
y_ += 1 | |
# 当处理失败则返回False | |
except IndexError: | |
yield False | |
# 然后是斜率生成器,这个生成器依赖子轮廓生成器,每一次生成轮廓后会计算斜率,另一个点的选取和stepSize有关 | |
def kGenerator(image_: np.ndarray, mode): | |
""" | |
导数生成器,用来生成每一个点对应的导数 | |
""" | |
y_ = y_start | |
# 对起始点建立一个生成器, mode=1时是左边轮廓,mode=2时是右边轮廓 | |
c_generator = contoursGenerator(image_=image_, y_=y_, mode=mode) | |
for cell in c_generator: | |
# 寻找距离当前cell距离为stepSize的轮廓点 | |
kc = contoursGenerator(image_=image_, y_=cell[0] + stepSize, mode=mode) | |
kCell = next(kc) | |
if kCell is False: | |
# 寻找失败 | |
yield False, False | |
else: | |
# 寻找成功,返回当坐标点和斜率值 | |
# 对于左边而言,斜率必然是前一个点的坐标减去后一个点的坐标 | |
# 对于右边而言,斜率必然是后一个点的坐标减去前一个点的坐标 | |
k = (cell[1] - kCell[1]) / stepSize if mode == 1 else (kCell[1] - cell[1]) / stepSize | |
yield k, cell | |
# 接着开始写寻找算法,需要注意的是我们是分两边选择的 | |
def findPt(image_:np.ndarray, mode): | |
x_base = x_left if mode == 1 else x_right | |
k_generator = kGenerator(image_=image_, mode=mode) | |
k, cell = k_generator.__next__() | |
if k is False: | |
raise ValueError("无法找到拐点!") | |
k_next, cell_next = k_generator.__next__() | |
while k_next is not False: | |
cell = cell_next | |
if cell[1] > x_base and mode == 2: | |
x_base = cell[1] | |
elif cell[1] < x_base and mode == 1: | |
x_base = cell[1] | |
# 跳出循环的方式一:斜率超过了某个值 | |
if k_next > point_k: | |
print("K out") | |
break | |
# 跳出循环的方式二:超出阈值 | |
elif abs(cell[1] - x_base) > offset: | |
print("O out") | |
break | |
k_next, cell_next = k_generator.__next__() | |
if abs(cell[1] - x_base) > offset: | |
cell[0] = cell[0] - offset - 1 | |
return cell[0] | |
# 先找左边的拐点: | |
pointY_left = findPt(image_=a_thresh, mode=1) | |
# 再找右边的拐点: | |
pointY_right = findPt(image_=a_thresh, mode=2) | |
point = min(pointY_right, pointY_left) | |
per = (point - y_high) / (y_low - y_high) | |
# pointX_left = next(contoursGenerator(image_=a_thresh, y_= point- 1, mode=1))[1] | |
# pointX_right = next(contoursGenerator(image_=a_thresh, y_=point - 1, mode=2))[1] | |
# return [pointX_left, point], [pointX_right, point] | |
return per | |
def checkSharpCorner(image:np.ndarray): | |
_, _, _, a = cv2.split(image) # 这应该是一个四通道的图像 | |
height, width = a.shape | |
ret, a_thresh = cv2.threshold(a, 127, 255, cv2.THRESH_BINARY) # 将透明图层二值化 | |
# 直接返回脖子的位置信息, 修正系数为0, get_box_pro内部也封装了二值化,所以直接输入原图 | |
y_high, y_low, _, _ = get_box_pro(image=image, model=1, correction_factor=0) | |
def scan(y_:int, max_num:int=2): | |
num = 0 | |
# 设定两个值,分别代表脖子的左边和右边 | |
left = False | |
right = False | |
for x_ in range(width): | |
if a_thresh[y_][x_] != 0: | |
# 检测左边 | |
if x_ < width // 2 and left is False: | |
num += 1 | |
left = True | |
# 检测右边 | |
elif x_ > width // 2 and right is False: | |
num += 1 | |
right = True | |
return True if num >= max_num else False | |
def locate_neck_above(): | |
""" | |
定位脖子的尖尖脚 | |
""" | |
# y_high就是脖子的最高点 | |
for y_ in range(y_high, height): | |
if scan(y_): | |
return y_ | |
y_start = locate_neck_above() | |
return y_start | |
def checkJaw(image:np.ndarray, y_start:int): | |
# 寻找"马鞍点" | |
_, _, _, a = cv2.split(image) # 这应该是一个四通道的图像 | |
height, width = a.shape | |
ret, a_thresh = cv2.threshold(a, 127, 255, cv2.THRESH_BINARY) # 将透明图层二值化 | |
if width <=1: raise TypeError("图像太小!") | |
x_left, x_right = 0, width - 1 | |
for x_left in range(width): | |
if a_thresh[y_start][x_left] != 0: | |
while a_thresh[y_start][x_left] != 0: x_left += 1 | |
break | |
for x_right in range(width-1, -1, -1): | |
if a_thresh[y_start][x_right] != 0: | |
while a_thresh[y_start][x_right] != 0: x_right -= 1 | |
break | |
point_list_y = [] | |
point_list_x = [] | |
for x in range(x_left, x_right): | |
y = y_start | |
while a_thresh[y][x] == 0: y += 1 | |
point_list_y.append(y) | |
point_list_x.append(x) | |
y = max(point_list_y) | |
x = point_list_x[point_list_y.index(y)] | |
return x, y | |
def checkHairLOrR(cloth_image_input_cut, | |
input_a, | |
neck_a, | |
cloth_image_input_top_y, | |
cutbar_top=0.4, | |
cutbar_bottom=0.5, | |
threshold=0.3): | |
""" | |
本函数用于检测衣服是否被头发遮挡,当前只考虑左右是否被遮挡,即"一刀切" | |
返回int | |
0代表没有被遮挡 | |
1代表左边被遮挡 | |
2代表右边被遮挡 | |
3代表全被遮挡了 | |
约定,输入的图像是一张灰度图,且被二值化过. | |
""" | |
def per_darkPoint(img:np.ndarray) -> int: | |
""" | |
用于遍历相加图像上的黑点. | |
然后返回黑点数/图像面积 | |
""" | |
h, w = img.shape | |
sum_darkPoint = 0 | |
for y in range(h): | |
for x in range(w): | |
if img[y][x] == 0: | |
sum_darkPoint += 1 | |
return sum_darkPoint / (h * w) | |
if threshold < 0 or threshold > 1: raise TypeError("阈值设置必须在0和1之间!") | |
# 裁出cloth_image_input_cut按高度40%~50%的区域-cloth_image_input_cutbar,并转换为A矩阵,做二值化 | |
cloth_image_input_height = cloth_image_input_cut.shape[0] | |
_, _, _, cloth_image_input_cutbar = cv2.split(cloth_image_input_cut[ | |
int(cloth_image_input_height * cutbar_top):int( | |
cloth_image_input_height * cutbar_bottom), :]) | |
_, cloth_image_input_cutbar = cv2.threshold(cloth_image_input_cutbar, 127, 255, cv2.THRESH_BINARY) | |
# 裁出input_image、neck_image的A矩阵的对应区域,并做二值化 | |
input_a_cutbar = input_a[cloth_image_input_top_y + int(cloth_image_input_height * cutbar_top): | |
cloth_image_input_top_y + int(cloth_image_input_height * cutbar_bottom), :] | |
_, input_a_cutbar = cv2.threshold(input_a_cutbar, 127, 255, cv2.THRESH_BINARY) | |
neck_a_cutbar = neck_a[cloth_image_input_top_y + int(cloth_image_input_height * cutbar_top): | |
cloth_image_input_top_y + int(cloth_image_input_height * cutbar_bottom), :] | |
_, neck_a_cutbar = cv2.threshold(neck_a_cutbar, 50, 255, cv2.THRESH_BINARY) | |
# 将三个cutbar合到一起-result_a_cutbar | |
input_a_cutbar = np.uint8(255 - input_a_cutbar) | |
result_a_cutbar = cv2.add(input_a_cutbar, cloth_image_input_cutbar) | |
result_a_cutbar = cv2.add(result_a_cutbar, neck_a_cutbar) | |
if_mask = 0 | |
# 我们将图像 一刀切,分为左边和右边 | |
height, width = result_a_cutbar.shape # 一通道图像 | |
left_image = result_a_cutbar[:, :width//2] | |
right_image = result_a_cutbar[:, width//2:] | |
if per_darkPoint(left_image) > threshold: | |
if_mask = 1 | |
if per_darkPoint(right_image) > threshold: | |
if_mask = 3 if if_mask == 1 else 2 | |
return if_mask | |
def find_black(image): | |
""" | |
找黑色点函数,遇到输入矩阵中的第一个黑点,返回它的y值 | |
""" | |
height, width = image.shape[0], image.shape[1] | |
for i in range(height): | |
for j in range(width): | |
if image[i, j] < 127: | |
return i | |
return None | |
def convert_black_array(image): | |
height, width = image.shape[0], image.shape[1] | |
mask = np.zeros([height, width]) | |
for j in range(width): | |
for i in range(height): | |
if image[i, j] > 127: | |
mask[i:, j] = 1 | |
break | |
return mask | |
def checkLongHair(neck_image, head_bottom_y, neck_top_y): | |
""" | |
长发检测函数,输入为head/neck图像,通过下巴是否为最低点,来判断是否为长发 | |
:return 0 : 短发 | |
:return 1 : 长发 | |
""" | |
jaw_y = neck_top_y + checkJaw(neck_image, y_start=checkSharpCorner(neck_image))[1] | |
if jaw_y >= head_bottom_y-3: | |
return 0 | |
else: | |
return 1 | |
def checkLongHair2(head_bottom_y, cloth_top_y): | |
if head_bottom_y > cloth_top_y+10: | |
return 1 | |
else: | |
return 0 | |
if __name__ == "__main__": | |
for i in range(1, 8): | |
img = cv2.imread(f"./neck_temp/neck_image{i}.png", cv2.IMREAD_UNCHANGED) | |
# new = transformationNeck(image=img, cutNeckHeight=419,neckBelow=472, toHeight=150) | |
# point_list = bestJunctionCheck(img, offset=5, stepSize=3) | |
# per = bestJunctionCheck(img, offset=5, stepSize=3) | |
# # 返回一个小数的形式, 接下来我将它处理为两个点 | |
point_list = [] | |
# y_high_, y_low_, _, _ = get_box_pro(image=img, model=1, conreection_factor=0) | |
# _y = y_high_ + int((y_low_ - y_high_) * per) | |
# _, _, _, a_ = cv2.split(img) # 这应该是一个四通道的图像 | |
# h, w = a_.shape | |
# r, a_t = cv2.threshold(a_, 127, 255, cv2.THRESH_BINARY) # 将透明图层二值化 | |
# _x = 0 | |
# for _x in range(w): | |
# if a_t[_y][_x] != 0: | |
# break | |
# point_list.append([_x, _y]) | |
# for _x in range(w - 1, -1, -1): | |
# if a_t[_y][_x] != 0: | |
# break | |
# point_list.append([_x, _y]) | |
y = checkSharpCorner(img) | |
point = checkJaw(image=img, y_start=y) | |
point_list.append(point) | |
new = draw_picture_dots(img, point_list, pen_size=2) | |
cv2.imshow(f"{i}", new) | |
cv2.waitKey(0) |