Métodos de aumento de imágenes y preprocesamiento basados en Python en aprendizaje profundo

Propenso a errores:

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_'

El motivo del error suele ser que la ruta guardada es incorrecta:

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

debe cambiarse a:

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

Es decir, el primer parámetro debe escribir en el nombre del archivo, no solo escribir en la carpeta y detenerse.

Determinar si la imagen es una imagen en escala de grises

Las imágenes en escala de grises son algo similares a las imágenes en blanco y negro, pero no idénticas.

Una imagen en escala de grises significa que el color de cada píxel está representado por un valor de escala de grises, generalmente representado por un número entero de 8 bits sin signo (0-255). Cada píxel de una imagen en escala de grises puede tener un valor de escala de grises diferente, por lo que puede haber muchos niveles de escala de grises diferentes en la imagen.

Una imagen en blanco y negro generalmente se refiere a una imagen binaria, donde solo hay dos valores de píxeles, generalmente en blanco y negro. Cada píxel en una imagen en blanco y negro tiene solo dos valores posibles, por lo que solo hay dos niveles de gris en la imagen.

Aunque tanto las imágenes en escala de grises como las imágenes en blanco y negro solo usan valores de escala de grises para representar píxeles en la imagen, difieren en el rango de valores y la cantidad de píxeles.

# 黑白照片(灰度图)识别
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

referencia

Renombrar archivos de imagen por lotes

Cambiar el nombre de un lote de archivos

Pregunta 1: cuando se usa la función os.path.join() para fusionar rutas, solo se muestra la última.
Motivo: utilice os.path.join(r'D:\datasets\new_name', “/1000” + str(i) + “.jpg”)

Se pueden pasar múltiples rutas a la función os.path.join(): el empalme comenzará desde el primer parámetro que comienza con "/", y todos los parámetros anteriores se descartarán.
El caso anterior viene primero. En el caso del caso anterior, si hay un parámetro que empieza por "./", se empezará a empalmar desde el parámetro anterior del parámetro que empieza por "./".

Tenga en cuenta que os.rename(old_name, new_name) en el siguiente código es una operación directa en el archivo original, lo que hará que el archivo original desaparezca. Si desea conservar el archivo original, 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("路径不存在!")


NOTA: Procesar un archivo con este código eliminará la imagen original y solo guardará la nueva imagen.
La operación es riesgosa, por lo que se puede registrar el proceso de modificación.

referencia

Mezclar el orden de un lote de archivos y cambiarles el nombre

# 日期:  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("路径不存在!")


Rotación/giro de imagen

# 日期:  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)

Con respecto a la rotación de la imagen, a veces es fácil tener un malentendido. Rotar es rotación y Transponer es la posición de transformación. Este artículo usa voltear para referirse a ella. Las descripciones de estas dos funciones son las siguientes:

img.rotate(90) es para rotar grados 90. Cabe señalar aquí que solo se rota el contenido de la imagen, y el archivo de imagen en sí (o puede entenderse como el lienzo) no se rotará.
Image.Transpose.ROTATE_90 es girar 90 grados. Al ejecutar esta declaración, todo el lienzo se volteará.

Al usar la función np.rot90 en la biblioteca NumPy, el segundo parámetro k representa el número de rotaciones. Si k es un número entero positivo, la matriz se rotará k veces. Si k es un número entero negativo, la matriz se rotará en sentido antihorario abs(k) Segunda clase.
Y el tercer parámetro, ejes, indica los ejes que se intercambiarán. De forma predeterminada, los dos primeros ejes se intercambian. Por ejemplo, para una matriz bidimensional, la primera y la segunda dimensión se intercambian. En este caso, no es necesario especificar el tercer parámetro.
Sin embargo, cuando la matriz procesada no es una matriz bidimensional, es necesario especificar el parámetro de ejes. En este caso, los ejes deben ser una tupla de longitud 2 que especifique los ejes para intercambiar. Por ejemplo, en una matriz tridimensional, la primera y la segunda dimensión se pueden intercambiar, mientras que la tercera dimensión permanece sin cambios, usando axes=(1,0).
En el código, -1 se usa como el valor del parámetro de ejes. En este caso -1 significa el último eje. Por lo tanto, la función np.rot90 rotará a lo largo del último eje de la matriz. Esto es equivalente a intercambiar la primera y la segunda dimensión en una matriz bidimensional.

Referencia 1
Referencia 2
Referencia 3
Referencia 4

Voltear tanto la imagen como el archivo de etiqueta xml correspondiente

La parte comentada se voltea verticalmente, la de abajo se voltea horizontalmente.

La función flip_bbox_horizontal es voltear horizontalmente las coordenadas del cuadro rectangular en el archivo de anotación XML y guardar el archivo de anotación modificado en la ruta especificada.
Específicamente, la función primero usa la función ET.parse para analizar el archivo XML entrante y obtener el nodo raíz del archivo XML. Luego, atraviesa todos los nodos de objeto debajo del nodo raíz y voltea horizontalmente las coordenadas del cuadro rectangular representado por el nodo secundario bndbox debajo de cada nodo. En el proceso de volteo horizontal, la función primero obtiene las coordenadas de los límites izquierdo y derecho xmin y xmax del marco rectangular, luego modifica xmin al ancho de la imagen original menos xmax, y modifica xmax al ancho de la imagen original menos xmin, para conseguir las coordenadas horizontales del marco rectangular Flip.
Finalmente, la función usa el método ET.ElementTree.write para guardar el archivo de marcado XML modificado en la ruta especificada.
Cabe señalar que el parámetro image_width de esta función es un parámetro que se utiliza para representar el ancho de la imagen y se utiliza para calcular el giro horizontal de las coordenadas del marco rectangular. Por lo tanto, al llamar a esta función, debe pasar el ancho de la imagen correspondiente a este parámetro.

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)

Recortar el área donde se encuentra el objetivo

¡Finalmente, descubrí que los tres métodos son fáciles de usar!

método uno

Recorta el área donde se encuentra la lámina de vidrio.

# 日期:  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)

Los pasos en el código son los siguientes:

  1. Lea la foto y conviértala a una imagen en escala de grises.
  2. Detección de bordes en imágenes en escala de grises.
  3. Encuentre contornos cerca de rectángulos mediante la detección de contornos.
  4. Recorte el área rectangular utilizando el cuadro delimitador del contorno rectangular.
  5. Guarde el área recortada como una nueva imagen.
    Cuando el código anterior procesa algunas imágenes, la salida será completamente negra.

Método dos:

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)

referencia

metodo tres

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)

recortar y enderezar

Recorta el área donde se encuentra la lámina de vidrio. Y rectifica la foto recortada, porque la cámara industrial producirá distorsión al hacer las fotos.

Para rectificar la imagen capturada de modo que la hoja de vidrio rectangular se convierta en un rectángulo en la imagen, puede usar la transformación de perspectiva en la biblioteca de OpenCV.

El problema con el siguiente código es que la imagen después de recortar y rectificar se rota con respecto a la imagen original, como si se hubiera reflejado horizontalmente, no sé si esto es así para todas las imágenes.

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)

Los pasos en el código son los siguientes:

1. Lea la foto y conviértala en una imagen en escala de grises.
2. Realice la detección de bordes en imágenes en escala de grises.
3. Encuentre el contorno cerca del rectángulo por detección de contorno.
4. Utilice el cuadro delimitador del contorno rectangular para recortar el área rectangular.
5. Calcula las posiciones de los cuatro vértices del área rectangular en la imagen transformada.
6. Use la transformación de perspectiva (Transformación de perspectiva) para transformar el área rectangular en un rectángulo.
7. Guarde la imagen transformada como una imagen nueva.

En el código, primero se encuentra el contorno cercano al rectángulo y las posiciones de los cuatro vértices del área rectangular en la imagen transformada se calculan a través del cuadro delimitador del contorno. Luego, use la función cv2.getPerspectiveTransform() para obtener la matriz de transformación de perspectiva M y use la función cv2.warpPerspective() para realizar la transformación de perspectiva en la imagen. La imagen transformada será un rectángulo y la hoja de vidrio también será un rectángulo.

Una colección de métodos de aumento de imágenes (para una sola imagen)

Preste atención a modificar la ruta para leer y guardar imágenes, y modifique los parámetros según sea necesario.

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")  # 保存添加噪声后的图片文件


Supongo que te gusta

Origin blog.csdn.net/ThreeS_tones/article/details/130303519
Recomendado
Clasificación