使用PIL在Python中进行图像浮雕处理 - 添加深度、方位等

7 投票
2 回答
6177 浏览
提问于 2025-04-15 17:50

我正在尝试使用 PIL 给一张图片添加浮雕效果。

PIL 提供了一种简单的方法来给图片添加浮雕效果(使用 ImageFilter.EMBOSS)。

在像 GIMP 这样的图像编辑软件中,你可以调整一些参数,比如方位角、深度和高度,来改变浮雕效果。

那么,如何在 PIL 中做到这一点呢?至少我想调整浮雕效果的“深度”。

更新:我尝试了 Paul 提出的建议(修改 filterargs,比如 scale, offset 和矩阵),但我还是无法改变“深度”效果。所以我仍在寻找答案。

这是使用 PIL(左侧)和 GIMP(右侧)进行浮雕效果的对比。原始图片可以在这里找到,http://www.linuxtopia.org/online_books/graphics_tools/gimp_advanced_guide/gimp_guide_node74.html

alt text

2 个回答

2

要增加浮雕滤镜的深度,可以增大滤镜的遮罩半径。深度低的时候:

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(Python Imaging Library),你可以调整滤镜参数,甚至定义一个全新的滤波器。缩放和偏移参数和你想要的效果没有关系。滤镜的大小对调整深度是最重要的。

经过进一步调查,PIL不允许你把滤镜大小改得超过5x5。这听起来有点奇怪。因此,你可能不会得到你预期的那么明显的深度变化。

如果想要完全控制,建议你试试我和保罗之前提到的Scipy解决方案。把滤镜大小改得很大,比如21x21,看看是否能达到你想要的效果。

10

如果你通过使用或组合一些操作(比如先旋转图像,然后应用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个像素的组合,按照矩阵中的值进行加权。例如,如果矩阵中全是0,只有中间是1,那么图像就不会改变。

还有两个额外的参数scaleoffset。对于内置的EMBOSS,值是1(scale)和128(offset)。改变这些值会影响结果的整体强度。

来自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

之后

alt text

撰写回答