Comments 50
плашку туториал надо было прилепить.
"Опошлю" и эту статью «магией» «консольного „фотошопа“» :)
Оттенки серого
Получается выразительней. Наверное, потому что используется что-нибудь посложнее среднего значения
Сепия
Негатив
Яркость. Положительный параметр делает ярче, отрицательный — темнее
Бинаризацию вообще можно многими путями:
Вот результаты:
Еще можно расчехлить -fx и использовать для всех этих эффектов свои формулы-программы. :)
Оттенки серого
convert input.jpg -colorspace gray gray.jpg
Получается выразительней. Наверное, потому что используется что-нибудь посложнее среднего значения
Скрытый текст

Сепия
convert input.jpg -sepia-tone 80% sepia.jpg
Негатив
convert input.jpg -negate negated.jpg
Яркость. Положительный параметр делает ярче, отрицательный — темнее
convert input.jpg -brightness-contrast -20% dark.jpg
convert input.jpg -brightness-contrast 20% bright.jpg
Бинаризацию вообще можно многими путями:
convert input.jpg -monochrome -normalize binarize3.jpg
convert input.jpg -remap pattern:gray50 binarize2.jpg
convert input.jpg -colorspace gray +dither -colors 2 binarize1.jpg
Вот результаты:
Скрытый текст





Еще можно расчехлить -fx и использовать для всех этих эффектов свои формулы-программы. :)
Это конечно хорошо, но не спортивно, самому же интереснее. Конечно, так удобнее и быстрее, но для обучения, можно и попрактиковаться.
Кстати насчёт оттенков серого: автор мог бы всё же поинтересоваться, и узнать, что обычно берется не среднее, а что-то вроде
(R*30 + G*59 + B*11)/100
(R*30 + G*59 + B*11)/100
ImageMagick это здорово, но на деле PIL быстрее
Оттенки серого считаются вовсе не так.
А остальные эффекты где можно найти? Может какой ресурс есть.
Формула, использованная автором, очень грубая и лобовая — и тем не менее, имеет место и такой вариант тоже.
Вообще не существует какой-то одной единственно-верной формулы пересчета в grayscale, аналогично для сепии и пр.
Там если углубляться — черт ногу сломит.
Вообще не существует какой-то одной единственно-верной формулы пересчета в grayscale, аналогично для сепии и пр.
Там если углубляться — черт ногу сломит.
Спасибо, как раз неспешно в обеденное время прохожу python challenge, там надо как раз по всякому ковырять картинки на предмет зашифрованного содержимого.
> Это очень просто, достаточно лишь каждое значение пиксела вычесть из 255.
> draw.point((i, j), (255 — a, 255 — b, 255 — c))
Здесь что-то не сходится. Особенно если там по вычету 255, то вычитать 255 не имеет смысла.
> draw.point((i, j), (255 — a, 255 — b, 255 — c))
Здесь что-то не сходится. Особенно если там по вычету 255, то вычитать 255 не имеет смысла.
А насколько это все медленно/быстро работает?
Для картинки из примеров приблизительно 3 сек на моём нетбуке.
OpenCV в среднем такие операции на картинке такого размера выполняет за 1 мс, без учета IO конечно.
Пойду почитаю о нем.
Сами посудите, если у вас процессор 2ГГц, получается что на обработку 1 пикселя уходит 30 000 тактов. Как-то многовато
Но у меня и нетбук слабенький очень.
Это просто задача не для Python. Безопасность, проверки границ и др. очень сильно тормозит работу.
Я полностью согласен. Питоновская скорость работы тут не годится для реального применения, но тем не менее получается красивый и лаконичный код, писать это на питоне нужно, я думаю, только для практики.
PIL написан на сях, поэтому PIL достаточно быстр. Быстрее чем ImageMagick, например.
Это разве длиннее?
#include <QCoreApplication>
#include <QImage>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QImage img("test.jpg");
for (int i = 0; i < img.width(); i++) {
for (int j = 0; j < img.height(); j++) {
QRgb rgb = img.pixel(j, i);
int s = (qRed(rgb) + qGreen(rgb) + qBlue(rgb)) / 3;
img.setPixel(j, i, qRgb(s, s, s));
}
}
img.save("outtest.jpg", "JPEG");
return 0;
}
time ./imagecvt
real 0m0.033s
user 0m0.023s
sys 0m0.010s
Дело не только а размере кода (хотя код на Питоне из-за особенностей языка всеравно будет короче), а в том, что не нужен процесс компиляции.
Т.е. делаете вы тулсет для работы с изображениями, вдруг требования немного изменились — открыли в notepad скрипт, подправили, сохранили — можно работать дальше.
Т.е. делаете вы тулсет для работы с изображениями, вдруг требования немного изменились — открыли в notepad скрипт, подправили, сохранили — можно работать дальше.
Если бы я писал тулсет, я всё равно писал бы на C++. Компиляция не так страшна. Но даже если бы и понадобилось на Python, то написать часть оптимизированную на Си (вроде Python это позволяет, и выше мне подсказывают, что PIL так и сделан), а вызовы дёргать из Python. И тогда реализовать метод с матрицей 5х5 для цветового преобразования изображения.
Так вот для этого и используют Python+PIL :)
И не нужно писать ничего низкоуровневого — все написано до нас.
И не нужно писать ничего низкоуровневого — все написано до нас.
А в PIL есть что-то вроде:
Для инверсии?
Или
Для grayscale?
image2 = image.colorTransform([[-1, 0, 0, 0, 1],
[0, -1, 0, 0, 1],
[0, 0, -1, 0, 1],
[0, 0, 0, 1, 0],
[0, 0, 0, 0, 1]]);
Для инверсии?
Или
[[0.299, 0.587, 0.114, 0, 0],
[0.299, 0.587, 0.114, 0, 0],
[0.299, 0.587, 0.114, 0, 0],
[0, 0, 0, 1, 0],
[0, 0, 0, 0, 1]]
Для grayscale?
Ну, PIL быстрее, чем ImageMagick, например
Код примера
from PIL import Image, ImageDraw, ImageMath
def bw1(s):
d = s.copy()
pix = d.load()
draw = ImageDraw.Draw(d)
for i in range(s.size[0]):
for j in range(s.size[1]):
a = pix[i, j][0]
b = pix[i, j][1]
c = pix[i, j][2]
S = (a + b + c) // 3
draw.point((i, j), (S, S, S))
return d
def bw2(s):
r, g, b = s.split()
return ImageMath.eval("convert((r + g + b) / 3, 'L')", r=r, g=g, b=b)
def bw3(s):
return s.convert('L')
s = Image.open('s.jpg')
bw1(s).save('bw1.jpg')
bw2(s).save('bw2.jpg')
bw3(s).save('bw3.jpg')
from timeit import timeit
print 'Per pixel:', timeit(lambda: bw1(s), number=10)
print 'ImageMath:', timeit(lambda: bw2(s), number=10)
print 'Image.convert:', timeit(lambda: bw3(s), number=10)
$ ./test.py
Per pixel: 7.06867003441
ImageMath: 0.0504641532898
Image.convert: 0.00568413734436
Разница более чем в 100 и 1000 раз. Второй вариант идентичен вашему (ну вдруг вам именно это нужно было), третий — правильный. Все остальные преобразования делаются аналогично.
c = max(c, 0)
c = min(c, 255)
А лучше вынесли бы в отдельную функцию:
def limit(value, low=0, high=255):
return min(max(value, low), high)
Другие алгоритмы:
Мой вариант сепии, где картинка получается более насыщенной. У сепии RGB = (112, 66, 20), компоненты с разницей в 46, поэтому я просто складываю и вычитаю на 46.

Чёрно-белое со случайным порогом (factor). Выглядит лучше, при просмотре издалека.

Есть ещё интересное создание чёрно-белых изображений с матрицами возмущений, но там уже не так очевидно, лучше почитайте где-нибудь.
Мой вариант сепии, где картинка получается более насыщенной. У сепии RGB = (112, 66, 20), компоненты с разницей в 46, поэтому я просто складываю и вычитаю на 46.
mean = (R + G + B) / 3
R = mean + 46 // если больше 255, то = 255
G = mean
B = mean - 46 // если меньше 0, то = 0

Чёрно-белое со случайным порогом (factor). Выглядит лучше, при просмотре издалека.
mean = (R + G + B) / 3
factor = rand()%255 + 1 // Случайное число от 1 до 256
Если mean < factor то
result = 0
Иначе
result = 255
R = result
G = result
B = result

Есть ещё интересное создание чёрно-белых изображений с матрицами возмущений, но там уже не так очевидно, лучше почитайте где-нибудь.
Зачем использовать draw, если можно писать напрямую в пиксели аналогично тому, как вы их читаете? Это будет быстрее. В качестве туториала статья никуда не годится. Такое ощущение, что вы освоили цикл пробежки по пикселям и все. PIL позволяет делать куда больше. Позволяет пережимать в различные форматы, увеличивать/уменьшать с различным качеством, вырезать/вставлять куски изображений. Все, что вы сделали можно сделать фильтрами PIL и работать это будет гораздо быстрее.
Не понимаю, почему человеку влепили за это минус, он прав!
getpixel и putpixel работает несколько быстрее, да и PIL из коробки это умеет делать, причем ресурсоемкие операции написаны на Си.
getpixel и putpixel работает несколько быстрее, да и PIL из коробки это умеет делать, причем ресурсоемкие операции написаны на Си.
Как эффективнее всего переставлять пиксели местами? Хочу написать программу, создающую изображение как результат комплексной функции, взятой от исходного изображения, где горизонтальные координаты пикселей - действительная ось, а вертикальные - мнимая.
if (mode == 2):
goto 100
a = pix[i, j][0]
b = pix[i, j][1]
c = pix[i, j][2]
->
a, b, c = pix[i, j]
Все хорошо. Вот только это далеко не Pythonic way. Да и вообще с python мало связано. То же можно было на чем угодно продемонстрировать.
Простите меня за непрофильный вопрос, который, чесслово, мучает со времен школы: правильно ли говорить «играться» и существует ли такое слово? «Биться об стену» и «бить баклуши» понимаю, а вот с «играть с самим собой»… какие-то неприличные синонимы в голову лезут. Ау, Mithgol!
Сделал на базе вашего интересный эффект:
Добавляем в начало кода строки
Image2 нужен, в качестве «палитры» оригинальных пикселей, без искажения.
Сам код (добавляем перед сохранением изображения):
Вот, например, стандартная картинка из Windows7, пропущенная через прогу с параметрами mul = 10, freq = 40:

Добавляем в начало кода строки
image2 = Image.open("test.jpg")
pix = image2.load() #Выгружаем значения пикселей.
Image2 нужен, в качестве «палитры» оригинальных пикселей, без искажения.
Сам код (добавляем перед сохранением изображения):
if (mode == 6):
mul = int(input('Multiple: '))
abc = float(input('Frequency: '))
freq = abc / 1000
for i in range(width):
for j in range(1,height-1):
v = round((mul*math.sin(i*freq)))
m = v + j -mul
if (m > (height-1)):
m = height-1
if (m < 1):
m = 1
a = pix[i,m][0]
b = pix[i,m][1]
c = pix[i,m][2]
draw.point((i,j),(a,b,c))
Вот, например, стандартная картинка из Windows7, пропущенная через прогу с параметрами mul = 10, freq = 40:

Коэффицент восприимчивости RGB — не, не слышали
Sign up to leave a comment.
Играемся с изображениями в Python