Программа для автоматического изменения размера изображения с сохранением пропорции сторон на 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/


Интересные статьи

Интересные статьи

Google Sheet и Sheets API - классный (простой, бесплатный и универсальный) способ организовать хранение и анализ данных получаемых от своего сервиса. При условии что этих данных не то чтобы очень мног...
Число Пи — пожалуй, самое знаменитое число в мире. Это единственное число, ради которого учредили особый день в году — День числа пи, отмечаемый 14 марта. В этой статье м...
Статья отражает личный опыт разработки расширения ядра для Linux.В ней рассмотрен способ выполнения привилегированных системных вызовов процессом пространства ядра по запросам управляющей...
Привет, Хабр! Сегодня мы затрагиваем актуальнейшую тему — Python для работы с GPU. Автор рассматривает пример, тривиальный в своей монструозности, и демонстрирует решение, сопровож...
Недавно я нашел странную особенность в Google Drive. Определенная последовательность действий в Google Drive веб интерфейсе приводит к тому, что каждый может загрузить файлы любого объема в свою ...