Как стать автором
Обновить

Комментарии 50

плашку туториал надо было прилепить.
Исправил.
"Опошлю" и эту статью «магией» «консольного „фотошопа“» :)
Оттенки серого
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 и использовать для всех этих эффектов свои формулы-программы. :)
Это конечно хорошо, но не спортивно, самому же интереснее. Конечно, так удобнее и быстрее, но для обучения, можно и попрактиковаться.
Отлично, можно и спортивно :) Градация серого по вашей формуле:

convert input.jpg -fx '(r+g+b)/3' gray-fx.jpg

Или «шум»:

convert input.jpg -fx 'u+0.5*u*rand()' noise.jpg
Кстати насчёт оттенков серого: автор мог бы всё же поинтересоваться, и узнать, что обычно берется не среднее, а что-то вроде
(R*30 + G*59 + B*11)/100
ImageMagick это здорово, но на деле PIL быстрее
А остальные эффекты где можно найти? Может какой ресурс есть.
Формула, использованная автором, очень грубая и лобовая — и тем не менее, имеет место и такой вариант тоже.
Вообще не существует какой-то одной единственно-верной формулы пересчета в grayscale, аналогично для сепии и пр.
Там если углубляться — черт ногу сломит.
Безусловно, но и углубляться никуда не нужно — эти коэффициенты всего лишь отражают восприятие разных цветов человеком. Оттуда же и растут ноги у цветовых моделей типа YUV.
Спасибо, как раз неспешно в обеденное время прохожу python challenge, там надо как раз по всякому ковырять картинки на предмет зашифрованного содержимого.
Пойду посмотрю, что это.
> Это очень просто, достаточно лишь каждое значение пиксела вычесть из 255.
> draw.point((i, j), (255 — a, 255 — b, 255 — c))

Здесь что-то не сходится. Особенно если там по вычету 255, то вычитать 255 не имеет смысла.
Я вас не понимаю. Что не сходится?
Простите, я прочел как из пиксела вычесть 255.
Еще раз извиняюсь.
А насколько это все медленно/быстро работает?
Для картинки из примеров приблизительно 3 сек на моём нетбуке.
OpenCV в среднем такие операции на картинке такого размера выполняет за 1 мс, без учета IO конечно.
Пойду почитаю о нем.
Сами посудите, если у вас процессор 2ГГц, получается что на обработку 1 пикселя уходит 30 000 тактов. Как-то многовато
Но у меня и нетбук слабенький очень.
Это просто задача не для Python. Безопасность, проверки границ и др. очень сильно тормозит работу.
Я полностью согласен. Питоновская скорость работы тут не годится для реального применения, но тем не менее получается красивый и лаконичный код, писать это на питоне нужно, я думаю, только для практики.
PIL написан на сях, поэтому PIL достаточно быстр. Быстрее чем ImageMagick, например.
Но только если работать попиксельно так, как это делается в топике, то думаю в силу вступают проверки массивов Python и всю скорость съедают. Или я не прав?
Это разве длиннее?
#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 скрипт, подправили, сохранили — можно работать дальше.
Если бы я писал тулсет, я всё равно писал бы на C++. Компиляция не так страшна. Но даже если бы и понадобилось на Python, то написать часть оптимизированную на Си (вроде Python это позволяет, и выше мне подсказывают, что PIL так и сделан), а вызовы дёргать из Python. И тогда реализовать метод с матрицей 5х5 для цветового преобразования изображения.
Так вот для этого и используют Python+PIL :)
И не нужно писать ничего низкоуровневого — все написано до нас.
А в PIL есть что-то вроде:
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?
Image.convert принимает второй аргумент — матрицу. Правда у меня не получилось использовать отрицательные коэффициенты.
Ну, 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 раз. Второй вариант идентичен вашему (ну вдруг вам именно это нужно было), третий — правильный. Все остальные преобразования делаются аналогично.
Тут просто тупой перебор каждого пикселя, это ахтунг. Если использовать numpy, то думаю скорость возрастет на порядок, а то и на два
Особенно если делать более сложные вещи, например, размытие, где нужно минор (подматрицу) постоянно брать.
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.
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



Есть ещё интересное создание чёрно-белых изображений с матрицами возмущений, но там уже не так очевидно, лучше почитайте где-нибудь.
Когда-то делал сепию для одного проекта, нашёл вот такие коэффициенты:
def sepia(im):
    if not "RGB" in im.mode: im = im.convert("RGB")
    im = im.convert("RGB",(0.393, 0.769, 0.189, 0,
                           0.349, 0.686, 0.168, 0,
                           0.272, 0.534, 0.131, 0))
    
    return im
Зачем использовать draw, если можно писать напрямую в пиксели аналогично тому, как вы их читаете? Это будет быстрее. В качестве туториала статья никуда не годится. Такое ощущение, что вы освоили цикл пробежки по пикселям и все. PIL позволяет делать куда больше. Позволяет пережимать в различные форматы, увеличивать/уменьшать с различным качеством, вырезать/вставлять куски изображений. Все, что вы сделали можно сделать фильтрами 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 = 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:
image
Коэффицент восприимчивости RGB — не, не слышали
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории