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

Image embossing in Python with PIL — adding depth, azimuth, etc

我正在尝试使用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

与高深度的对比:

1
2
3
4
5
6
7
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]]

要更改方位角,请将非零系数放置在不同的angular:

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

我不太确定海拔。您可能需要更改非零系数值?我只知道它需要是一个高通滤波器。

无论如何,要使用Scipy解决方案来计算和显示图像:

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

希望这会有所帮助。

编辑:好的,正如Paul在PIL中所暗示的,您可以调整过滤器参数,甚至定义一个全新的内核。比例和偏移量参数与您要查找的内容无关。过滤器的尺寸对于调节深度最重要。

在进一步调查中,PIL不允许您将滤镜大小更改为超过5x5。似乎很奇怪。因此,您所得到的深度变化不会像您预期的那样剧烈。

对于总体控制,您可能想尝试我和Paul前面提到的Scipy解决方案。将过滤器的大小更改为可笑的值,例如21x21,然后查看它是否满足您的要求。

  • 如果您无法通过使用或组合操作(例如旋转,然后应用EMBOSS滤镜,重新旋转)(或增强对比度然后进行压印)来实现目标,则可以诉诸于更改(或创建自己的)过滤矩阵。

    在ImageFilter.py中,您将找到此类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    ##
    # 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的矩阵不会更改图像。

    其他参数是scaleoffset。对于内置的EMBOSS,值是1(比例)和128(偏移)。更改这些将改变结果的整体强度。

    来自ImageFilter.py:

    1
    2
    3
    4
    5
    # @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个元素的矩阵。

    要在不重新保存源代码的情况下对过滤器进行临时更改,只需执行以下操作:

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

    编辑:(采用NumPy方法)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    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

    1
    2
    3
    h = [[1, 0, 0]
         [0, 0, 0]
         [0, 0, -1]]
  • 与高深度的对比:

    1
    2
    3
    4
    5
    6
    7
    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]]

    要更改方位角,请将非零系数放置在不同的angular:

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

    我不太确定海拔。您可能需要更改非零系数值?我只知道它需要是一个高通滤波器。

    无论如何,要使用Scipy解决方案来计算和显示图像:

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

    希望这会有所帮助。

    编辑:好的,正如Paul在PIL中所暗示的,您可以调整过滤器参数,甚至定义一个全新的内核。比例和偏移量参数与您要查找的内容无关。过滤器的尺寸对于调节深度最重要。

    在进一步调查中,PIL不允许您将滤镜大小更改为超过5x5。似乎很奇怪。因此,您所得到的深度变化不会像您预期的那样剧烈。

    对于总体控制,您可能想尝试我和Paul前面提到的Scipy解决方案。将过滤器的大小更改为可笑的值,例如21x21,然后查看它是否满足您的要求。

  • 如果您无法通过使用或组合操作(例如旋转,然后应用EMBOSS滤镜,重新旋转)(或增强对比度然后进行压印)来实现目标,则可以诉诸于更改(或创建自己的)过滤矩阵。

    在ImageFilter.py中,您将找到此类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    ##
    # 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的矩阵不会更改图像。

    其他参数是scaleoffset。对于内置的EMBOSS,值是1(比例)和128(偏移)。更改这些将改变结果的整体强度。

    来自ImageFilter.py:

    1
    2
    3
    4
    5
    # @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个元素的矩阵。

    要在不重新保存源代码的情况下对过滤器进行临时更改,只需执行以下操作:

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

    编辑:(采用NumPy方法)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    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

    1
    2
    3
    h = [[1, 0, 0]
         [0, 0, 0]
         [0, 0, -1]]
  • 与高深度的对比:

    1
    2
    3
    4
    5
    6
    7
    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]]

    要更改方位角,请将非零系数放置在不同的angular:

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

    我不太确定海拔。您可能需要更改非零系数值?我只知道它需要是一个高通滤波器。

    无论如何,要使用Scipy解决方案来计算和显示图像:

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

    希望这会有所帮助。

    编辑:好的,正如Paul在PIL中所暗示的,您可以调整过滤器参数,甚至定义一个全新的内核。比例和偏移量参数与您要查找的内容无关。过滤器的尺寸对于调节深度最重要。

    在进一步调查中,PIL不允许您将滤镜大小更改为超过5x5。似乎很奇怪。因此,您所得到的深度变化不会像您预期的那样剧烈。

    对于总体控制,您可能想尝试我和Paul前面提到的Scipy解决方案。将过滤器的大小更改为可笑的值,例如21x21,然后查看它是否满足您的要求。

  • 如果您无法通过使用或组合操作(例如旋转,然后应用EMBOSS滤镜,重新旋转)(或增强对比度然后进行压印)来实现目标,则可以诉诸于更改(或创建自己的)过滤矩阵。

    在ImageFilter.py中,您将找到此类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    ##
    # 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的矩阵不会更改图像。

    其他参数是scaleoffset。对于内置的EMBOSS,值是1(比例)和128(偏移)。更改这些将改变结果的整体强度。

    来自ImageFilter.py:

    1
    2
    3
    4
    5
    # @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个元素的矩阵。

    要在不重新保存源代码的情况下对过滤器进行临时更改,只需执行以下操作:

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

    编辑:(采用NumPy方法)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    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

    1
    2
    3
    h = [[1, 0, 0]
         [0, 0, 0]
         [0, 0, -1]]
  • 与高深度的对比:

    1
    2
    3
    4
    5
    6
    7
    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]]

    要更改方位角,请将非零系数放置在不同的angular:

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

    我不太确定海拔。您可能需要更改非零系数值?我只知道它需要是一个高通滤波器。

    无论如何,要使用Scipy解决方案来计算和显示图像:

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

    希望这会有所帮助。

    编辑:好的,正如Paul在PIL中所暗示的,您可以调整过滤器参数,甚至定义一个全新的内核。比例和偏移量参数与您要查找的内容无关。过滤器的尺寸对于调节深度最重要。

    在进一步调查中,PIL不允许您将滤镜大小更改为超过5x5。似乎很奇怪。因此,您所得到的深度变化不会像您预期的那样剧烈。

    对于总体控制,您可能想尝试我和Paul前面提到的Scipy解决方案。将过滤器的大小更改为可笑的值,例如21x21,然后查看它是否满足您的要求。

  • 如果您无法通过使用或组合操作(例如旋转,然后应用EMBOSS滤镜,重新旋转)(或增强对比度然后进行压印)来实现目标,则可以诉诸于更改(或创建自己的)过滤矩阵。

    在ImageFilter.py中,您将找到此类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    ##
    # 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的矩阵不会更改图像。

    其他参数是scaleoffset。对于内置的EMBOSS,值是1(比例)和128(偏移)。更改这些将改变结果的整体强度。

    来自ImageFilter.py:

    1
    2
    3
    4
    5
    # @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个元素的矩阵。

    要在不重新保存源代码的情况下对过滤器进行临时更改,只需执行以下操作:

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

    编辑:(采用NumPy方法)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    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

    1
    2
    3
    h = [[1, 0, 0]
         [0, 0, 0]
         [0, 0, -1]]
  • 与高深度的对比:

    1
    2
    3
    4
    5
    6
    7
    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]]

    要更改方位角,请将非零系数放置在不同的angular:

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

    我不太确定海拔。您可能需要更改非零系数值?我只知道它需要是一个高通滤波器。

    无论如何,要使用Scipy解决方案来计算和显示图像:

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

    希望这会有所帮助。

    编辑:好的,正如Paul在PIL中所暗示的,您可以调整过滤器参数,甚至定义一个全新的内核。比例和偏移量参数与您要查找的内容无关。过滤器的尺寸对于调节深度最重要。

    在进一步调查中,PIL不允许您将滤镜大小更改为超过5x5。似乎很奇怪。因此,您所得到的深度变化不会像您预期的那样剧烈。

    对于总体控制,您可能想尝试我和Paul前面提到的Scipy解决方案。将过滤器的大小更改为可笑的值,例如21x21,然后查看它是否满足您的要求。