误差扩散、Floyd-Steinberg 抖动、有序抖动、Riemersma 抖动算法
- 1.误差扩散算法详解
- 算法步骤
- Floyd-Steinberg 算法
- 公式
- Python 实现
- 详细解释
- 优缺点
- 2.有序抖动算法详解
- 算法步骤
- Bayer矩阵
- 公式
- Python 实现
- 详细解释
- 优缺点
- 3.Riemersma 抖动算法详解
- 算法步骤
- 公式
- Python 实现
- 详细解释
- 优缺点
1.误差扩散算法详解
误差扩散算法(Error Diffusion Algorithm)是一种用于图像抖动(Dithering)和半色调(Halftoning)的技术。它通过将像素的量化误差扩散到邻近像素,从而在整体上保持图像的灰度特性。这种方法通常用于图像打印和显示,特别是将灰度图像转换为二值图像时。
算法步骤
- 遍历图像:逐像素处理图像,从左到右,从上到下。
- 量化像素:将当前像素的灰度值量化为目标值(通常是0或255)。
- 计算误差:计算当前像素的量化误差。
- 扩散误差:将误差分配给邻近像素。
常见的误差扩散算法有:
- Floyd-Steinberg算法
- Jarvis, Judice, and Ninke算法
- Stucki算法
Floyd-Steinberg 算法
Floyd-Steinberg 算法是最常用的误差扩散算法之一。其扩散矩阵如下:
这里,* 表示当前处理的像素,其他值表示扩散给邻近像素的误差比例。
公式
Python 实现
以下是Floyd-Steinberg误差扩散算法的Python实现代码:
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
def floyd_steinberg_dithering(image):
"""
Floyd-Steinberg 误差扩散算法实现
参数:
image (PIL.Image): 灰度图像
返回:
PIL.Image: 二值化后的图像
"""
# 将图像转换为灰度图像
grayscale_image = image.convert("L")
image_array = np.array(grayscale_image, dtype=float)
# 获取图像的行和列
rows, cols = image_array.shape
# 遍历图像
for y in range(rows):
for x in range(cols):
old_pixel = image_array[y, x]
new_pixel = 0 if old_pixel < 128 else 255
image_array[y, x] = new_pixel
quant_error = old_pixel - new_pixel
if x + 1 < cols:
image_array[y, x + 1] += quant_error * 7 / 16
if x - 1 >= 0 and y + 1 < rows:
image_array[y + 1, x - 1] += quant_error * 3 / 16
if y + 1 < rows:
image_array[y + 1, x] += quant_error * 5 / 16
if x + 1 < cols and y + 1 < rows:
image_array[y + 1, x + 1] += quant_error * 1 / 16
# 将处理后的数组转换为图像
dithered_image = Image.fromarray(np.clip(image_array, 0, 255).astype(np.uint8))
return dithered_image
# 示例用法
if __name__ == "__main__":
image = Image.open('example.jpg') # 打开原始图像
dithered_image = floyd_steinberg_dithering(image) # 调用Floyd-Steinberg误差扩散算法
dithered_image.show() # 显示二值化后的图像
dithered_image.save('dithered_example.jpg') # 保存二值化后的图像
# 显示原始图像和二值化后的图像
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.title('Original Image')
plt.imshow(image.convert("L"), cmap='gray')
plt.axis('off')
plt.subplot(1, 2, 2)
plt.title('Dithered Image')
plt.imshow(dithered_image, cmap='gray')
plt.axis('off')
plt.show()
详细解释
-
读取图像和转换为灰度图像:
image = Image.open('example.jpg') grayscale_image = image.convert("L")
-
将灰度图像转换为NumPy数组:
image_array = np.array(grayscale_image, dtype=float)
-
遍历图像并进行量化和误差扩散:
for y in range(rows): for x in range(cols): old_pixel = image_array[y, x] new_pixel = 0 if old_pixel < 128 else 255 image_array[y, x] = new_pixel quant_error = old_pixel - new_pixel if x + 1 < cols: image_array[y, x + 1] += quant_error * 7 / 16 if x - 1 >= 0 and y + 1 < rows: image_array[y + 1, x - 1] += quant_error * 3 / 16 if y + 1 < rows: image_array[y + 1, x] += quant_error * 5 / 16 if x + 1 < cols and y + 1 < rows: image_array[y + 1, x + 1] += quant_error * 1 / 16
-
将处理后的数组转换为图像:
dithered_image = Image.fromarray(np.clip(image_array, 0, 255).astype(np.uint8))
-
显示和保存二值化后的图像:
dithered_image.show() dithered_image.save('dithered_example.jpg')
-
显示原始图像和二值化后的图像:
plt.subplot(1, 2, 1) plt.title('Original Image') plt.imshow(image.convert("L"), cmap='gray') plt.axis('off') plt.subplot(1, 2, 2) plt.title('Dithered Image') plt.imshow(dithered_image, cmap='gray') plt.axis('off') plt.show()
优缺点
优点:
- 高效:误差扩散算法在保留图像细节和灰度特性方面非常有效。
- 实现简单:算法逻辑简单,易于实现和理解。
缺点:
- 可能引入噪声:在某些图像中,可能会引入明显的噪声或伪影。
- 计算复杂度高:对大图像的处理可能较慢,特别是在高分辨率图像中。
误差扩散算法在图像抖动和半色调处理中非常有用,特别适用于需要将灰度图像转换为二值图像的应用场景,如打印和显示设备。
2.有序抖动算法详解
有序抖动算法(Ordered Dithering)是一种用于图像抖动和半色调的技术。它通过使用预先定义的抖动矩阵(dithering matrix)来量化图像像素,从而在保持图像灰度特性的同时,产生视觉上更平滑的二值图像。有序抖动算法简单且计算效率高,广泛应用于图像显示和打印。
算法步骤
- 生成抖动矩阵:创建一个预定义的抖动矩阵。
- 遍历图像:逐像素处理图像,根据抖动矩阵对每个像素进行量化。
- 更新像素值:将量化后的像素值更新到输出图像中。
常见的抖动矩阵有:
- Bayer矩阵
- Halftone矩阵
Bayer矩阵
Bayer矩阵是一种常见的抖动矩阵,可以递归生成。基本的
2
×
2
2 \times 2
2×2 Bayer矩阵如下:
通过递归扩展可以生成更大的矩阵,例如
4
×
4
4 \times 4
4×4 Bayer矩阵:
公式
Python 实现
以下是有序抖动算法的Python实现代码:
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
def generate_bayer_matrix(n):
if n == 2:
return np.array([
[0, 2],
[3, 1]
])
else:
smaller_matrix = generate_bayer_matrix(n // 2)
return np.block([
[4 * smaller_matrix + 0, 4 * smaller_matrix + 2],
[4 * smaller_matrix + 3, 4 * smaller_matrix + 1]
])
def ordered_dithering(image, bayer_matrix):
"""
有序抖动算法实现
参数:
image (PIL.Image): 灰度图像
bayer_matrix (numpy.ndarray): 抖动矩阵
返回:
PIL.Image: 二值化后的图像
"""
# 将图像转换为灰度图像
grayscale_image = image.convert("L")
image_array = np.array(grayscale_image, dtype=float)
# 获取图像的行和列
rows, cols = image_array.shape
n = bayer_matrix.shape[0]
# 遍历图像
for y in range(rows):
for x in range(cols):
threshold = (bayer_matrix[y % n, x % n] + 0.5) / (n * n)
image_array[y, x] = 255 if (image_array[y, x] / 255.0) >= threshold else 0
# 将处理后的数组转换为图像
dithered_image = Image.fromarray(image_array.astype(np.uint8))
return dithered_image
# 示例用法
if __name__ == "__main__":
image = Image.open('example.jpg') # 打开原始图像
bayer_matrix = generate_bayer_matrix(4) # 生成 4x4 Bayer 矩阵
dithered_image = ordered_dithering(image, bayer_matrix) # 调用有序抖动算法
dithered_image.show() # 显示二值化后的图像
dithered_image.save('dithered_example.jpg') # 保存二值化后的图像
# 显示原始图像和二值化后的图像
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.title('Original Image')
plt.imshow(image.convert("L"), cmap='gray')
plt.axis('off')
plt.subplot(1, 2, 2)
plt.title('Dithered Image')
plt.imshow(dithered_image, cmap='gray')
plt.axis('off')
plt.show()
详细解释
-
生成Bayer矩阵:
def generate_bayer_matrix(n): if n == 2: return np.array([ [0, 2], [3, 1] ]) else: smaller_matrix = generate_bayer_matrix(n // 2) return np.block([ [4 * smaller_matrix + 0, 4 * smaller_matrix + 2], [4 * smaller_matrix + 3, 4 * smaller_matrix + 1] ])
-
读取图像并转换为灰度图像:
image = Image.open('example.jpg') grayscale_image = image.convert("L")
-
将灰度图像转换为NumPy数组:
image_array = np.array(grayscale_image, dtype=float)
-
遍历图像并根据抖动矩阵进行量化:
rows, cols = image_array.shape n = bayer_matrix.shape[0] for y in range(rows): for x in range(cols): threshold = (bayer_matrix[y % n, x % n] + 0.5) / (n * n) image_array[y, x] = 255 if (image_array[y, x] / 255.0) >= threshold else 0
-
将处理后的数组转换为图像:
dithered_image = Image.fromarray(image_array.astype(np.uint8))
-
显示和保存二值化后的图像:
dithered_image.show() dithered_image.save('dithered_example.jpg')
-
显示原始图像和二值化后的图像:
plt.subplot(1, 2, 1) plt.title('Original Image') plt.imshow(image.convert("L"), cmap='gray') plt.axis('off') plt.subplot(1, 2, 2) plt.title('Dithered Image') plt.imshow(dithered_image, cmap='gray') plt.axis('off') plt.show()
优缺点
优点:
- 高效:有序抖动算法的计算效率高,适合实时应用。
- 简单易实现:算法逻辑简单,易于实现和理解。
- 可控性好:通过选择不同的抖动矩阵,可以控制抖动效果。
缺点:
- 视觉伪影:可能会产生明显的图案或伪影,尤其是在大面积平坦区域。
- 对高频细节敏感:可能无法很好地保留图像中的高频细节。
有序抖动算法在图像抖动和半色调处理中非常有用,特别适用于需要快速处理和生成二值图像的应用场景,如打印和显示设备。通过选择合适的抖动矩阵,可以在不同应用场景中获得理想的抖动效果。
3.Riemersma 抖动算法详解
Riemersma 抖动算法是一种用于图像抖动的技术,通过遍历图像并根据给定的阈值矩阵进行量化。该算法采用一种类似于有序抖动的方法,但它使用更复杂的路径来遍历像素,从而减少图案和伪影。
Riemersma 抖动算法的核心思想是通过使用希尔伯特曲线(Hilbert curve)或其他空间填充曲线来遍历图像像素。这种遍历方式可以保持像素的空间局部性,从而在抖动过程中产生更自然的视觉效果。
算法步骤
- 生成希尔伯特曲线:生成图像尺寸的希尔伯特曲线索引。
- 生成抖动矩阵:创建一个预定义的抖动矩阵。
- 遍历图像:按照希尔伯特曲线的顺序逐像素处理图像,根据抖动矩阵对每个像素进行量化。
- 更新像素值:将量化后的像素值更新到输出图像中。
公式
Python 实现
以下是Riemersma抖动算法的Python实现代码:
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
def hilbert_curve(n):
"""生成n阶希尔伯特曲线的索引"""
if n == 1:
return np.array([[0, 1], [3, 2]])
t = hilbert_curve(n // 2)
size = t.size
t = np.rot90(t, 2)
t1 = t
t2 = t + size
t3 = t + 2 * size
t4 = t + 3 * size
t1 = np.rot90(t1, 3)
t3 = np.rot90(t3, 1)
return np.block([[t1, t2], [t4, t3]])
def generate_bayer_matrix(n):
if n == 2:
return np.array([
[0, 2],
[3, 1]
])
else:
smaller_matrix = generate_bayer_matrix(n // 2)
return np.block([
[4 * smaller_matrix + 0, 4 * smaller_matrix + 2],
[4 * smaller_matrix + 3, 4 * smaller_matrix + 1]
])
def riemersma_dithering(image, bayer_matrix, hilbert_indices):
"""
Riemersma 抖动算法实现
参数:
image (PIL.Image): 灰度图像
bayer_matrix (numpy.ndarray): 抖动矩阵
hilbert_indices (numpy.ndarray): 希尔伯特曲线索引
返回:
PIL.Image: 二值化后的图像
"""
# 将图像转换为灰度图像
grayscale_image = image.convert("L")
image_array = np.array(grayscale_image, dtype=float)
# 获取图像的行和列
rows, cols = image_array.shape
n = bayer_matrix.shape[0]
# 遍历图像
for index in hilbert_indices.flatten():
if index < rows:
y, x = divmod(index, cols)
threshold = (bayer_matrix[y % n, x % n] + 0.5) / (n * n)
image_array[y, x] = 255 if (image_array[y, x] / 255.0) >= threshold else 0
# 将处理后的数组转换为图像
dithered_image = Image.fromarray(image_array.astype(np.uint8))
return dithered_image
# 示例用法
if __name__ == "__main__":
image = Image.open('example.jpg') # 打开原始图像
bayer_matrix = generate_bayer_matrix(4) # 生成 4x4 Bayer 矩阵
hilbert_indices = hilbert_curve(max(image.size)) # 生成希尔伯特曲线索引
dithered_image = riemersma_dithering(image, bayer_matrix, hilbert_indices) # 调用Riemersma抖动算法
dithered_image.show() # 显示二值化后的图像
dithered_image.save('riemersma_dithered_example.jpg') # 保存二值化后的图像
# 显示原始图像和二值化后的图像
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.title('Original Image')
plt.imshow(image.convert("L"), cmap='gray')
plt.axis('off')
plt.subplot(1, 2, 2)
plt.title('Dithered Image')
plt.imshow(dithered_image, cmap='gray')
plt.axis('off')
plt.show()
详细解释
-
生成希尔伯特曲线索引:
def hilbert_curve(n): if n == 1: return np.array([[0, 1], [3, 2]]) t = hilbert_curve(n // 2) size = t.size t = np.rot90(t, 2) t1 = t t2 = t + size t3 = t + 2 * size t4 = t + 3 * size t1 = np.rot90(t1, 3) t3 = np.rot90(t3, 1) return np.block([[t1, t2], [t4, t3]])
-
生成Bayer矩阵:
def generate_bayer_matrix(n): if n == 2: return np.array([ [0, 2], [3, 1] ]) else: smaller_matrix = generate_bayer_matrix(n // 2) return np.block([ [4 * smaller_matrix + 0, 4 * smaller_matrix + 2], [4 * smaller_matrix + 3, 4 * smaller_matrix + 1] ])
-
读取图像并转换为灰度图像:
image = Image.open('example.jpg') grayscale_image = image.convert("L")
-
将灰度图像转换为NumPy数组:
image_array = np.array(grayscale_image, dtype=float)
-
遍历图像并根据抖动矩阵和希尔伯特曲线索引进行量化:
rows, cols = image_array.shape n = bayer_matrix.shape[0] for index in hilbert_indices.flatten(): y, x = divmod(index, cols) threshold = (bayer_matrix[y % n, x % n] + 0.5) / (n * n) image_array[y, x] = 255 if (image_array[y, x] / 255.0) >= threshold else 0
-
将处理后的数组转换为图像:
dithered_image = Image.fromarray(image_array.astype(np.uint8))
-
显示和保存二值化后的图像:
dithered_image.show() dithered_image.save('riemersma_dithered_example.jpg')
-
显示原始图像和二值化后的图像:
plt.subplot(1, 2, 1) plt.title('Original Image') plt.imshow(image.convert("L"), cmap='gray') plt.axis('off') plt.subplot(1, 2, 2) plt.title('Dithered Image') plt.imshow(dithered_image, cmap='gray') plt.axis('off') plt.show()
优缺点
优点:
- 减少图案和伪影:使用希尔伯特曲线遍历像素,能更好地保持图像细节和减少伪影。
- 高效:计算效率高,适合实时应用。
缺点:
- 实现复杂:相比其他抖动算法,Riemersma抖动算法实现较为复杂。
- 依赖抖动矩阵:效果受抖动矩阵的选择影响较大。
Riemersma 抖动算法通过引入希尔伯特曲线来改进传统的有序抖动算法,使其在保留图像细节和减少视觉伪影方面表现更佳。