python opencv: Batch identification of dividing lines of spliced pictures and automatic cropping

A project summary

When looking for picture materials on the Internet, many pictures are long pictures, and many pictures are spliced ​​on one picture. And many times we need a single picture. At this time, we need to crop the long picture. Generally, we can use picture tools. For simple cropping, you can use ps for advanced slicing processing. If the number of pictures is small, it is good to say that once there are a large number of pictures that need to be cropped, it will be very cumbersome and time-consuming. At this time, you will want to use automatic cropping tools for cropping. However, most tools on the Internet are for fixed-size cropping and require manual input of cropping positions. This method still cannot automatically identify the dividing lines between pictures and crop them. At this time, I remembered that I had learned a little bit about machine vision, so I tried to use python and opencv to implement the entire operation process to identify the image dividing lines and automatically crop and save them.
Network diagram example

Second project process

The general process to implement this process is to first scan all files under a given file, perform suffix processing on the scanned file names, compare the file suffix with the image type, and if it is an image file, first convert the image into a grayscale image. Then denoise, and then perform edge contour extraction. After obtaining the image edge contour, image morphology processing can be performed to make the dividing line more obvious, and then Hough linear transformation is used for straight line detection. The results of the straight line detection are processed to leave only horizontal straight lines. Extract the vertical coordinate of the straight line and sort it in ascending order. After sorting, crop and save. This cycle continues until the image files in the folder are processed. The difficulty in implementing the above process lies in extracting the outline of the image.

Three image edge extraction

Since we need to extract the edge contour of the image, we thought that we could use the Sobel operator for processing. The image gradient calculates the speed of image change. For the edge part of the image, the gray value changes greatly and the gradient value is also large. On the contrary, for the smoother parts of the image, the gray value changes little, correspondingly The gradient value is also smaller. Generally, the image gradient calculates the edge information of the image. The Sobel operator is a discrete differential operator that combines Gaussian smoothing and differential derivation operations. This operator uses local differences to find edges, and the calculated value is an approximation of the gradient. What we want to get is the horizontal dividing line, and we can calculate the partial derivatives in the horizontal and vertical directions to get the edge contour.
Apply sobel operator to calculate image gradient in opencv

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])

The result after image processing.
Insert image description here
In addition to applying the sobel operator, you can also use Canny edge detection.

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])

Insert image description here
From the above results, it can be seen that whether using sobel or cany, the processed image can clearly find the dividing line. However, if you look closely, you will find that in addition to the dividing line, there are other elements in the image, and the dividing line is not very continuous and jagged. shape, you can consider using morphology for processing. First expand the image and then corrode it.

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])

Insert image description here
After the expansion corrosion treatment, the dividing line is more obvious and the continuity of the straight line is better.

Four-line detection

The Hough transform is a method of finding straight lines, circles, and other simple shapes in an image. The Hough transform uses
a method similar to voting to obtain the shape set within the current image. The transform was first proposed by Paul Hough in 1962.
The initial Hough transform can only be used to detect straight lines. After development, the Hough transform can not only identify straight lines, but also other
simple graphic structures, such as circles and ellipses.
This time, we use the function cv2.HoughLinesP() provided by opencv for straight line detection.

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("没有找到图片裁剪线")

Insert image description here
The above is the found dividing line of the image.
Finally, a single image can be obtained by cropping.

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("没有找到图片裁剪线")

The following are the split results.
The first picture
Insert image description here
and the second picture.
Insert image description here

Five project codes

The following program can be run automatically. You only need to provide the original image file path and the split output path. If you find that the effect is not ideal, you can change the parameters and adjust it yourself.

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,"图像不需要裁剪或者无法找到分界线")



Guess you like

Origin blog.csdn.net/sijianwuxian/article/details/129103322