python opencv:批量识别拼接图片分界线并进行自动裁剪

一 项目概要

在网上找图片素材时,有很多的图片是长图片,在一张图片上拼接了许多张图片,而很多时候我们需要单张图片,此时就需要将长图进行裁剪,一般可以用图片工具进行简单裁剪,高级点可以采用ps进行切片处理,如果图片数量少还好说一旦有大量的图片需要裁剪就很繁琐并且费时费力。这时就会想用自动裁剪工具进行裁剪,而网上的大多数工具都是定尺寸裁剪需要手动输入裁剪位置,这种还是没法实现自动识别图片间的分界线并裁剪的操作。而此时想起曾学习过一点机器视觉的内容,就尝试用python和opencv实现整个操作过程,来识别图片分界线并自动裁剪保存。
网图示例

二 项目流程

实现这个过程的大致流程是先扫描给定文件下的所有文件,对扫描出来的文件名进行取后缀处理,将文件后缀与图片类型进行比对,如是图像文件则先将图片转换为灰度图然后去噪,再进行边缘轮廓提取,得到图像边缘轮廓后可以进行图像形态学处理使得分界线更明显,再运用霍夫直线变换进行直线检测,对直线检测的结果进行处理只留下水平直线,将直线的纵坐标提取并以升序排序,排序后进行裁剪保存,如此循环直到将文件夹下的图像文件处理完。而实现以上过程的难点就在于提取出图 片的轮廓。

三 图像边缘提取

由于需要提取图片的边缘轮廓,想到可以用Sobel算子进行处理。图像梯度计算的是图像变化的速度,对于图像的边缘部分,其灰度值变化较大,梯度值也较大;相反,对于图像中比较平滑的部分,其灰度值变化较小,相应的梯度值也较小。一般情况下,图像梯度计算的是图像的边缘信息。Sobel 算子是一种离散的微分算子,该算子结合了高斯平滑和微分求导运算。该算子利用局部差分寻找边缘,计算所得的是一个梯度的近似值。我们想要得到的是水平分界线,可以计算水平垂直方向的偏导数从而得到边缘轮廓。
在opencv中应用sobel算子计算计算图像梯度

import cv2
import numpy as np

# 加载图片并转换为灰度图
img = cv2.imread(r"D:\Image\lsfjlejljlkk (2).jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 对灰度图进行高斯模糊
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
# 使用Sobel算子计算梯度
grad_x = cv2.Sobel(blurred, cv2.CV_16S, 1, 0)
grad_y = cv2.Sobel(blurred, cv2.CV_16S, 0, 1)
# 将梯度转换为绝对值
abs_grad_x = cv2.convertScaleAbs(grad_x)
abs_grad_y = cv2.convertScaleAbs(grad_y)
# 合并梯度
grad = cv2.addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0)
# 对梯度进行二值化处理
thresh,binary=cv2.threshold(grad,80,255,cv2.THRESH_BINARY)
cv2.imwrite(r"D:\Image\lsfjlejljlkk (2).jpg", binary,[cv2.IMWRITE_JPEG_QUALITY,98])

图片处理后的结果
在这里插入图片描述
除了应用sobel算子还可以使用Canny 边缘检测

import cv2
import numpy as np

# 加载图片并转换为灰度图
img = cv2.imread(r"D:\Image\lsfjlejljlkk (2).jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#cany处理
cany=cv2.Canny(gray, 50, 150, apertureSize=3)
cv2.imwrite(r"D:\Image\lsfjlejljlkk (3).jpg", cany,[cv2.IMWRITE_JPEG_QUALITY,98])

在这里插入图片描述
从以上结果可以看出来不管是用sobel还是cany,处理后的图像都能明显找出分界线,但仔细观察就会发现图像中除了有分界线还有其他元素,并且分界线不是很连续有锯齿状,可以考虑使用形态学进行处理,先对图像进行膨胀后在进行腐蚀

import cv2
import numpy as np

# 加载图片并转换为灰度图
img = cv2.imread(r"D:\Image\46777cb7e3ae46d6b724b377a6dac721.jpeg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray_filtering=cv2.GaussianBlur(gray,(3,3),0,0)
cany = cv2.Canny(gray_filtering, 50, 150, apertureSize=3)
kernel = np.ones((2, 9), np.uint8)
kernel1 = np.ones((1, 40), np.uint8)
cany = cv2.dilate(cany, kernel, iterations=2)
cany = cv2.erode(cany, kernel1, iterations=3)  # 腐蚀
kernel = np.ones((5, 1), np.uint8)
morphology1 = cv2.erode(cany, kernel, iterations=5)
morphology2 = cv2.subtract(cany, morphology1)#相减除去多余元素
cv2.imwrite(r"D:\Image\lsfjlejljlkk (5).jpg", morphology2,[cv2.IMWRITE_JPEG_QUALITY,98])

在这里插入图片描述
经过膨胀腐蚀处理后分界线更加明显,且直线的连续性更好了。

四 直线检测

霍夫变换是一种在图像中寻找直线、圆形以及其他简单形状的方法。霍夫变换采用类似于
投票的方式来获取当前图像内的形状集合,该变换由 Paul Hough(霍夫)于 1962 年首次提出。
最初的霍夫变换只能用于检测直线,经过发展后,霍夫变换不仅能够识别直线,还能识别其他
简单的图形结构,常见的有圆、椭圆等。
本次用opencv提供的函数cv2.HoughLinesP()进行直线检测

import cv2
import numpy as np

Quantity_factor=0.65
Interval_factor=0.25
img = cv2.imread(r"D:\Image\46777cb7e3ae46d6b724b377a6dac721.jpeg")
high=img.shape[0]
wide=img.shape[1]
Quantity =int(Quantity_factor*wide)
Interval =int(Interval_factor*wide)
# 加载图片并转换为灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray_filtering=cv2.GaussianBlur(gray,(3,3),0,0)
#使用cany处理图像
cany = cv2.Canny(gray_filtering, 50, 150, apertureSize=3)
kernel = np.ones((2, 9), np.uint8)
kernel1 = np.ones((1, 40), np.uint8)
cany = cv2.dilate(cany, kernel, iterations=2)
cany = cv2.erode(cany, kernel1, iterations=3)  # 腐蚀
kernel = np.ones((5, 1), np.uint8)
morphology1 = cv2.erode(cany, kernel, iterations=5)
morphology2 = cv2.subtract(cany, morphology1)#相减除去多余元素
lines = cv2.HoughLinesP(morphology2,1, np.pi/2,Quantity,maxLineGap=Interval)  #返回值是三维数组
if lines is not None:
    lines_img = new_img(high, wide)
    for line in lines:
        x1, y1, x2, y2 = line[0]
        cv2.line(lines_img, (x1, y1), (x2, y2), 255, 1)
    cv2.imwrite(r"D:\Image\lsfjlejljlkk (6).jpg", lines_img, [cv2.IMWRITE_JPEG_QUALITY, 98])
else:
    print("没有找到图片裁剪线")

在这里插入图片描述
以上就是找出的图片分界线
最后进行裁剪操作就能得到单张图像了

import cv2
import numpy as np

Quantity_factor=0.65
Interval_factor=0.25
img = cv2.imread(r"D:\Image\46777cb7e3ae46d6b724b377a6dac721.jpeg")
high=img.shape[0]
wide=img.shape[1]
Quantity =int(Quantity_factor*wide)
Interval =int(Interval_factor*wide)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray_filtering=cv2.GaussianBlur(gray,(3,3),0,0)

cany = cv2.Canny(gray_filtering, 50, 150, apertureSize=3)
kernel = np.ones((2, 9), np.uint8)
kernel1 = np.ones((1, 40), np.uint8)
cany = cv2.dilate(cany, kernel, iterations=2)
cany = cv2.erode(cany, kernel1, iterations=3)  # 腐蚀
kernel = np.ones((5, 1), np.uint8)
morphology1 = cv2.erode(cany, kernel, iterations=5)
morphology2 = cv2.subtract(cany, morphology1)#相减除去多余元素
lines = cv2.HoughLinesP(morphology2,1, np.pi/2,Quantity,maxLineGap=Interval)  #返回值是三维数组
if lines is not None:
    lines_img = new_img(high, wide)
    for line in lines:
        x1, y1, x2, y2 = line[0]
        cv2.line(lines_img, (x1, y1), (x2, y2), 255, 1)
    cv2.imwrite(r"D:\Image\lsfjlejljlkk (6).jpg", lines_img, [cv2.IMWRITE_JPEG_QUALITY, 98])
    fenjieY = []
    for lint in range(len(lines)):
        if lines[lint][0][1] == lines[lint][0][3] and lines[lint][0][1] > 0 and lines[lint][0][1] < high - 1:
            fenjieY.append(lines[lint][0][1])
    fenjieY.sort()
    for lie in range(len(fenjieY) - 1):
        if fenjieY[lie + 1] - fenjieY[lie] < 5:
            xz = int((fenjieY[lie] + fenjieY[lie + 1]) / 2)
            fenjieY[lie] = xz
            fenjieY[lie + 1] = xz
    fenjieY.extend([0, high - 1])
    fenjieY = list(set(fenjieY))
    fenjieY.sort()
    for lie in range(len(fenjieY) - 1):
        if fenjieY[lie + 1] - fenjieY[lie] > 500:
            img1 = img[fenjieY[lie]:fenjieY[lie + 1] + 1, 0:wide]
            dizhi = r"D:\Image" + "\\" + "lsfjlejljlkk_" + str(lie) + ".jpg"
            cv2.imwrite(dizhi, img1, [cv2.IMWRITE_JPEG_QUALITY, 98])
else:
    print("没有找到图片裁剪线")

以下就是拆分结果
第一张
在这里插入图片描述
第二张
在这里插入图片描述

五 项目代码

以下程序可以自动运行,只需要提供原始图像文件路径和拆分后的输出路径,若发现效果不理想可以自行更改参数进行调整。

import os
import cv2
import numpy as np
#读取文件列表并加载相关参数
img_list=[".jpg",".png",".jpeg",".bmp"]
bassIn_path=r"D:\Image\fen1"
bassOut_path=r"D:\Image\fen1\10"
Quantity_factor=0.65     
Interval_factor=0.25
path_list = os.listdir(bassIn_path)
#Cany处理
def canny(img):
    cany = cv2.Canny(img, 50, 150, apertureSize=3)
    kernel = np.ones((2, 9), np.uint8)
    kernel1 = np.ones((1, 40), np.uint8)
    cany = cv2.dilate(cany, kernel, iterations=2)
    cany = cv2.erode(cany, kernel1, iterations=3)  # 形态学处理 腐蚀
    return cany
#形态学处理
def morphology(img):
    kernel = np.ones((5, 1), np.uint8)
    morphology1 = cv2.erode(img, kernel, iterations=5)  # 形态学处理 腐蚀
    morphology2=cv2.subtract(img, morphology1)
    return morphology2

def new_img(high,wide):
    img = np.zeros((high, wide), np.uint8)
    # 黑色色背景
    img.fill(0)
    return img

#循环处理每个文件
for path_name in path_list:
    if os.path.splitext(path_name)[-1].lower() in img_list:
        iput_path_bass = path_name.replace(os.path.splitext(path_name)[-1],'')
        iput_path=bassIn_path+"\\"+path_name
        # 读入图片
        img = cv2.imread(iput_path)
        print(path_name,"图像属性:",img.shape)
        high=img.shape[0]
        wide=img.shape[1]
        Quantity =int(Quantity_factor*wide)
        Interval =int(Interval_factor*wide)
        # 转为灰度图
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        gray_filtering=cv2.GaussianBlur(gray,(3,3),0,0)
        #进行cany处理
        edges=canny(gray_filtering)
        dizhi1 = bassOut_path + "\\" + iput_path_bass + "_01" + ".jpg"
        cv2.imwrite(dizhi1, edges, [cv2.IMWRITE_JPEG_QUALITY, 98])
        #形态学处理
        edges=morphology(edges)
        dizhi1 = bassOut_path + "\\" + iput_path_bass + "_morphology" + ".jpg"
        cv2.imwrite(dizhi1, edges, [cv2.IMWRITE_JPEG_QUALITY, 98])
        
        #统计概率霍夫线变换
        lines = cv2.HoughLinesP(edges,1, np.pi/2,Quantity,maxLineGap=Interval)  #返回值是三维数组
        #处理直线检测结果
        if lines is not None:
            lines_img=new_img(high,wide)
            for line in lines:
                x1, y1, x2, y2 = line[0]
                cv2.line(lines_img, (x1, y1), (x2, y2), 255, 1)
            dizhi1 = bassOut_path + "\\" + iput_path_bass + "_lines" + ".jpg"
            cv2.imwrite(dizhi1, lines_img, [cv2.IMWRITE_JPEG_QUALITY, 98])
            fenjieY=[]
            for lint in range(len(lines)):
                if lines[lint][0][1]==lines[lint][0][3] and lines[lint][0][1]>0 and lines[lint][0][1]<high-1:
                    fenjieY.append(lines[lint][0][1])
            fenjieY.sort()
            for lie in range(len(fenjieY)-1):
                if fenjieY[lie+1]-fenjieY[lie]<5:
                    xz=int((fenjieY[lie]+fenjieY[lie+1])/2)
                    fenjieY[lie]=xz
                    fenjieY[lie+1]=xz
            fenjieY.extend([0,high-1])
            fenjieY = list(set(fenjieY))
            fenjieY.sort()
            print(path_name,"图像文件处理完成共找到%d条分界线:开始裁剪" % int(len(fenjieY)-2))
            for lie in range(len(fenjieY)-1):
                if fenjieY[lie+1]-fenjieY[lie] > 500:
                    img1 = img[fenjieY[lie]:fenjieY[lie+1]+1, 0:wide]
                    dizhi=bassOut_path+"\\"+iput_path_bass+str(lie)+".jpg"
                    cv2.imwrite(dizhi, img1,[cv2.IMWRITE_JPEG_QUALITY,98])
            print(path_name,"图像裁剪完成")
        else:
            print(path_name,"图像不需要裁剪或者无法找到分界线")



猜你喜欢

转载自blog.csdn.net/sijianwuxian/article/details/129103322
今日推荐