灰度直方图模拟生成水墨风格图片

获取水墨风格灰度分布直方图

  • 在网络搜索若干水墨风格图片

img

  • 绘制并观察灰度直方图特点

img

观察到:灰度级位小于240部分比较平缓、分布比较均匀;在灰度级大于240出激增,出现峰值。

  • 将这四个水墨风格图像灰度直方图进行拟合

img

直方图规定化

  • 待处理图片

img

  • 计算累积分布函数(CDF)
1
2
3
4
5
# 计算源图像的直方图和累积分布函数
source_hist = cv.calcHist([source_img], [0], None, [256], [0, 256])
source_cdf = source_hist.cumsum() / source_img.size
# 计算目标直方图的累积分布函数
style_cdf = style_hist.cumsum() / graylist[0].size
  • 创建映射表

    • 对每个灰度级,找到源图像CDF和目标图像CDF之间差值最小的灰度级,构建映射表

      1
      2
      3
      4
      5
      6
      7
      8
      # 创建映射表
      mapping = np.zeros(256, dtype=np.uint8)
      for i in range(256):
      diff = np.abs(source_cdf[i] - style_cdf)
      mapping[i] = np.argmin(diff)

      # 应用映射表进行直方图规定化
      applyed_img = cv.LUT(source_img, mapping)
    • 使用映射表对源图像进行直方图规定化,得到处理后的图像 applyed_img

      img

验证效果

  • 原图片灰度分布直方图

    img

  • 预期实现的灰度分布图

    img

  • 实际处理后图片灰度分布直方图

    img

源代码

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import matplotlib.pyplot as plt
import cv2 as cv
import numpy as np
import os

def show(img):
if img.ndim == 2:
plt.imshow(img, cmap='gray', vmin=0, vmax=255)
else:
plt.imshow(cv.cvtColor(img, cv.COLOR_BGR2RGB))
plt.show()

# 读入水墨风格灰度图像
piclist = ["calligraphy/fig{}.jpg".format(i+1) for i in range(4)]
graylist = []

for i in piclist:
if not os.path.exists(i):
print("图像不存在")
continue
img = cv.imread(i, 0) # 读取图片并转化为灰度图像
graylist.append(img)

# 计算灰度级数组
hists = [cv.calcHist([img], [0], None, [256], [0, 256]) for img in graylist]
# [plt.plot(hist) for hist in hists]
# plt.show()

# 计算平均直方图
sum_hist = np.sum(hists, axis=0)
avg_hist = sum_hist / len(graylist)
style_hist = avg_hist

# 读取待处理图像并 转换 为灰度图像
source_img_path = 'apply/fig3.jpg'
source_img = cv.imread(source_img_path, 0)

# 计算源图像的直方图
source_hist = cv.calcHist([source_img], [0], None, [256], [0, 256])

# 计算源图像的累积分布函数(CDF)
source_cdf = source_hist.cumsum() / source_img.size
# 计算目标直方图的累积分布函数(CDF)
style_cdf = style_hist.cumsum() / graylist[0].size

# 创建映射表
mapping = np.zeros(256, dtype=np.uint8)
for i in range(256):
diff = np.abs(source_cdf[i] - style_cdf)
mapping[i] = np.argmin(diff)

# 应用映射表进行直方图规定化
applyed_img = cv.LUT(source_img, mapping)
show(applyed_img)