弗洛伊德-斯坦伯格抖动算法

弗洛伊德-斯坦伯格抖动算法

这是一个真实的魔法技术。它愚弄了你的眼睛和大脑,让你以为自己看到的颜色要比实际的多。

一般来说,抖动是通过增加人工噪声去减少一个图像的颜色空间,主旨在于,一个区域的光量应该保持一致。

弗洛伊德-斯坦伯格抖动算法对周围的像素使用非均匀分布的量化误差达到抖动的目的。这就意味着要先将中心像素四舍五入为0或1,而后将残差加入其周围的像素中。

以上你看到的三张图片都是灰阶抖动的,它们全部都是只由两种颜色的噪音组成,而其余的信息,当然是因为你的大脑在转喽。

如果您想去看这些画作的真品,可以自行Google哦,在C64艺术品就能看到,这些画作一般都有4、8或16种颜色,而抖动技术又让我们感受到了更宽的颜色范围。

算法实现

import numpy as np
from PIL import Image
from requests import get
from bokeh.plotting import figure, show, output_notebook
from bokeh.layouts import layout
from bokeh.palettes import gray

output_notebook()

def image_dither(path, black='#000000', white='#ffffff'):
    image_rgb = read_image(path)
    image_gray = grayscale(image_rgb)
    image_bw = floyd_steinberg(image_gray)
    show(layout([[
        plot(image_gray, palette=gray(256)),
        plot(image_bw, palette=[black, white])        
    ]]))
def floyd_steinberg(image):
    image = image.copy()
    distribution = np.array([7, 3, 5, 1], dtype=float) / 16
    u = np.array([0, 1, 1, 1])
    v = np.array([1, -1, 0, 1])
    
    for y in range(image.shape[0] - 1):
        for x in range(image.shape[1] - 1):
            value = np.round(image[y, x])
            error = image[y, x] - value
            image[y, x] = value
            image[y + u, x + v] += error * distribution
            
    image[:, -1] = 1
    image[-1, :] = 1
    return image
def grayscale(image):
    height, width, _ = image.shape
    
    image = np.array(image, dtype=np.float32) / 255
    image = image[:, :, 0] * .21 + \
            image[:, :, 1] * .72 + \
            image[:, :, 2] * .07
    
    return image.reshape(height, width)
def read_image(path, size=400):
    if path.startswith('https://'):
        image = Image.open(get(path, stream=True).raw)
    else:
        image = Image.open(path)

width, height = image.size

width, height = size, int(size * height / width)

image = image.resize((width, height), Image.ANTIALIAS)

data = image.getdata()

assert data.bands in [3, 4], 'RGB or RGBA image is required'

raw = np.array(data, dtype=np.uint8)

return raw.reshape(height, width, data.bands)

def plot(image, palette):

y, x = image.shape

plot = figure(x_range=(0, x), y_range=(0, y),

plot_width=x, plot_height=y)

plot.axis.visible = False

plot.toolbar_location = None

plot.min_border = 0

plot.image([np.flipud(image)], x=0, y=0, dw=x, dh=y,

palette=palette)

return plot

测试

 

URL = lambda name: 'https://raw.githubusercontent.com/coells/100days/master/resource/day 96 - %s.jpg' % (name,)

# URL = lambda name: './resource/day 96 - %s.jpg' % (name,)

image_dither('./resource/day 96 - valinka.jpg')

  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值