python+opencv图像处理(提取矩形方框 提取颜色 滤波 轮廓检测 直线检测)

一、依赖

如果你的python环境没有这些包,则需要安装

pip install numpy \ opencv-python \ pillow

二、使用指南

1、基本信息

  • 近期由于项目需要,需要使用python的opencv库处理图像,在下面的代码中使用了很多cv2的库函数,对这些函数进行了封装,可以直接拷贝调用.项目地址
  • 该部分有两个文件构成 main.py 和  replace.py
    运行main.py可读取文件夹下的图像,提取图像格子中的字母并保存到本地

2、图像处理流程

原图如下:

提取颜色
  • HSV特定颜色提取

separate_color_red 提取图像中的红色框线
用HSV提取红色部分,查看HSV颜色分量范围[点击此处]
(https://blog.csdn.net/u013270326/article/details/80704754)
参数解释: cv2.inRange(hsv, lowerb=lower_hsv, upperb=high_hsv)
第一个参数为输入图像,图像格式必须转为HSV格式,可通过cv2.cvtColor()转换
第二个参数分别为H,S,V的最低值
第三个参数分别为H,S,V的最高值

hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)  # 色彩空间转换为hsv,便于分离
lower_hsv = np.array([0, 43, 46])  # 提取颜色的低值
high_hsv = np.array([10, 255, 255])  # 提取颜色的高值
mask = cv2.inRange(hsv, lowerb=lower_hsv, upperb=high_hsv)

效果如下:

提取颜色
  • 中值滤波

medianBlur 中值滤波,过滤除最外层框线以外的线条
参数解释:cv2.medianBlur(img,19)
第一个参数:输入的图像
第二个参数:滤波模板的尺寸大小,必须是大于1的奇数,如3、5、7

mediu = cv2.medianBlur(img,19)  

效果如下:

中值滤波
  • HoughLinesP概率霍夫变换直线检测

调用直线检测函数需要先执行边缘检测函数,函数返回二值化图像
参数解释:cv2.Canny(img, 20, 250)
第一个参数:输入图像
第二个参数:阀值1,用于将这些间断的边缘连接起来
第三个参数:阀值2,用于检测图像中明显的边缘
参数解释:cv2.HoughLinesP(img, 1, np.pi / 180, 120, line, minLineLength,maxLineGap)
第一个参数:输入图像,必须为先用Canny边缘检测的图像
第二个参数:直线的半径,建议选1
第三个参数:步长为π/180的角来搜索所有可能的直线
第五个参数:线的最短长度,比这个线短的都会被忽略
第六个参数:两条线之间的最大间隔,如果小于此值,这两条线就会被看成一条线

# 直线检测
img2 = cv2.Canny(img, 20, 250)  #边缘检测
line = 4
minLineLength = 50
maxLineGap = 150
# HoughLinesP函数是概率直线检测,注意区分HoughLines函数
lines = cv2.HoughLinesP(img2, 1, np.pi / 180, 120, lines=line, minLineLength=minLineLength,maxLineGap=maxLineGap)
lines1 = lines[:, 0, :]  # 降维处理
# line 函数勾画直线
# (x1,y1),(x2,y2)坐标位置
# (0,255,0)设置BGR通道颜色
# 2 是设置颜色粗浅度
for x1, y1, x2, y2 in lines1:
    cv2.line(img, (x1, y1), (x2, y2), (255, 255, 255), 2)
  • findContours轮廓检测

轮廓检测,获取最外层矩形框的偏转角度angle

参数解释:image, contours, hier = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

第一个参数:二值化的图像,可以调用如下函数转为二值化图像

cv2.threshold(cv2.cvtColor(img, 127, 255, cv2.THRESH_BINARY))

第二个参数:表示轮廓的检索模式,有四种:
cv2.RETR_EXTERNAL表示只检测外轮廓
cv2.RETR_LIST检测的轮廓不建立等级关系
cv2.RETR_CCOMP建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息
cv2.RETR_TREE建立一个等级树结构的轮廓。
第三个参数:cv2.CHAIN_APPROX_NONE存储所有的轮廓点,相邻的两个点的像素位置差不超过1
cv2.CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标.
下面的代码对检测到的轮廓进行了筛选.并返回轮廓矩形坐标或倾斜角度

#检测轮廓
# ret, thresh = cv2.threshold(cv2.cvtColor(img, 127, 255, cv2.THRESH_BINARY))
image, contours, hier = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for c in contours:  #遍历轮廓
    rect = cv2.minAreaRect(c)  #生成最小外接矩形
    box_ = cv2.boxPoints(rect)
    h = abs(box_[3, 1] - box_[1, 1])
    w = abs(box_[3, 0] - box_[1, 0])
    print("宽,高",w,h)
    #只保留需要的轮廓
    if (h > 3000 or w > 2200):
        continue
    if (h < 2500 or w < 1500):
        continue
    box = cv2.boxPoints(rect)  # 计算最小面积矩形的坐标
    box = np.int0(box)  # 将坐标规范化为整数
    angle = rect[2]  #获取矩形相对于水平面的角度
    if angle > 0:
        if abs(angle) > 45:
            angle = 90 - abs(angle)
    else:
        if abs(angle) > 45:
            angle = (90 - abs(angle))
    # 绘制矩形
    # cv2.drawContours(img, [box], 0, (255, 0, 255), 3)
print("轮廓数量", len(contours))
  • rotate 旋转图像至水平方向

参数解释:cv2.getRotationMatrix2D(center, angle, 1)
第一个参数:旋转图像的基点,这里同过获取图像的宽高计算中心点
第二个参数:旋转角度
第三个参数:图像缩放因子
参数解释:cv2.warpAffine(img, img_ratete, (w, h))
第一个参数:原图像
第二个参数:从getRotationMatrix2D得到的图像
第三个参数:图像大小

(h, w) = img.shape[:2]  #获得图片高,宽
center = (w // 2, h // 2)  #获得图片中心点
img_ratete = cv2.getRotationMatrix2D(center, angle, 1)
rotated = cv2.warpAffine(img, img_ratete, (w, h))

效果如下:

中值滤波
  • cut1 通过图像坐标从外层矩形框处裁剪原图
沿边框裁剪
  • 裁剪图片
    裁剪图片可通过数组切片操作完成,进行此操作打开图像必须用cv2.imiread(“path”)加载图像
img[y1:y2, x1:x2]  #切片裁剪图像

效果如图:

沿方格裁剪
  • replace 消除多余红色框线
    如下代码可实现特定像素的替换,这个把红色替换为白色,进行此操作打开图像必须用PIL库函数Image.open(“path”)加载图像
img2 = Image.open(path3)
img2 = img2.convert('RGBA')  # 图像格式转为RGBA
pixdata = img2.load()
for y in range(img2.size[1]):
    for x in range(img2.size[0]):
        if pixdata[x, y][0] > 220:  # 红色像素
            pixdata[x, y] = (255, 255, 255, 255)  # 替换为白色,参数分别为(R,G,B,透明度)
img2 = img2.convert('RGB')  # 图像格式转为RGB
print("替换文件",pathd)
img2.save(path3)

效果如图:

消除多余框线

3、完整代码

#main.py
import cv2
import numpy as np
import os
import replace


def separate_color_red(img):
    #颜色提取
    # gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)  # 色彩空间转换为hsv,便于分离
    lower_hsv = np.array([0, 43, 46])  # 提取颜色的低值
    high_hsv = np.array([10, 255, 255])  # 提取颜色的高值
    mask = cv2.inRange(hsv, lowerb=lower_hsv, upperb=high_hsv)
    print("颜色提取完成")
    return mask


def salt(img, n):
    #椒盐去燥
    for k in range(n):
        i = int(np.random.random() * img.shape[1])
        j = int(np.random.random() * img.shape[0])
        if img.ndim == 2:
            img[j, i] = 255
        elif img.ndim == 3:
            img[j, i, 0] = 255
            img[j, i, 1] = 255
            img[j, i, 2] = 255
        print("去燥完成")
        return img


def show(name, img):
    #显示图片
    cv2.namedWindow(str(name), cv2.WINDOW_NORMAL)
    cv2.resizeWindow(str(name), 800, 2000)  # 改变窗口大小
    cv2.imshow(str(name), img)


def lines(img):
    # 直线检测
    img2 = cv2.Canny(img, 20, 250)  #边缘检测
    line = 4
    minLineLength = 50
    maxLineGap = 150
    # HoughLinesP函数是概率直线检测,注意区分HoughLines函数
    lines = cv2.HoughLinesP(img2, 1, np.pi / 180, 120, lines=line, minLineLength=minLineLength,maxLineGap=maxLineGap)
    lines1 = lines[:, 0, :]  # 降维处理
    # line 函数勾画直线
    # (x1,y1),(x2,y2)坐标位置
    # (0,255,0)设置BGR通道颜色
    # 2 是设置颜色粗浅度
    for x1, y1, x2, y2 in lines1:
        cv2.line(img, (x1, y1), (x2, y2), (255, 255, 255), 2)
    return img


def contour(img):
    #检测轮廓
    # ret, thresh = cv2.threshold(cv2.cvtColor(img, 127, 255, cv2.THRESH_BINARY)) 
    image, contours, hier = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    for c in contours:  #遍历轮廓
        rect = cv2.minAreaRect(c)  #生成最小外接矩形
        box_ = cv2.boxPoints(rect)
        h = abs(box_[3, 1] - box_[1, 1])
        w = abs(box_[3, 0] - box_[1, 0])
        print("宽,高",w,h)
        #只保留需要的轮廓
        if (h > 3000 or w > 2200):
            continue
        if (h < 2500 or w < 1500):
            continue
        box = cv2.boxPoints(rect)  # 计算最小面积矩形的坐标
        box = np.int0(box)  # 将坐标规范化为整数
        angle = rect[2]  #获取矩形相对于水平面的角度
        if angle > 0:
            if abs(angle) > 45:
                angle = 90 - abs(angle)
        else:
            if abs(angle) > 45:
                angle = (90 - abs(angle))
        # 绘制矩形
        # cv2.drawContours(img, [box], 0, (255, 0, 255), 3)
    print("轮廓数量", len(contours))
    return img, box, angle


def rotate(img, angle):
    #旋转图片
    (h, w) = img.shape[:2]  #获得图片高,宽
    center = (w // 2, h // 2)  #获得图片中心点
    img_ratete = cv2.getRotationMatrix2D(center, angle, 1)
    rotated = cv2.warpAffine(img, img_ratete, (w, h))
    return rotated


def cut1(img, box):
    #从轮廓出裁剪图片
    x1, y1 = box[1]  #获取左上角坐标
    x2, y2 = box[3]  #获取右下角坐标
    img_cut = img[y1+10:y2-10, x1+10:x2-10]  #切片裁剪图像
    return img_cut


def cut2(img, out_path, filed):
    #裁剪方格中图像并保存
    #@config i_:i为裁剪图像的y坐标区域, j_:j为裁剪图像的x坐标区域

    if not os.path.isdir(out_path):  #创建文件夹
        os.makedirs(out_path)
    h, w, _ = img.shape  #获取图像通道
    print(h, w)
    s,i_ = 0,0
    #循环保存图像
    for i in range(h//12,h,h//12):
        j_ = 0
        for j in range(w//8,w,w//8):
            imgd = img[i_ + 5:i - 5, j_ + 5:j - 5]
            # img_list.append(img[i_+10:i-10,j_+10:j-10])
            out_pathd =  out_path+filed[:-4]+"_"+str(s)+".jpg"  #图像保存路径
            cv2.imwrite(out_pathd,imgd)
            print("保存文件",out_pathd)
            s += 1
            j_ = j
        i_ = i

if __name__ == "__main__":
    path = "/home/crxm/C1/"      #读取图像文件夹
    put_path = "/home/crxm/C2/"  #保存图像文件夹
    for file in os.listdir(path):  #遍历访问图像
        pathd = path + file + "/"
        out_path = put_path + file + "/"
        for filed in os.listdir(pathd):
            img_path = (pathd + filed)
            img = cv2.imread(img_path)  #读取图像
            img_separate = separate_color_red(img)  #提取红色框先
            mediu = cv2.medianBlur(img_separate,19) #中值滤波,过滤除最外层框线以外的线条
            img_lines = lines(mediu)  #直线检测,补充矩形框线
            img_contours,box,angle = contour(img_lines)  #轮廓检测,获取最外层矩形框的偏转角度
            print("角度",angle,"坐标",box)
            img_rotate = rotate(img_lines,angle)  #旋转图像至水平方向
            img_contours,box, _ = contour(img_rotate)   #获取水平矩形框的坐标

            img_original_rotate = rotate(img,angle)  #旋转原图至水平方向
            img_original_cut = cut1(img_original_rotate,box)  #通过图像坐标从外层矩形框处裁剪原图
            cut2(img_original_cut,out_path,filed)  #裁剪方格保存
            replace.replace(put_path)  #消除多余红色框线
            # show("img",img)
            # show("img_separate", img_separate)
            # show("mediu", mediu)
            # show("img_lines", img_lines)
            # show("img_contours", img_contours)
            # show("img__rotate",img_rotate)
            # show("img_original_cut",img_original_cut)
            cv2.waitKey(0)
            cv2.destroyAllWindows()


#replace.py
from PIL import Image
import os

def replace(img_path):
    for path in os.listdir(img_path):
        path2 = img_path + path + "/"
        for pathd in os.listdir(path2):
            path3 = path2 + pathd
            img2 = Image.open(path3)
            img2 = img2.convert('RGBA')  # 图像格式转为RGBA
            pixdata = img2.load()
            for y in range(img2.size[1]):
                for x in range(img2.size[0]):
                    if pixdata[x, y][0] > 220:  # 红色像素
                        pixdata[x, y] = (255, 255, 255, 255)  # 替换为白色,参数分别为(R,G,B,透明度)
            img2 = img2.convert('RGB')  # 图像格式转为RGB
            print("替换文件",pathd)
            img2.save(path3)
#红色像素替换为白色
  • 64
    点赞
  • 607
    收藏
    觉得还不错? 一键收藏
  • 23
    评论
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值