Python-based preprocessing and image augmentation methods in deep learning

Prone to error:

cv2.error: OpenCV(4.7.0) D:\a\opencv-python\opencv-python\opencv\modules\imgcodecs\src\loadsave.cpp:692: error: (-2:Unspecified error) could not find a writer for the specified extension in function 'cv::imwrite_'

The reason for the error is usually that the saved path is incorrect:

cv2.imwrite('./crop_images/origin_imgs', roi)

should be changed to:

cv2.imwrite(os.path.join('./crop_images/origin_imgs', img_name), roi)

That is, the first parameter should write to the name of the file, not just write to the folder and stop.

Determine whether the image is a grayscale image

Grayscale images are somewhat similar to black and white images, but not identical.

A grayscale image means that the color of each pixel is represented by a grayscale value, usually represented by an 8-bit unsigned integer (0-255). Each pixel in a grayscale image may have a different grayscale value, so there may be many different grayscale levels in the image.

A black and white picture usually refers to a binary image, where there are only two pixel values, usually black and white. Each pixel in a black and white image has only two possible values, so there are only two gray levels in the image.

Although both grayscale images and black and white images only use grayscale values ​​to represent pixels in the image, they differ in the value range and number of pixels.

# 黑白照片(灰度图)识别
def isGrayMap(img, threshold = 15):
    """
    入参:
    img:PIL读入的图像
    threshold:判断阈值,图片3个通道间差的方差均值小于阈值则判断为灰度图。
    阈值设置的越小,容忍出现彩色面积越小;设置的越大,那么就可以容忍出现一定面积的彩色,例如微博截图。
    如果阈值设置的过小,某些灰度图片会被漏检,这是因为某些黑白照片存在偏色,例如发黄的黑白老照片、
    噪声干扰导致灰度图不同通道间值出现偏差(理论上真正的灰度图是RGB三个通道的值完全相等或者只有一个通道,
    然而实际上各通道间像素值略微有偏差看起来仍是灰度图)
    出参:
    bool值
    """
    if len(img.getbands()) == 1:
        return True
    img1 = np.asarray(img.getchannel(channel=0), dtype=np.int16)
    img2 = np.asarray(img.getchannel(channel=1), dtype=np.int16)
    img3 = np.asarray(img.getchannel(channel=2), dtype=np.int16)
    diff1 = (img1 - img2).var()
    diff2 = (img2 - img3).var()
    diff3 = (img3 - img1).var()
    diff_sum = (diff1 + diff2 + diff3) / 3.0
    if diff_sum <= threshold:
        return True
    else:
        return False

reference

Batch rename image files

Rename a batch of files

Question 1: When using the os.path.join() function to merge paths, only the latter is displayed.
Reason: use os.path.join(r'D:\datasets\new_name', “/1000” + str(i) + “.jpg”)

Multiple paths can be passed in to the os.path.join() function: The splicing will start from the first parameter starting with "/", and all previous parameters will be discarded.
The above one case comes first. In the case of the previous case, if there is a parameter starting with "./", it will start splicing from the previous parameter of the parameter starting with "./".

Note that os.rename(old_name, new_name) in the following code is a direct operation on the original file, which will cause the original file to disappear. If you want to keep the original file, use shutil.copy(old_name, new_name).

# 日期:  2023/4/22 11:47
import os

root_path = r'D:\datasets\1osr_original_img'

if os.path.exists(root_path):
    i = 1
    for filename in os.listdir(root_path):
        # 把图像改名
        old_name = os.path.join(root_path, filename)
        # 保证图片文件名称位数一致
        lens = 5 - len(str(i))
        new_name = os.path.join(root_path, "1" + "0"*lens + str(i) + ".jpg")  # 100001
        # 当文件不存在的时候创建,否则会报错:当文件已存在时,无法创建该文件。
        if not os.path.exists(new_name):
            os.rename(old_name, new_name)
        i += 1

        # 记录修改过程
        filelist = []
        if old_name != new_name:
            filelist.append('Old: ' + old_name)
            filelist.append('New: ' + new_name)

            f = open("rename_log.txt", "a")
            for file in filelist:
                f.write(file + '\n')

            f.close()
    print('OK')
else:
    print("路径不存在!")


NOTE: Processing a file with this code will delete the original image and only save the new image.
The operation is risky, so the modification process can be recorded.

reference

Shuffle the order of a batch of files and rename them

# 日期:  2023/4/22 11:47
import os
import random

root_path = './crop_images/origin_imgs1'
save_path = './crop_images/origin_imgs'

if os.path.exists(root_path):
    lst = []
    for filename in os.listdir(root_path):
        i = random.randint(1, 242)
        while i in lst:
            i = random.randint(1, 242)
        lst.append(i)
        # 把图像改名
        old_name = os.path.join(root_path, filename)
        # 保证图片文件名称位数一致
        lens = 5 - len(str(i))
        new_name = os.path.join(save_path, "1" + "0" * (lens - 1) + str(i) + ".jpg")  # 100001
        # 当文件不存在的时候创建,否则会报错:当文件已存在时,无法创建该文件。
        if os.path.exists(new_name):
            print('文件名已存在。')
        else:
            os.rename(old_name, new_name)
        # i += 1

        # 记录修改过程
        filelist = []
        if old_name != new_name:
            filelist.append('Old: ' + old_name)
            filelist.append('New: ' + new_name)

            f = open("rename_log1.txt", "a")
            for file in filelist:
                f.write(file + '\n')

            f.close()
    print('OK')
else:
    print("路径不存在!")


Image rotation/flip

# 日期:  2023/4/21 20:02
import cv2
import os

import numpy as np
from PIL import Image

root_path = r'D:\datasets\osr_original_img'

for filename in os.listdir(root_path):
    img_path = os.path.join(root_path, filename)
    angle = 270

    # 旋转方式
    # 1
    times = angle // 90
    img = cv2.imread(img_path)  # 生成的是numpy.ndarray,为ndarray多维数组类型,(H,W,C)
    img = np.rot90(img, k=times, axes=(0, 1))  # 对图像矩阵顺时针旋转90度,得到的像素与原来不同,变为H,W,与手机和电脑里对照片的旋转功能相同,如果想要顺时针,k设置为负数
    # 旋转后的图片的保存路径
    if angle == 90:
        new_name = filename.split('.')[0] + '_090.jpg'
    else:
        new_name = filename.split('.')[0] + '_{}.jpg'.format(angle)

    save_path = '../datasets/np rot{}/'.format(angle) + new_name
    # 使用cv2库保存图片,直接对矩阵保存就行
    cv2.imwrite(save_path, img)  # 保存旋转后的图像

    # # 2
    # img = Image.open(img_path)  # 生成的是PIL.JpegImagePlugin.JpegImageFile,(W,H)
    # img = img.transpose(Image.ROTATE_180)  # 引用固定的常量值,得到的像素与原来不同,变为H,W,与手机和电脑里对照片的旋转功能相同

    # 3
    # img = Image.open(img_path)  # 生成的是PIL.JpegImagePlugin.JpegImageFile,(W,H)
    # img = img.rotate(angle)  # 自定义旋转度数,得到的图片的像素与原来一样,都是W,H。逆时针旋转,如果需要顺时针加负号

    # # 旋转后的图片的保存路径
    # if angle == 90:
    #     new_name = filename.split('.')[0] + '_090.jpg'
    # else:
    #     new_name = filename.split('.')[0] + '_{}.jpg'.format(angle)
    #
    # save_path = '../datasets/rotate{}/'.format(angle) + new_name
    # # 使用Image库保存图片
    # img.save(save_path)

Regarding the rotation of the image, sometimes it is easy to have a misunderstanding. Rotate is rotation, and Transpose is the transformation position. This article uses flip to refer to it. The descriptions of these two functions are as follows:

img.rotate(90) is to rotate 90 degrees. It should be noted here that only the image content is rotated, and the image file itself (or it can be understood as the canvas) will not be rotated.
Image.Transpose.ROTATE_90 is to flip 90 degrees. By executing this statement, the entire canvas will flip.

When using the np.rot90 function in the NumPy library, the second parameter k represents the number of rotations. If k is a positive integer, the array will be rotated k times. If k is a negative integer, the array will be rotated counterclockwise abs(k) Second-rate.
And the third parameter axes indicates the axes to be swapped. By default, the first two axes are swapped. For example, for a two-dimensional array, the first and second dimensions are swapped. In this case, there is no need to specify the third parameter.
However, when the processed array is not a two-dimensional array, the axes parameter needs to be specified. In this case axes should be a length-2 tuple specifying the axes to swap. For example, in a three-dimensional array, the first and second dimensions can be swapped, while the third dimension remains unchanged, using axes=(1,0).
In the code, -1 is used as the value of the axes parameter. In this case -1 means the last axis. Therefore, the np.rot90 function will rotate along the last axis of the array. This is equivalent to swapping the first and second dimensions in a two-dimensional array.

Reference 1
Reference 2
Reference 3
Reference 4

Flip both the image and the corresponding xml tag file

The commented part is flipped vertically, the one below is flipped horizontally.

The flip_bbox_horizontal function is to flip the coordinates of the rectangular box in the XML annotation file horizontally, and save the modified annotation file to the specified path.
Specifically, the function first uses the ET.parse function to parse the incoming XML file and obtain the root node of the XML file. Then, it traverses all object nodes under the root node, and horizontally flips the coordinates of the rectangular box represented by the bndbox child node under each node. In the process of horizontal flipping, the function first obtains the left and right boundary coordinates xmin and xmax of the rectangular frame, then modifies xmin to the original image width minus xmax, and modifies xmax to the original image width minus xmin, so as to achieve the horizontal coordinates of the rectangular frame Flip.
Finally, the function uses the ET.ElementTree.write method to save the modified XML markup file to the specified path.
It should be noted that the parameter image_width of this function is a parameter used to represent the width of the image, and it is used to calculate the horizontal flip of the coordinates of the rectangular frame. Therefore, when calling this function, you need to pass the width of the corresponding image into this parameter.

import os
import xml.etree.ElementTree as ET
from PIL import Image


# def flip_image_vertical(image_path, save_path, output_dir):
#     image = Image.open(image_path)
#     flipped_image = image.transpose(Image.FLIP_TOP_BOTTOM)
#     save_dir = os.path.join(output_dir, os.path.relpath(os.path.dirname(image_path), os.path.dirname(input_image_dir)))
#     if not os.path.exists(save_dir):
#         os.makedirs(save_dir)
#     flipped_image.save(os.path.join(save_dir, os.path.basename(save_path)))
# 
# 
# def flip_bbox_vertical(xml_path, save_path, image_height, output_dir):
#     tree = ET.parse(xml_path)
#     root = tree.getroot()
#     for obj in root.iter('object'):
#         bbox = obj.find('bndbox')
#         ymin = int(bbox.find('ymin').text)
#         ymax = int(bbox.find('ymax').text)
#         bbox.find('ymin').text = str(image_height - ymax)
#         bbox.find('ymax').text = str(image_height - ymin)
#     save_dir = os.path.join(output_dir, os.path.relpath(os.path.dirname(xml_path), os.path.dirname(input_xml_dir)))
#     if not os.path.exists(save_dir):
#         os.makedirs(save_dir)
#     tree.write(os.path.join(save_dir, os.path.basename(save_path)))
# 
# 
# def main(input_image_dir, input_xml_dir, output_image_dir, output_xml_dir):
#     for root, dirs, files in os.walk(input_image_dir):
#         for file in files:
#             if file.endswith('.jpg'):
#                 # 竖直翻转图像
#                 image_path = os.path.join(root, file)
#                 flipped_image_path = os.path.join(os.path.relpath(root, input_image_dir),
#                                                   file.split('.')[0] + '_vertical_flipped.jpg')
#                 flip_image_vertical(image_path, flipped_image_path, output_image_dir)
# 
#                 # 修改XML文件中矩形框坐标
#                 xml_path = os.path.join(input_xml_dir, file.split('.')[0] + '.xml')
#                 flipped_xml_path = os.path.join(os.path.relpath(root, input_image_dir),
#                                                 file.split('.')[0] + '_vertical_flipped.xml')
#                 flip_bbox_vertical(xml_path, flipped_xml_path, Image.open(image_path).size[1], output_xml_dir)
# 
# 
# if __name__ == '__main__':
#     input_image_dir = 'img/image'
#     input_xml_dir = 'img/label'
#     output_image_dir = 'img_vertical_flip/image'
#     output_xml_dir = 'img_vertical_flip/label'
#     if not os.path.exists(output_image_dir):
#         os.makedirs(output_image_dir)
#     if not os.path.exists(output_xml_dir):
#         os.makedirs(output_xml_dir)
#     main(input_image_dir, input_xml_dir, output_image_dir, output_xml_dir)

def flip_image_horizontal(image_path, save_path):
    image = Image.open(image_path)
    flipped_image = image.transpose(Image.FLIP_LEFT_RIGHT)
    flipped_image.save(save_path)


def flip_bbox_horizontal(xml_path, save_path, image_width):
    tree = ET.parse(xml_path)
    root = tree.getroot()
    for obj in root.iter('object'):
        bbox = obj.find('bndbox')
        xmin = int(bbox.find('xmin').text)
        xmax = int(bbox.find('xmax').text)
        bbox.find('xmin').text = str(image_width - xmax)
        bbox.find('xmax').text = str(image_width - xmin)
    tree.write(save_path)


def main(image_dir, xml_dir, output_dir):
    for root, dirs, files in os.walk(image_dir):
        for file in files:
            if file.endswith('.jpg'):
                # 水平翻转图像
                image_path = os.path.join(image_dir, file)
                flipped_image_path = os.path.join(output_dir, file.split('.')[0] + '_flipped.jpg')
                flip_image_horizontal(image_path, flipped_image_path)

                # 修改XML文件中矩形框坐标
                xml_path = os.path.join(xml_dir, file.split('.')[0] + '.xml')
                flipped_xml_path = os.path.join(output_dir, file.split('.')[0] + '_flipped.xml')
                flip_bbox_horizontal(xml_path, flipped_xml_path, Image.open(image_path).size[0])


if __name__ == '__main__':
    image_dir = 'img/image'
    xml_dir = 'img/label'
    output_dir = 'img_flip/output_dir'
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)
    main(image_dir, xml_dir, output_dir)

Cut out the area where the target is located

Finally, I found that the three methods are easy to use!

method one

Cut out the area where the glass sheet is located.

# 日期:  2023/4/22 14:34
import cv2
import numpy as np

# 读取图片
img_path = r'D:\datasets\osr_original_img\100001.jpg'
img = cv2.imread(img_path)

# 灰度化处理
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 边缘检测
edges = cv2.Canny(gray, 100, 200)

# 轮廓检测
contours, hierarchy = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# 找到矩形轮廓
rect = None
for cnt in contours:
    approx = cv2.approxPolyDP(cnt, 0.01*cv2.arcLength(cnt, True), True)
    if len(approx) == 4:
        rect = approx
        break

# 裁剪矩形区域
if rect is not None:
    rect = rect.reshape(-1, 2)
    x, y, w, h = cv2.boundingRect(rect)
    roi = img[y:y+h, x:x+w]
    cv2.imwrite('D:/your_cropped_image_path.jpg', roi)

The steps in the code are as follows:

  1. Read photo and convert to grayscale image.
  2. Edge detection on grayscale images.
  3. Find contours close to rectangles by contour detection.
  4. Crop out the rectangular area using the bounding box of the rectangular outline.
  5. Save the cropped area as a new picture.
    When the above code processes some pictures, the output will be all black.

Method Two:

import cv2
import numpy as np

image = cv2.imread(r'D:\datasets\osr_original_img\100018.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

gradX = cv2.Sobel(gray, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1)
gradY = cv2.Sobel(gray, ddepth=cv2.CV_32F, dx=0, dy=1, ksize=-1)

# subtract the y-gradient from the x-gradient
gradient = cv2.subtract(gradX, gradY)
gradient = cv2.convertScaleAbs(gradient)

# blur and threshold the image
blurred = cv2.blur(gradient, (9, 9))
(_, thresh) = cv2.threshold(blurred, 90, 255, cv2.THRESH_BINARY)

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (25, 25))
closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)

# perform a series of erosions and dilations
closed = cv2.erode(closed, None, iterations=4)
closed = cv2.dilate(closed, None, iterations=4)

(cnts, _) = cv2.findContours(closed.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
c = sorted(cnts, key=cv2.contourArea, reverse=True)[0]

# compute the rotated bounding box of the largest contour
rect = cv2.minAreaRect(c)
box = np.int0(cv2.boxPoints(rect))

# draw a bounding box arounded the detected barcode and display the image
# cv2.drawContours(image, [box], -1, (255, 255, 255), 2)
cv2.imshow("Image", image)
cv2.imwrite("contoursImage2.jpg", image)
cv2.waitKey(0)

Xs = [i[0] for i in box]
Ys = [i[1] for i in box]
x1 = min(Xs)
x2 = max(Xs)
y1 = min(Ys)
y2 = max(Ys)
hight = y2 - y1
width = x2 - x1
cropImg = image[y1:y1+hight, x1:x1+width]

cv2.imwrite('D:/your_warped_image_path18.jpg', cropImg)

reference

method three

import matplotlib.pyplot as plt
import numpy as np
import cv2
import os
import math

# 图像灰度延展、直方图均衡化

file_root = "./osr_original_img"  # 当前文件夹下的所有图片
file_list = os.listdir(file_root)
save_out = "./crop_images/origin_imgs1"  # 保存图片的文件夹名称
for img_name in file_list:
    img_path = file_root + "/" + img_name

    original_image = cv2.imread(img_path, -1)
    image = original_image.copy()
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    blurred = cv2.GaussianBlur(gray, (3, 3), 0)
    canny = cv2.Canny(blurred, 120, 255, 1)
    kernel = np.ones((9, 9), np.uint8)
    result = cv2.dilate(canny, kernel)
    # Find contours in the image
    cnts = cv2.findContours(result.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]

    # Obtain area for each contour
    contour_sizes = [(cv2.arcLength(contour, True), contour) for contour in cnts]

    # Find maximum contour and crop for ROI section
    if len(contour_sizes) > 0:
        largest_contour = max(contour_sizes, key=lambda x: x[0])[1]
        # cv2.drawContours(image, largest_contour, -1, (0, 255, 0), 10)
        x, y, w, h = cv2.boundingRect(largest_contour)
        # cv2.rectangle(image, (x, y), (x + w, y + h), (255, 0, 0), 5)
        ROI = original_image[y-100:y + h + 100, x-100:x + w + 100]

    out_name = img_name.split('.')[0]
    save_path = save_out + "/" + out_name + '.jpg'
    cv2.imwrite(save_path, ROI)

crop and deskew

Cut out the area where the glass sheet is located. And rectify the cropped picture, because the industrial camera will produce distortion when taking pictures.

To rectify the captured image so that the rectangular glass sheet becomes a rectangle in the image, you can use the perspective transformation in the OpenCV library.

The problem with the following code is that the image after cropping and rectifying is rotated compared to the original image, as if it has been mirrored horizontally. I don’t know if this is the case for all images.

import cv2
import numpy as np

# 读取图片
img = cv2.imread(r'D:\datasets\osr_original_img\100002.jpg')

# 灰度化处理
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 边缘检测
edges = cv2.Canny(gray, 100, 200)

# 轮廓检测
contours, hierarchy = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# 找到矩形轮廓
rect = None
for cnt in contours:
    approx = cv2.approxPolyDP(cnt, 0.01*cv2.arcLength(cnt, True), True)
    if len(approx) == 4:
        rect = approx
        break

# 纠偏图像
if rect is not None:
    rect = rect.reshape(-1, 2)
    rect = rect.astype(np.float32)
    (tl, tr, br, bl) = rect
    w1 = np.sqrt((tl[0]-tr[0])**2 + (tl[1]-tr[1])**2)
    w2 = np.sqrt((bl[0]-br[0])**2 + (bl[1]-br[1])**2)
    h1 = np.sqrt((tl[0]-bl[0])**2 + (tl[1]-bl[1])**2)
    h2 = np.sqrt((tr[0]-br[0])**2 + (tr[1]-br[1])**2)
    maxWidth = max(int(w1), int(w2))
    maxHeight = max(int(h1), int(h2))
    dst = np.array([[0, 0], [maxWidth-1, 0], [maxWidth-1, maxHeight-1], [0, maxHeight-1]], dtype=np.float32)
    M = cv2.getPerspectiveTransform(rect, dst)
    warped = cv2.warpPerspective(img, M, (maxWidth, maxHeight))
    cv2.imwrite('D:/your_warped_image_path2.jpg', warped)

The steps in the code are as follows:

1. Read the photo and convert it to a grayscale image.
2. Perform edge detection on grayscale images.
3. Find the contour close to the rectangle by contour detection.
4. Use the bounding box of the rectangular outline to crop out the rectangular area.
5. Calculate the positions of the four vertices of the rectangular area in the transformed image.
6. Use perspective transformation (Perspective Transformation) to transform the rectangular area into a rectangle.
7. Save the transformed image as a new picture.

In the code, the contour close to the rectangle is first found, and the positions of the four vertices of the rectangular area in the transformed image are calculated through the bounding box of the contour. Then, use the cv2.getPerspectiveTransform() function to obtain the perspective transformation matrix M, and use the cv2.warpPerspective() function to perform perspective transformation on the image. The transformed image will be a rectangle, and the glass sheet will also be a rectangle.

A collection of image augmentation methods (for a single image)

Pay attention to modify the path for reading and saving pictures, and modify the parameters as needed.

from PIL import Image, ImageEnhance, ImageFilter, ImageDraw
import random

img_path = 'crop_images/0427origin_imgs/100272.jpg'
num = 100272

image = Image.open(img_path)  # 打开图片文件


# 旋转30度

rotated_image = image.rotate(30)  # 将图片旋转30度
rotated_image.save("crop_images/rotated_image_{}.jpg".format(num))  # 保存旋转后的图片文件

# 水平翻转

flipped_image = image.transpose(Image.FLIP_LEFT_RIGHT)  # 将图片水平翻转
flipped_image.save("crop_images/flipped_image_{}.jpg".format(num))  # 保存翻转后的图片文件

# 随机裁剪

width, height = image.size  # 获取图片的宽和高
crop_width, crop_height = 500, 500  # 设定裁剪后的图片大小
left = random.randint(0, width - crop_width)  # 随机生成左上角横坐标
upper = random.randint(0, height - crop_height)  # 随机生成左上角纵坐标
right = left + crop_width  # 计算右下角横坐标
lower = upper + crop_height  # 计算右下角纵坐标
cropped_image = image.crop((left, upper, right, lower))  # 裁剪图片
cropped_image.save("crop_images/cropped_image_{}.jpg".format(num))  # 保存裁剪后的图片文件

# 平移

offset = (50, 50)  # 定义平移量
trans_image = image.transform(image.size, Image.AFFINE, (1, 0, offset[0], 0, 1, offset[1]))  # 对图片进行平移
trans_image.save("trans_image.jpg")  # 保存平移后的图片文件

# 对比度增强

contrast_enhancer = ImageEnhance.Contrast(image)  # 创建对比度增强器
enhanced_image = contrast_enhancer.enhance(1.5)  # 对图片进行对比度增强
enhanced_image.save("contrast_enhanced_image.jpg")  # 保存对比度增强后的图片文件

# 亮度增强

brightness_enhancer = ImageEnhance.Brightness(image)  # 创建亮度增强器
enhanced_image = brightness_enhancer.enhance(1.5)  # 对图片进行亮度增强
enhanced_image.save("brightness_enhanced_image.jpg")  # 保存亮度增强后的图片文件

# 颜色转换


converted_image = image.copy()  # 创建图片副本
r, g, b = converted_image.split()  # 分离图片的RGB通道
r = r.point(lambda i: i + 50)  # 对R通道进行颜色转换
g = g.point(lambda i: i - 30)  # 对G通道进行颜色转换
b = b.point(lambda i: i - 20)  # 对B通道进行颜色转换
converted_image = Image.merge("RGB", (r, g, b))  # 合并三个通道重构图片
converted_image.save("converted_image.jpg")  # 保存颜色转换后的图片文件

# 转换为灰度图

converted_image = image.convert("L")  # 将图片转换为灰度图
converted_image.save("gray_image.jpg")  # 保存灰度图

# 模糊

blurred_image = image.filter(ImageFilter.GaussianBlur(radius=2))  # 对图片进行高斯模糊
blurred_image.save("blurred_image.jpg")  # 保存模糊后的图片文件

# 锐化

sharpened_image = image.filter(ImageFilter.SHARPEN)  # 对图片进行锐化
sharpened_image.save("sharpened_image.jpg")  # 保存锐化后的图片文件

# 添加噪声

draw = ImageDraw.Draw(image)  # 创建绘制对象
width, height = image.size  # 获取图片的宽和高
for x in range(width):
    for y in range(height):
        draw.point((x, y), fill=(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)))  # 为每个像素点添加随机噪声
image.save("noisy_image.jpg")  # 保存添加噪声后的图片文件


Guess you like

Origin blog.csdn.net/ThreeS_tones/article/details/130303519