Программа для автоматического изменения размера изображения с сохранением пропорции сторон на Python

от автора

Введение

Иногда необходимо изменить размер изображения с сохранением пропорции сторон. Особенно, когда это очень большое количество файлов. Это приложение позволяет изменить размер изображения, сохранить его в нужную папку, а также инвертировать цвет (подходит для редактирования осциллограмм) и переименовать файл. Возможно, для существуют уже какие-то программы, но была необходимость сделать это самому. Поделюсь кодом с вами, возможно кому-то потребуется (Код программы в конце статьи).

Функции программы

Перди мной стояла задача, сделать простую программу позволяющую изменять размер изображения с сохранением пропорции сторон. Долго недумая решил написать небольшой скрипт, который удовлетворял бы такие потребности:

  1. Возможность работы с разными типами файлов изображений (png, bmp, jpg и т.д.)

  2. Удобно добавлять файлы в программу

  3. Возможность добавлять сразу несколько изображений

  4. Возможность переименовывать отредактированный файл

  5. Возможность инвертировать цвета

  6. Удобный выбор пути для сохранения файлов

  7. Простой интерфейс программы

Внешний вид

Сначала рассмотрим, как выглядит программа и что выполняет, а затем перейдем к самой реализации.

Интерфейс программы выглядит следующим образом:

Рис. 1. Внешний вид
Рис. 1. Внешний вид

Как видим, программа не перегружена лишними деталями. Разберем каждый элемент подробнее.

  • Image files — выбор изображений, которые нужно будет редактировать. Откроется стандартный файловый проводник.

  • Save in folder — выбор папки куда будет сохранен редактируемый файл если пользователь не выберет папку, они будут сохранены в папке корня программы под названием Edited photos (папка создается автоматически). Откроется стандартный файловый проводник.

  • Image height (cm) — здесь нужно внести высоту изображения, которая необходима (ширина будет отредактирована автоматически с сохранением пропорций)

  • Inver color — при выборе этого чекбокса, цвета изображения будут инвертированы в RGBA (Это очень удобно если работаете с изображением осциллограмм)

  • Rename the file — при выборе чекбокса разблокирует поле ввода «Add ending to file name» и дописывает в название файла соответствующий текст.
    Пример: название изображения «x». В поле Add ending to file name указываем «edit». Файл или файлы сохраняются с таким названием: «x_edit».
    Если чекбокс не выбран, то файл сохранится с таким же названием («x»)

  • Convert — редактирование файла и сохранение с соответствующими параметрами указанными выше.

Рис.2 Результат выполнения программы
Рис.2 Результат выполнения программы

Реализация программы

Код был написан на Python с использованием следующих библиотек:

from tkinter import * from tkinter import messagebox from tkinter import filedialog import PIL.ImageOps from PIL import Image import errno import os

Полный код программы на Python:

from tkinter import * from tkinter import messagebox from tkinter import filedialog import PIL.ImageOps from PIL import Image import errno import os  ##--------------------------------------------------------------------------------------------------------------------## ##Creating a folder at the root of the program def make_sure_path_exists(path):     try: os.makedirs(path)     except OSError as exception:         if exception.errno != errno.EEXIST:             raise  ##Фуfunction that translates from cm to pixel def cm_in_px(cm):     global px     px = int(cm) * 38     return px  ##Creating a folder at the root of the program-------------------------------------------------------------------------# make_sure_path_exists('Edited photos') #----------------------------------------------------------------------------------------------------------------------#  ##ФScale and store aspect ratio def scale_image(input_image_path,                 output_image_path,                 width=None,                 height=None                 ):     original_image = Image.open(input_image_path)     w, h = original_image.size     print('The original image size is {wide} wide x {height} '           'high'.format(wide=w, height=h))      if width and height:         max_size = (width, height)     elif width:         max_size = (width, h)     elif height:         max_size = (w, height)     else:         # No width or height specified         raise RuntimeError('Width or height required!')      original_image.thumbnail(max_size, Image.ANTIALIAS)     original_image.save(output_image_path)      scaled_image = Image.open(output_image_path)     width, height = scaled_image.size     print('The scaled image size is {wide} wide x {height} '           'high'.format(wide=width, height=height)) #----------------------------------------------------------------------------------------------------------------------#  ##Signature checkbox 2 def chek_cb2():     global message_entry     if ismarried2.get() == 0:         message_entry = Entry(window, state=DISABLED, bd=2, width = 48)         message_entry.place(x=10, y=275)         return Label(window, text='Add ending to file name:', font=('Arial', 10)).place(x=10, y=250)     else:         message_entry = Entry(window, state=NORMAL, bd=2, width = 48)         message_entry.place(x=10, y=275)         return Label(window, text='Add ending to file name:', font=('Arial', 10)).place(x=10, y=250) #----------------------------------------------------------------------------------------------------------------------#  ##file renaming def rename():     global last_name     if ismarried2.get() == 1:         last_name = '_' + message_entry.get()     else:         last_name = ''     return last_name #----------------------------------------------------------------------------------------------------------------------#  #----------------------------------------------------------------------------------------------------------------------# ##Path to upload images number_f = 0 def clicked_dialogOpen():     global choosefile     global number_f     choosefile = filedialog.askopenfilename(multiple=True, parent = window, filetypes=(("Image files", "*.png"), ("all files", "*.*")))     number_f = len(choosefile)     label_file() #----------------------------------------------------------------------------------------------------------------------# ##Check for characters in the string def check_name():     global d     d = 0     for i in message_entry.get():         if i.isalpha():             d += 1         elif i.isdigit():             d+= 1         else:             d+= 1     return d #----------------------------------------------------------------------------------------------------------------------# ##Display information about the number of selected images def label_file():      if number_f == 0:         lbl2 = Label(window, text="Image not selected", font=('Arial', 9), fg = 'red')         lbl2.place(x=start_pos_x, y=start_pos_y + step_des * 0.9)     elif number_f == 1:         lbl2 = Label(window, text='File selected             '.format(number_f), font=('Arial', 9), fg = 'green')         lbl2.place(x=start_pos_x, y=start_pos_y + step_des * 0.9)     else:         lbl2 = Label(window, text='Files selected - {}       '.format(number_f), font=('Arial', 9), fg = 'green')         lbl2.place(x=start_pos_x, y=start_pos_y + step_des * 0.9) #----------------------------------------------------------------------------------------------------------------------# ##Open the path to save the file filename = 0 def browse_button():     global filename     filename = filedialog.askdirectory()     label_folder() #----------------------------------------------------------------------------------------------------------------------# ##Display save directory information def label_folder():      if filename == 0:         lbl = Label(window, text="Path not selected", font=('Arial', 9), fg = 'red')         lbl.place(x=start_pos_x, y=start_pos_y + step_des * 2.3)     elif filename == '':         lbl = Label(window, text=os.getcwd() + '/Edited photos/', font=('Arial', 9), fg = 'green')         lbl.place(x=start_pos_x, y=start_pos_y + step_des * 2.3)     else:         lbl = Label(window, text=filename, font=('Arial', 9), fg = 'green')         lbl.place(x=start_pos_x, y=start_pos_y + step_des * 2.3) #----------------------------------------------------------------------------------------------------------------------# ##Zoom and record an image def scale():     check_name()     if number_f < 1:         messagebox.showerror("Error", "No file selected")     if ismarried2.get() == 1 and d < 1:         messagebox.showerror("Error", "Parameter not entered: Add ending to file name")     else:         for i in range(number_f):             try:                 with open(choosefile[i]) as im:                     r = os.path.splitext(choosefile[i])                     var = (os.path.basename(r[0]), r[1])                      if filename == 0:                         folder = os.getcwd() + '/Edited photos/'                         output_name = folder + var[0] + rename() + var[1]                         scale_image(input_image_path=choosefile[i],                             output_image_path=output_name,                             height=cm_in_px(message_cm.get()))                          if ismarried.get() == 1:                             image = Image.open(output_name)                             if image.mode == 'RGBA':                                 r, g, b, a = image.split()                                 rgb_image = Image.merge('RGB', (r, g, b))                                 inverted_image = PIL.ImageOps.invert(rgb_image)                                 r2, g2, b2 = inverted_image.split()                                 final_transparent_image = Image.merge('RGBA', (r2, g2, b2, a))                                 final_transparent_image.save(output_name)                             else:                                 inverted_image = PIL.ImageOps.invert(image)                                 inverted_image.save(output_name)                      else:                         folder = filename                         output_name = filename + '/' + var[0] + rename() + var[1]                         scale_image(input_image_path=choosefile[i],                             output_image_path = output_name,                             height=cm_in_px(message_cm.get()))                          if ismarried.get() == 1:                             image = Image.open(output_name)                             if image.mode == 'RGBA':                                 r, g, b, a = image.split()                                 rgb_image = Image.merge('RGB', (r, g, b))                                 inverted_image = PIL.ImageOps.invert(rgb_image)                                 r2, g2, b2 = inverted_image.split()                                 final_transparent_image = Image.merge('RGBA', (r2, g2, b2, a))                                 final_transparent_image.save(output_name)                             else:                                 inverted_image = PIL.ImageOps.invert(image)                                 inverted_image.save(output_name)                     print("Çhose",choosefile[0])             except:                 print('Error')                 messagebox.showerror("Error", "An error has occurred.\n\nThe program is intended for image processing only.\n\nContact the e-mail address:\nolehlastovetskyi99@gmail.com")                 quit()         messagebox.showinfo("Message", "Completed!\nChanged {} files.\nFiles saved in the directory:\n{}".format(number_f, folder)) #----------------------------------------------------------------------------------------------------------------------#  ##--------------------------------------------------------------------------------------------------------------------##  window = Tk() ismarried = IntVar(value= 2) ismarried.set(0) ismarried2 = IntVar(value= 2) ismarried2.set(0) chek_cb2() rename() ##------------------------------------------------------------------------------------------------------------------------##  start_pos_x = 10 start_pos_y = 10   height_button = 2 width_button = 32  font_button = ("Arial Bold", 11) font_checkbox = ("Arial", 11) font_combobox = ('Arial', 11) font_label = ("Arial Bold", 11) step_des = 60  label_folder() label_file()  window.title("Scale")  btn_dialogOpen = Button(window, text="Image files", command=clicked_dialogOpen, height=height_button,                         width=width_button, font=font_button) btn_dialogOpen.place(x=start_pos_x, y=start_pos_y)  btn_browsebutton = Button(window, text="Save in folder", command=browse_button, height=height_button,                           width=width_button, font=font_button)  btn_browsebutton.place(x=start_pos_x, y=start_pos_y + step_des + 25)  lbl = Label(window, text="Image height (cm):", font=font_label) lbl.place(x=start_pos_x + 1, y=start_pos_y + step_des * 2.95)  message_cm = Entry(width=7) message_cm.place(x=start_pos_x + 150, y=start_pos_y + step_des * 2.97) message_cm.insert(0, "9")  ismarried_checkbutton = Checkbutton(text="Invert color", variable=ismarried, font =font_checkbox) ismarried_checkbutton.place(x=start_pos_x + 1, y=start_pos_y + step_des * 3.4)  ismarried_checkbutton2 = Checkbutton(text="Rename the file", variable=ismarried2,                                      font = font_checkbox, command = chek_cb2) ismarried_checkbutton2.place(x=start_pos_x + 170, y=start_pos_y + step_des * 3.4)  btn_scale = Button(window, text="Convert", command=scale, height=height_button, width=width_button,                    font=font_button, state = NORMAL)  btn_scale.place(x=start_pos_x, y=start_pos_y + step_des * 5.2)  Label(window, text='Github:', font=('Arial', 8)).place(x=10, y=385)  x = (window.winfo_screenwidth() - window.winfo_reqwidth()) / 2 y = (window.winfo_screenheight() - window.winfo_reqheight()) / 2 window.wm_geometry("+%d+%d" % (x, y))  window.maxsize(320,410) window.minsize(320,410) window.resizable(0, 0)  window.mainloop() 

Ссылки на github:https://github.com/Oleh-Last/Python.git


ссылка на оригинал статьи https://habr.com/ru/post/648665/