Тиснение изображения в Python с помощью PIL - добавление глубины, азимута и т. Д. - PullRequest
6 голосов
/ 09 января 2010

Я пытаюсь выбить изображение, используя PIL .

PIL предоставляет базовый способ нанесения изображения (используя ImageFilter.EMBOSS).

В пакетах для редактирования изображений, таких как GIMP, вы можете изменять параметры, такие как азимут, глубина и высота в этом тисненом изображении.

Как это сделать с PIL? По крайней мере, я хочу настроить глубину рельефного изображения.

Обновление: Я попробовал то, что предложил Пол (изменив filterargs, например scale, offset и матрицу), но я не смог изменить эффект "глубины". Так что все еще ищу ответ.

Вот сравнение эффекта тиснения с использованием PIL (слева) и GIMP (справа). Оригинальное изображение находится здесь, http://www.linuxtopia.org/online_books/graphics_tools/gimp_advanced_guide/gimp_guide_node74.html.

alt text

Ответы [ 2 ]

8 голосов
/ 09 января 2010

Если вы не можете достичь своей цели с помощью или сочетанием операций (например, вращение, затем применение фильтра EMBOSS, повторное вращение) (или усиление контраста, а затем тиснение), тогда вы можете прибегнуть к изменению (или созданию собственного ) матрица фильтра.

В ImageFilter.py вы найдете этот класс:

##
# Embossing filter.

class EMBOSS(BuiltinFilter):
    name = "Emboss"
    filterargs = (3, 3), 1, 128, (
        -1,  0,  0,
        0,  1,  0,
        0,  0,  0
        )

Помещение -1 в другой угол матрицы изменит азимут, а его значение -2 может получить эффект, который вы ищете.

Матрица применяется попиксельно. Каждый элемент в матрице соответствует текущему пикселю и окружающим пикселям; значение центра, представляющее текущий пиксель. Новый преобразованный текущий пиксель будет создан как комбинация всех 9 пикселей, взвешенных по значениям в матрице. Например, матрица со всеми нулями и 1 в центре не изменит изображение.

Дополнительные параметры: scale и offset. Для встроенного EMBOSS значения 1 (шкала) и 128 (смещение). Их изменение изменит общую силу результата.

Из ImageFilter.py:

# @keyparam scale Scale factor.  If given, the result for each
#    pixel is divided by this value.  The default is the sum
#    of the kernel weights.
# @keyparam offset Offset.  If given, this value is added to the
#    result, after it has been divided by the scale factor.

Поскольку я не знаком с эффектами параметра «глубина» в GIMP, я не могу сказать, какой из них, скорее всего, будет делать то, что вы хотите.

Вы также можете сделать матрицу другого размера. Замените (3,3) на (5,5), а затем создайте 25-элементную матрицу.

Чтобы сделать временные изменения в фильтре без повторного сохранения исходного кода, просто сделайте это:

ImageFilter.EMBOSS.filterargs=((3, 3), 1, 128, (-1, 0, 0, 0, 1, 0, 0, 0, 0))

Редактировать: (подход NumPy )

from PIL import Image
import numpy

# defining azimuth, elevation, and depth
ele = numpy.pi/2.2 # radians
azi = numpy.pi/4.  # radians
dep = 10.          # (0-100)

# get a B&W version of the image
img = Image.open('daisy.jpg').convert('L') 
# get an array
a = numpy.asarray(img).astype('float')
# find the gradient
grad = numpy.gradient(a)
# (it is two arrays: grad_x and grad_y)
grad_x, grad_y = grad
# getting the unit incident ray
gd = numpy.cos(ele) # length of projection of ray on ground plane
dx = gd*numpy.cos(azi)
dy = gd*numpy.sin(azi)
dz = numpy.sin(ele)
# adjusting the gradient by the "depth" factor
# (I think this is how GIMP defines it)
grad_x = grad_x*dep/100.
grad_y = grad_y*dep/100.
# finding the unit normal vectors for the image
leng = numpy.sqrt(grad_x**2 + grad_y**2 + 1.)
uni_x = grad_x/leng
uni_y = grad_y/leng
uni_z = 1./leng
# take the dot product
a2 = 255*(dx*uni_x + dy*uni_y + dz*uni_z)
# avoid overflow
a2 = a2.clip(0,255)
# you must convert back to uint8 /before/ converting to an image
img2 = Image.fromarray(a2.astype('uint8')) 
img2.save('daisy2.png')

Надеюсь, это поможет. Теперь я понимаю, почему вы были разочарованы результатами PIL. Wolfram Mathworld - хороший ресурс для переподготовки векторной алгебры.

До

alt text

* После 1042 *

alt text

2 голосов
/ 25 января 2010

Чтобы увеличить глубину тиснения фильтра, увеличьте радиус маски фильтра. Малая глубина:

h = [[1, 0, 0]
     [0, 0, 0]
     [0, 0, -1]]

против большой глубины:

h = [[1, 0, 0, 0, 0, 0, 0]
     [0, 0, 0, 0, 0, 0, 0]
     [0, 0, 0, 0, 0, 0, 0]
     [0, 0, 0, 0, 0, 0, 0]
     [0, 0, 0, 0, 0, 0, 0]
     [0, 0, 0, 0, 0, 0, 0]
     [0, 0, 0, 0, 0, 0, -1]]

Чтобы изменить азимут, поместите ненулевые коэффициенты под другим углом:

h = [[0, 0, 1]
     [0, 0, 0]
     [-1, 0, 0]]

Я не совсем уверен насчет высоты. Вам может понадобиться изменить ненулевые значения коэффициентов? Я просто знаю, что это должен быть фильтр верхних частот.

В любом случае для вычисления и отображения изображения с использованием решения Scipy:

import scipy.misc, scipy.signal
im = scipy.misc.imread(filename)
im_out = scipy.signal.convolve2d(im, h, 'same')
scipy.misc.imshow(im_out)

Надеюсь, это поможет.

РЕДАКТИРОВАТЬ: Хорошо, как Павел намекнул с PIL, вы можете настроить параметры фильтра или даже определить совершенно новое ядро. Параметры масштаба и смещения не имеют ничего общего с тем, что вы ищете. Размер фильтра наиболее важен для регулировки глубины.

При дальнейшем исследовании PIL не позволяет изменять размер фильтра выше 5x5. Кажется странным Следовательно, вы не получите столь резкого изменения глубины, как вы, вероятно, ожидаете.

Для полного контроля, вы можете попробовать решение Scipy, о котором я и Пол упоминали ранее. Измените размер фильтра на что-то смешное, например, 21x21, и посмотрите, имеет ли он тот тип разницы, который вы хотите.

...