美文网首页
图片转素描

图片转素描

作者: DonLex | 来源:发表于2018-09-22 11:12 被阅读0次

之前分享了将图片转换为字符画的文章,但是感觉那个脚本的不是很好玩。然后突发奇想,想到了之前的人物素描头像。于是使用PIL制作素描画的小脚本就出来了

在写代码前先介绍几个概念,理解了这几个概念才能更好的理解代码
1.图像深度值:

图像深度是指存储每个像素所用的位数,也用于量度图像的色彩分辨率。

2.图像梯度:

梯度的本意是一个向量(矢量),表示某一函数在该点处的方向导数沿着该方向取得最大值,即函数在该点处沿着该方向(此梯度的方向)变化最快,变化率最大(为该梯度的模)。
图像梯度可以把图像看成二维离散函数

图像梯度其实就是这个二维离散函数的求导:

G(x,y) = dx(i,j) + dy(i,j);
dx(i,j) = I(i+1,j) - I(i,j);
dy(i,j) = I(i,j+1) - I(i,j);

其中,I是图像像素的值(如:RGB值),(i,j)为像素的坐标。
图像梯度一般也可以用中值差分:

dx(i,j) = [I(i+1,j) - I(i-1,j)]/2;
dy(i,j) = [I(i,j+1) - I(i,j-1)]/2;

图像边缘一般都是通过对图像进行梯度运算来实现的。

3.灰度:

灰度使用黑色调表示物体,即用黑色为基准色,不同的饱和度的黑色来显示图像。 每个灰度对象都具有从 0%(白色)到100%(黑色)的亮度值。

图像的手绘效果实现

1、手绘效果的几个特征:

  • 黑白灰色
  • 边界线条较重
  • 相同或相近色彩趋于白色
  • 略有光源效果

2、利用像素之间的梯度值和虚拟深度值对图像进行重构,根据灰度变化来模拟人类视觉的远近程度

3、考虑光源效果,根据灰度变化来模拟人类视觉的远近程度

  • 设计一个位于图像斜上方的虚拟光源
  • 光源相对于图像的俯视角为Elevation,方位角为Azimuth
  • 建立光源对个点梯度值的影响函数
  • 运算出各点的新像素值
light.png

编写代码

代码:

from PIL import Image
import numpy as np

a = np.asarray(Image.open('ha.png').convert('L')).astype('float')

depth = 10.  # (0-100)
grad = np.gradient(a)  # 取图像灰度的梯度值
grad_x, grad_y = grad  # 分别取横纵图像梯度值
grad_x = grad_x * depth / 100.
grad_y = grad_y * depth / 100.
A = np.sqrt(grad_x ** 2 + grad_y ** 2 + 1.)
uni_x = grad_x / A
uni_y = grad_y / A
uni_z = 1. / A

vec_el = np.pi / 2.2  # 光源的俯视角度,弧度值
vec_az = np.pi / 4.  # 光源的方位角度,弧度值
dx = np.cos(vec_el) * np.cos(vec_az)  # 光源对x 轴的影响
dy = np.cos(vec_el) * np.sin(vec_az)  # 光源对y 轴的影响
dz = np.sin(vec_el)  # 光源对z 轴的影响

b = 255 * (dx * uni_x + dy * uni_y + dz * uni_z)  # 光源归一化
b = b.clip(0, 255)

im = Image.fromarray(b.astype('uint8'))  # 重构图像
im.save('haHD.jpg')

效果对比:


ha.png haHD.jpg

这样处理出来的效果不太好,感觉有点假,所以就另外找了一种实现方法

代码:

from PIL import Image
import os

def transform(imgName):
# 图像组成:红绿蓝  (RGB)三原色组成    亮度(255,255,255)
    image = imgName
    img = Image.open(image)
    img_all = "素描" + image
    new = Image.new("L", img.size, 255)
    width, height = img.size
    img = img.convert("L")

    # 定义画笔的大小
    Pen_size = 3
    # 色差扩散器
    Color_Diff = 6
    for i in range(Pen_size + 1, width - Pen_size - 1):
        for j in range(Pen_size + 1, height - Pen_size - 1):
            # 原始的颜色
            originalColor = 255
            lcolor = sum([img.getpixel((i - r, j)) for r in range(Pen_size)]) // Pen_size
            rcolor = sum([img.getpixel((i + r, j)) for r in range(Pen_size)]) // Pen_size

            # 通道----颜料
            if abs(lcolor - rcolor) > Color_Diff:
                originalColor -= (255 - img.getpixel((i, j))) // 4
                new.putpixel((i, j), originalColor)

            ucolor = sum([img.getpixel((i, j - r)) for r in range(Pen_size)]) // Pen_size
            dcolor = sum([img.getpixel((i, j + r)) for r in range(Pen_size)]) // Pen_size

            # 通道----颜料
            if abs(ucolor - dcolor) > Color_Diff:
                originalColor -= (255 - img.getpixel((i, j))) // 4
                new.putpixel((i, j), originalColor)

            acolor = sum([img.getpixel((i - r, j - r)) for r in range(Pen_size)]) // Pen_size
            bcolor = sum([img.getpixel((i + r, j + r)) for r in range(Pen_size)]) // Pen_size

            # 通道----颜料
            if abs(acolor - bcolor) > Color_Diff:
                originalColor -= (255 - img.getpixel((i, j))) // 4
                new.putpixel((i, j), originalColor)

            qcolor = sum([img.getpixel((i + r, j - r)) for r in range(Pen_size)]) // Pen_size
            wcolor = sum([img.getpixel((i - r, j + r)) for r in range(Pen_size)]) // Pen_size

            # 通道----颜料
            if abs(qcolor - wcolor) > Color_Diff:
                originalColor -= (255 - img.getpixel((i, j))) // 4
                new.putpixel((i, j), originalColor)

    new.save(img_all)

    # 打开转换后的图片
    os.system(img_all)

if __name__ == '__main__':
    imageName = "ha.png"
    transform(imageName)

具体的实现原理不是很清楚。但是不影响使用,并且实现的效果也是杠杠的

素描ha.png

相关文章

网友评论

      本文标题:图片转素描

      本文链接:https://www.haomeiwen.com/subject/dzgtoftx.html