项目一改进:调节图片的曝光度并对通过透视变换矫正图片(解决图中所有最小覆盖矩形标记指定坐标时提取box中四个顶点顺序不一致问题)

本篇中,我们需要完成以下任务:

1、调节图片曝光率,使得图片颜色显得均匀
2、提取标记块轮廓,单独标记出其坐标x值
3、通过透视变换对调节好曝光度的图片进行矫正
4、对矫正处理好的图片重复第二步操作并做出对比

开始操作吧

一、读取原图

# 1.读取图片
# 使用函数cv2.imread(filepath,flags)读入一副图片
image = cv2.imread("C:/myprojects/expose_img.jpg")
print("original shape:{}".format(image.shape))
# (5792,4344,3)
# 源图像:3通道图像
height, width, channel = image.shape
# 通过resize()函数进行图像缩放
# 插值方法:基于4x4像素邻域的3次插值法
image = cv2.resize(image, (int(0.125*width), int(0.125*height)), interpolation=cv2.INTER_CUBIC)
# 使用函数cv2.imshow(wname,img)显示图像,第一个参数是显示图像的窗口的名字,第二个参数是要显示的图像(imread读入的图像)
cv2.imshow("original", image)

在这里插入图片描述
二、调节曝光度
详细解读🔗

#调节曝光度,使图片颜色更加均匀
def gamma_trans(img,gamma):#gamma函数处理
    gamma_table=[np.power(x/255.0,gamma)*255.0 for x in range(256)]#建立映射表
    gamma_table=np.round(np.array(gamma_table)).astype(np.uint8)#颜色值为整数
    return cv2.LUT(img,gamma_table)#图片颜色查表;另外可以根据光强(颜色)均匀化原则设计自适应算法。
#gamma大于1曝光度下降,大于0小于1曝光度增强
value_of_gamma = 2.5
image_gamma_correct=gamma_trans(image,value_of_gamma)
cv2.imshow("after_expose",image_gamma_correct)
cv2.imwrite("C:/myprojects/after_expose.jpg", image_gamma_correct)

在这里插入图片描述
三、提取图片黄色部分并做腐蚀处理

# 2.提取图片中的黄色部分
hsv = cv2.cvtColor(image_gamma_correct, cv2.COLOR_BGR2HSV)
low_hsv = np.array([26,43,46])
high_hsv = np.array([34,255,255])
mask = cv2.inRange(hsv,lowerb=low_hsv,upperb=high_hsv)
cv2.imshow("find_yellow",mask)

# 3.预处理
#腐蚀,消除图像边缘小的部分,并将图像缩小,从而使其补集扩大,原型为:dst=cv2.erode(src表示原图像,kernel表示卷积核,iterations表示迭代次数)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))  #设置kernel卷积核为 3 * 3 正方形,8位uchar型,全1结构元素
mask = cv2.erode(mask, kernel,15)
cv2.imshow("morphology", mask)

观察发现:经过腐蚀处理的图像明显更加干净,剔除了许多细小轮廓
在这里插入图片描述
四、查找轮廓以及筛选轮廓

# 4.查找轮廓,输出找到的轮廓个数为2个
contours, hierarchy = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
print("find", len(contours), "contours")

# 绘制轮廓函数
# 自定义绘制轮廓的函数(为简化操作)
# 输入1:winName:窗口名
# 输入2:image:原图
# 输入3:contours:轮廓
# 输入4:draw_on_blank:绘制方式,True在白底上绘制,False:在原图image上绘制
def drawMyContours(winName, image, contours, draw_on_blank):
    # cv2.drawContours(image, contours, index, color, line_width)
    # 输入参数:
    # image:与原始图像大小相同的画布图像(也可以为原始图像)
    # contours:轮廓(python列表)
    # index:轮廓的索引(当设置为-1时,绘制所有轮廓)
    # color:线条颜色,
    # line_width:线条粗细
    # 返回绘制了轮廓的图像image
    if (draw_on_blank): # 在白底上绘制轮廓
        temp = np.ones(image.shape, dtype=np.uint8) * 255
        cv2.drawContours(temp, contours, -1, (0, 0, 0), 2)
    else:
        temp = image.copy()
        cv2.drawContours(temp, contours, -1, (0, 0, 255), 2)
    cv2.imshow(winName, temp)

# 5.绘制原始轮廓
drawMyContours("find contours", image_gamma_correct, contours, True)

#  自定义函数:用于删除列表指定序号的轮廓
#  输入 1:contours:原始轮廓
#  输入 2:delete_list:待删除轮廓序号列表
#  返回值:contours:筛选后轮廓
def delet_contours(contours, delete_list):
    delta = 0
    for i in range(len(delete_list)):
        # print("i= ", i)
        del contours[delete_list[i] - delta]
        delta = delta + 1
    return contours

# 6.筛选轮廓,计算每个轮廓长度
lengths = list()
for i in range(len(contours)):
    length = cv2.arcLength(contours[i], True)
    lengths.append(length)
    print("轮廓%d 的周长: %d" % (i, length))

# 使用轮廓长度滤波
min_size = 130
max_size = 165
delete_list = []
for i in range(len(contours)):
    if (cv2.arcLength(contours[i], True) < min_size) or (cv2.arcLength(contours[i], True) > max_size):
        delete_list.append(i)

# 根据列表序号删除不符合要求的轮廓
contours = delet_contours(contours, delete_list)#筛选后的轮廓
print("find", len(contours), "contours left after length filter")#打印筛选后的轮廓
drawMyContours("contours after length filtering", image_gamma_correct, contours, False)

在这里插入图片描述
五、绘制各标记块的最小覆盖矩形并标记左上角坐标X值

# 7.形状描述子
# 7.1 最小覆盖矩形
# 标记左上角坐标点(轮廓和点在同一张图中显示)
for i in range(len(contours)):
    rect = cv2.minAreaRect(contours[i])
    box = cv2.boxPoints(rect)
    box = np.int0(box)
    draw_rect = cv2.drawContours(image_gamma_correct.copy(), [box], -1, (0, 0, 255), 2)
    print("box_{}:{}".format(i,box))
    # 左上角坐标值
    pt = (box[1][0], box[1][1])
    # 画绿点
    circle = cv2.circle(draw_rect.copy(), pt, 2, (0, 255, 0), 2)
    text = "x=" + str(pt[0])
    all = cv2.putText(circle.copy(), text, (pt[0] + 40, pt[1] + 40), cv2.FONT_HERSHEY_PLAIN, 2, (255, 255, 255), 2, 8, 0)
    image_gamma_correct = all
cv2.imshow("all_res", all)

采用如上代码,即之前项目中最常用的方法之后,我发现这次并没有一次性成功标记出所有标记块左上角的坐标,也就是说得到的box变量中矩形四个顶点的顺序是不一致的,在网上查阅资料后得出结论:储存顶点的顺序会因为轮廓的不同而不同。所以对于图中的两个标记块,我们不能同一使用 pt = (box[1][0], box[1][1])来获取左上角坐标值。
在这里插入图片描述
在这里插入图片描述

解决办法:根据左上角坐标具有的共性,自定义了一个获取左上角坐标的函数

共性:对于box中的四个顶点坐标值(x, y),首先对y值进行排序,找出两个最小的y值,再在最小y值对应的两个坐标中对比哪个的x值更小,哪个就是筛选出来的左上角坐标

def find_left_high_pt(box):
    # 由于四个点顺序不定,该函数根据左上角坐标点的特性筛选出左上角坐标点
    list_y = []
    for i in range(4):
        list_y.append(box[i][1])
    list_y = sorted(list_y)
    min_two_y = list_y[:2]
    #print(min_two_y)
    min_two_x = []
    for i in range(4):
        if box[i][1] in min_two_y:
            min_two_x.append(box[i][0])
    min_two_x = sorted(min_two_x)
    #print(min_two_x)
    min_x = min_two_x[0]
    #print(min_x)
    for i in range(4):
        if box[i][0] == min_x:
            pt = (box[i][0], box[i][1])
    return pt

完整代码如下:

for i in range(len(contours)):
    rect = cv2.minAreaRect(contours[i])
    box = cv2.boxPoints(rect)
    box = np.int0(box)
    draw_rect = cv2.drawContours(image_gamma_correct.copy(), [box], -1, (0, 0, 255), 2)
    print("box_{}:{}".format(i,box))
    # 左上角坐标值
    pt = find_left_high_pt(box)
    print("左上角坐标点为:{}".format(pt))
    print("左上角坐标x的值为:{}".format(pt[0]))
    # 画绿点
    circle = cv2.circle(draw_rect.copy(), pt, 2, (0, 255, 0), 2)
    text = "x=" + str(pt[0])
    all = cv2.putText(circle.copy(), text, (pt[0] + 40, pt[1] + 40), cv2.FONT_HERSHEY_PLAIN, 2, (255, 255, 255), 2, 8, 0)
    image_gamma_correct = all
cv2.imshow("before_res", all)

这样,就可以成功标记出轮廓左上角坐标X值啦
在这里插入图片描述
六、对曝光处理后的图片进行透视变换矫正
通过查阅以及学习网络上一些关于通过透视变换方法矫正图片的知识,发现几乎所有的目标图片都是有明显外部轮廓的(详细解读🔗
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
困难:而我的这张原图并没有具体的某一条外部轮廓,根本提取不到四个角点
解决办法:通过鼠标控制点击,自定义四个角点(参考学习🔗

# 1.图像矫正——透视变换
# 1.1 本例无明显外部轮廓:故自定义四角点,点击4角点进行透视变换
img = cv2.imread('C:/myprojects/after_expose.jpg')  # 输入图像
p_list = []  # 左上,右上,左下,右下顺序点击
dst_point = (543, 724)  # 变换目标大小,自己根据需要设置

def capture_event(event, x, y, flags, params):
    if event == cv2.EVENT_LBUTTONDOWN:
        # create a circle at that position
        # of radius 30 and color greeen
        cv2.circle(img, (x, y), 2, (0, 0, 255), -1)
        cv2.imshow("need_rectify", img)
        p_list.append([x, y])
        if len(p_list) == 4:
            pts1 = np.float32(p_list)
            pts2 = np.float32(
                [[0, 0], [dst_point[0], 0], [0, dst_point[1]], [dst_point[0], dst_point[1]]])
            dst = cv2.warpPerspective(img.copy(), cv2.getPerspectiveTransform(pts1, pts2), dst_point)
            #cv2.namedWindow("rectify_res", cv2.WINDOW_NORMAL)
            cv2.imshow("rectify_res", dst)
            cv2.imwrite('C:/myprojects/rectify_res.jpg', dst)  # 输出图像
cv2.imshow("need_rectify", img)
cv2.setMouseCallback("need_rectify", capture_event)

在这里插入图片描述
这样,就可以成功得到矫正后的图片了,将其保存好,对其进行上面同样的操作,完整代码如下:

# 1.2读取图片
# 使用函数cv2.imread(filepath,flags)读入一副图片
image = cv2.imread("C:/myprojects/rectify_res.jpg")
print("original shape:{}".format(image.shape))
# (5792,4344,3)
# 源图像:3通道图像
height, width, channel = image.shape
# 通过resize()函数进行图像缩放
# 插值方法:基于4x4像素邻域的3次插值法
image = cv2.resize(image, (int(width), int(height)), interpolation=cv2.INTER_CUBIC)
# 使用函数cv2.imshow(wname,img)显示图像,第一个参数是显示图像的窗口的名字,第二个参数是要显示的图像(imread读入的图像)
cv2.imshow("original", image)

'''
#调节曝光度,使图片颜色更加均匀
def gamma_trans(img,gamma):#gamma函数处理
    gamma_table=[np.power(x/255.0,gamma)*255.0 for x in range(256)]#建立映射表
    gamma_table=np.round(np.array(gamma_table)).astype(np.uint8)#颜色值为整数
    return cv2.LUT(img,gamma_table)#图片颜色查表;另外可以根据光强(颜色)均匀化原则设计自适应算法。
#gamma大于1曝光度下降,大于0小于1曝光度增强
value_of_gamma = 2.5
image_gamma_correct=gamma_trans(image,value_of_gamma)
cv2.imshow("after_expose",image_gamma_correct)
cv2.imwrite("C:/myprojects/after_expose.jpg", image_gamma_correct)
'''

# 2.提取图片中的黄色部分
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
low_hsv = np.array([26,43,46])
high_hsv = np.array([34,255,255])
mask = cv2.inRange(hsv,lowerb=low_hsv,upperb=high_hsv)
cv2.imshow("find_yellow",mask)

# 3.预处理
#腐蚀,消除图像边缘小的部分,并将图像缩小,从而使其补集扩大,原型为:dst=cv2.erode(src表示原图像,kernel表示卷积核,iterations表示迭代次数)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))  #设置kernel卷积核为 3 * 3 正方形,8位uchar型,全1结构元素
mask = cv2.erode(mask, kernel,15)
cv2.imshow("morphology", mask)

# 4.查找轮廓,输出找到的轮廓个数为2个
contours, hierarchy = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
print("find", len(contours), "contours")

# 绘制轮廓函数
# 自定义绘制轮廓的函数(为简化操作)
# 输入1:winName:窗口名
# 输入2:image:原图
# 输入3:contours:轮廓
# 输入4:draw_on_blank:绘制方式,True在白底上绘制,False:在原图image上绘制
def drawMyContours(winName, image, contours, draw_on_blank):
    # cv2.drawContours(image, contours, index, color, line_width)
    # 输入参数:
    # image:与原始图像大小相同的画布图像(也可以为原始图像)
    # contours:轮廓(python列表)
    # index:轮廓的索引(当设置为-1时,绘制所有轮廓)
    # color:线条颜色,
    # line_width:线条粗细
    # 返回绘制了轮廓的图像image
    if (draw_on_blank): # 在白底上绘制轮廓
        temp = np.ones(image.shape, dtype=np.uint8) * 255
        cv2.drawContours(temp, contours, -1, (0, 0, 0), 2)
    else:
        temp = image.copy()
        cv2.drawContours(temp, contours, -1, (0, 0, 255), 2)
    cv2.imshow(winName, temp)

# 5.绘制原始轮廓
drawMyContours("find contours", image, contours, True)

#  自定义函数:用于删除列表指定序号的轮廓
#  输入 1:contours:原始轮廓
#  输入 2:delete_list:待删除轮廓序号列表
#  返回值:contours:筛选后轮廓
def delet_contours(contours, delete_list):
    delta = 0
    for i in range(len(delete_list)):
        # print("i= ", i)
        del contours[delete_list[i] - delta]
        delta = delta + 1
    return contours

# 6.筛选轮廓,计算每个轮廓长度
lengths = list()
for i in range(len(contours)):
    length = cv2.arcLength(contours[i], True)
    lengths.append(length)
    print("轮廓%d 的周长: %d" % (i, length))

# 使用轮廓长度滤波
min_size = 150
max_size = 170
delete_list = []
for i in range(len(contours)):
    if (cv2.arcLength(contours[i], True) < min_size) or (cv2.arcLength(contours[i], True) > max_size):
        delete_list.append(i)

# 根据列表序号删除不符合要求的轮廓
contours = delet_contours(contours, delete_list)#筛选后的轮廓
print("find", len(contours), "contours left after length filter")#打印筛选后的轮廓
drawMyContours("contours after length filtering", image, contours, False)

# 7.形状描述子
# 7.1 最小覆盖矩形
# 标记左上角坐标点(轮廓和点在同一张图中显示)
def find_left_high_pt(box):
    # 由于四个点顺序不定,该函数根据左上角坐标点的特性筛选出左上角坐标点
    list_y = []
    for i in range(4):
        list_y.append(box[i][1])
    list_y = sorted(list_y)
    min_two_y = list_y[:2]
    #print(min_two_y)
    min_two_x = []
    for i in range(4):
        if box[i][1] in min_two_y:
            min_two_x.append(box[i][0])
    min_two_x = sorted(min_two_x)
    #print(min_two_x)
    min_x = min_two_x[0]
    #print(min_x)
    for i in range(4):
        if box[i][0] == min_x:
            pt = (box[i][0], box[i][1])
    return pt
for i in range(len(contours)):
    rect = cv2.minAreaRect(contours[i])
    box = cv2.boxPoints(rect)
    box = np.int0(box)
    draw_rect = cv2.drawContours(image.copy(), [box], -1, (0, 0, 255), 2)
    print("box_{}:{}".format(i,box))
    # 左上角坐标值
    pt = find_left_high_pt(box)
    print("左上角坐标点为:{}".format(pt))
    print("左上角坐标x的值为:{}".format(pt[0]))
    # 画绿点
    circle = cv2.circle(draw_rect.copy(), pt, 2, (0, 255, 0), 2)
    text = "x=" + str(pt[0])
    all = cv2.putText(circle.copy(), text, (pt[0] + 40, pt[1] + 40), cv2.FONT_HERSHEY_PLAIN, 2, (255, 255, 255), 2, 8, 0)
    image = all
  
cv2.imshow("before_res", all)

在这里插入图片描述
可以发现,矫正前后坐标发生了变化!

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值