Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 8 additions & 90 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,94 +1,12 @@
# Рефакторинг
Стало
![img.png](img.png)

В последнее время очень популярен [пиксельарт](https://en.wikipedia.org/wiki/Pixel_art), а также разнообразные наборы для ручного крафта.
Было
![img_1.png](img_1.png)

Один из проектов, [mozabrick](https://mozabrick.ru/products/model-l/), например, предлагает что-то типа мозайки из квадратиков 5 градаций серого, из которой можно собрать любую фотографию.
Ускорение почти в 10 раз за счет
эффективной реализации матричных операций в библиотеке numpy

![Преобразование](https://github.com/bibilov/refactoring/blob/main/img-test.png)
Выводим на экран информацию о высоте, ширине и типе картинки

С помощью приложения вы накладываете фильтр, получаете черно-белый пиксель-арт, который можно набрать уже мозаикой.

Иногда делают такие панно прямо на зданиях.

![Здание](https://github.com/bibilov/refactoring/blob/main/632.jpg)


Вот я прогнал через фильтр свое фото:

![Фото с фильтром](https://github.com/bibilov/refactoring/blob/main/m0oLR8Tx0zRG8s3SZQlQLnF8bhcnGu6AwzRA5aqi.png_4_1.png)

Вот так примерно выглядит процесс сборки панно:

![Сборка](https://github.com/bibilov/refactoring/blob/main/compile.png)

Естественно, появилось желание написать вручную такой фильтр. Он может понадобится для пиксель-арта, создания игр с анимацией а-ля ранний Mortal Kombat, японских кроссвордов или для вязки свитеров ближе к НГ. Для чтения-записи изображений используется библиотека `pillow`, для всех остальных манипуляций — `numpy`.


```python
from PIL import Image
import numpy as np
img = Image.open("img2.jpg")
arr = np.array(img)
a = len(arr)
a1 = len(arr[1])
i = 0
while i < a - 11:
j = 0
while j < a1 - 11:
s = 0
for n in range(i, i + 10):
for n1 in range(j, j + 10):
n1 = arr[n][n1][0]
n2 = arr[n][n1][1]
n3 = arr[n][n1][2]
M = n1 + n2 + n3
s += M
s = int(s // 100)
for n in range(i, i + 10):
for n1 in range(j, j + 10):
arr[n][n1][0] = int(s // 50) * 50
arr[n][n1][1] = int(s // 50) * 50
arr[n][n1][2] = int(s // 50) * 50
j = j + 10
i = i + 10
res = Image.fromarray(arr)
res.save('res.jpg')
```

Картинка прелставляет сосбой трехмерный массив, где два измерения &mdash; таблица с пикселями, а пиксель &mdash; что-то то типа массива `[12, 240, 123]`, содержащего компоненты RGB.

Я ввел размер элемента мозайки 10x10 пикселей. Среди 100 пикселей из большой ячейки я просто выясняю среднюю яркость и закрашиваю их все в один цвет средней яркости, приведенный к ступеньке с шагом 50.

В результате из такой картинки:

![Исходная каритинка](https://github.com/bibilov/refactoring/blob/main/img2.jpg)

Получается такая:

![Результат](https://github.com/bibilov/refactoring/blob/main/res.jpg)

Это не тот результат, на который я рассчитывал, и вам предстоит много поработать с моим кодом.

Представьте этапы как отдельные коммиты.

## Что делать?

### 1 этап
К коду настолько много вопросов, что я даже не знаю с чего начать...

* В коде содержатся как минимум четыре ошибки, которые заставляют фильтр работать не так, как нужно.
* Одна из них очень нетипична для программиста на Питоне и связана с переполнением беззнакового целого `numpy.uint8`.
* В одном месте я запутался с именами переменных.
* Неверно считаю компоненты серого цвета (забываю поделить на 3).
* Неверно работаю с граничными условиями, в результате чего справа и внизу остались необработанные полосы по 10 пикселей.

### 2 этап
* PEP8.
* Именование переменных.
* Возможность управлять размерами мозайки (сейчас &mdash; только 10x10).
* Возможность управлять градациями серого (сейчас &mdash; с шагом 50). Лучше сделать просто в виде задания количества шагов. Например: 4 градации, 6 градаций.
* Выделение функций.
### 3 этап
* По возможности убрать ручные циклы, заменив их матричными преобразованиями.
### 4 этап
* Возможно, переписать в консольную утилиту, которой на вход подаются имена исходного изображения и результата. Сейчас чтобы заставить код работать с другой картинкой, его надо исправлять.
![screen_1.jpg](screen_1.jpg)
104 changes: 78 additions & 26 deletions filter.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,80 @@
from argparse import ArgumentParser

from PIL import Image
import numpy as np
img = Image.open("img2.jpg")
arr = np.array(img)
a = len(arr)
a1 = len(arr[1])
i = 0
while i < a - 11:
j = 0
while j < a1 - 11:
s = 0
for n in range(i, i + 10):
for n1 in range(j, j + 10):
n1 = arr[n][n1][0]
n2 = arr[n][n1][1]
n3 = arr[n][n1][2]
M = n1 + n2 + n3
s += M
s = int(s // 100)
for n in range(i, i + 10):
for n1 in range(j, j + 10):
arr[n][n1][0] = int(s // 50) * 50
arr[n][n1][1] = int(s // 50) * 50
arr[n][n1][2] = int(s // 50) * 50
j = j + 10
i = i + 10
res = Image.fromarray(arr)
res.save('res.jpg')


def pixelate(image_array, gradation_number=5, pixel_size=10):
"""
convert input image array to pixelated image array
:param image_array: array to transform
:param gradation_number: number of grey colour
:param pixel_size: pixel size
:return: None
"""
height = len(image_array)
width = len(image_array[0])
grey_step = 256 // gradation_number
for i in range(0, height, pixel_size):
for j in range(0, width, pixel_size):
summa = np.sum(image_array[i:i + pixel_size, j:j + pixel_size])
summa = int(summa // 3 // pixel_size // pixel_size)

image_array[i:i + pixel_size, j:j + pixel_size] = summa // grey_step * grey_step


def callback_parser(args):
"""parser callback"""
process_parser(args.input, args.output, int(args.gradation), int(args.size))


def process_parser(input_filepath, output_filepath, gradation, size):
"""parser process"""
img = Image.open(input_filepath)
arr = np.array(img)
pixelate(arr, gradation_number=gradation, pixel_size=size)
res = Image.fromarray(arr)
res.save(output_filepath)


def setup_parser(parser):
"""parser setup"""

parser.add_argument(
"-i", "--input", dest="input",
help="path to input image",
required=True,
)

parser.add_argument(
"-o", "--output", dest="output",
help="path to output image",
required=True,
)

parser.add_argument(
"-s", "--size", dest="size",
help="pixel size",
default=10,
required=False,
)

parser.add_argument(
"-g", "--gradation", dest="gradation",
help="gradation number",
default=8,
required=False,
)
parser.set_defaults(callback=callback_parser)


def main():
""" main program """
parser = ArgumentParser(description="Pixelate CLI")
setup_parser(parser)
args = parser.parse_args()
args.callback(args)


if __name__ == "__main__":
main()
30 changes: 30 additions & 0 deletions filter_1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from PIL import Image
import numpy as np
img = Image.open("img2.jpg")
arr = np.array(img)
a = len(arr)
a1 = len(arr[1])
i = 0
while i < a:
j = 0
while j < a1:
s = 0
for n in range(i, min(i + 10, a - 1)):
for d1 in range(j, j + 10):
n1 = arr[n][d1][0]
n2 = arr[n][d1][1]
n3 = arr[n][d1][2]
M = int(n1)
M = (M + n2 + n3) // 3
s += M
s = int(s // 100)
# print(s)
for n in range(i, min(i + 10, a - 1)):
for d1 in range(j, j + 10):
arr[n][d1][0] = int(s // 50) * 50
arr[n][d1][1] = int(s // 50) * 50
arr[n][d1][2] = int(s // 50) * 50
j = j + 10
i = i + 10
res = Image.fromarray(arr)
res.save('res.jpg')
33 changes: 33 additions & 0 deletions filter_2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from PIL import Image
import numpy as np
img = Image.open("img2.jpg")
arr = np.array(img)
height = len(arr)
width = len(arr[1])


def pixelate(image_array, gradation_number=5, pixel_size=10):
grey_step = 256 // gradation_number
for i in range(0, height, pixel_size):
for j in range(0, width, pixel_size):
summa = 0
for di in range(i, min(i + pixel_size, height)):
for dj in range(j, min(j + pixel_size, width)):
red = image_array[di][dj][0]
green = image_array[di][dj][1]
blue = image_array[di][dj][2]
mean_colour = (int(red) + green + blue) // 3
summa += mean_colour
summa = int(summa // pixel_size // pixel_size)
# print(s)
for di in range(i, min(i + pixel_size, height)):
for dj in range(j, min(j + pixel_size, width)):
image_array[di][dj][0] = int(summa // grey_step) * grey_step
image_array[di][dj][1] = int(summa // grey_step) * grey_step
image_array[di][dj][2] = int(summa // grey_step) * grey_step


pixelate(arr)

res = Image.fromarray(arr)
res.save('res.jpg')
22 changes: 22 additions & 0 deletions filter_3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from PIL import Image
import numpy as np
img = Image.open("img2.jpg")
arr = np.array(img)


def pixelate(image_array, gradation_number=5, pixel_size=10):
height = len(image_array)
width = len(image_array[0])
grey_step = 256 // gradation_number
for i in range(0, height, pixel_size):
for j in range(0, width, pixel_size):
summa = np.sum(image_array[i:i + pixel_size, j:j + pixel_size])
summa = int(summa // 3 // pixel_size // pixel_size) // grey_step * grey_step

image_array[i:i + pixel_size, j:j + pixel_size] = summa


pixelate(arr, 2, 12)

res = Image.fromarray(arr)
res.save('res.jpg')
28 changes: 28 additions & 0 deletions filter_old.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from PIL import Image
import numpy as np
img = Image.open("img2.jpg")
arr = np.array(img)
a = len(arr)
a1 = len(arr[1])
i = 0
while i < a - 11:
j = 0
while j < a1 - 11:
s = 0
for n in range(i, i + 10):
for n1 in range(j, j + 10):
n1 = arr[n][n1][0]
n2 = arr[n][n1][1]
n3 = arr[n][n1][2]
M = (n1 + n2 + n3) // 3
s += M
s = int(s // 100)
for n in range(i, i + 10):
for n1 in range(j, j + 10):
arr[n][n1][0] = int(s // 50) * 50
arr[n][n1][1] = int(s // 50) * 50
arr[n][n1][2] = int(s // 50) * 50
j = j + 10
i = i + 10
res = Image.fromarray(arr)
res.save('res.jpg')
Binary file added img.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified res.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screen_1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.