Contents
  1. 1. 创意
  2. 2. 实现
  3. 3. 改进

创意

这个方法其实很久以前我就了解了——用最小值和颜色减淡。我以前用photoshop的时候都是采用这个方法的。不过我过去一直不知道它的原理是什么,知道最近学习了数字图像处理。最小值十分适合用来侵蚀线条,而颜色减淡则可以很好的把线条凸显出来。具体的操作如下:

  • 去色,变为灰度图像,记为图1
  • 对图1反色,记为图2
  • 对图2应用最小值滤波器,得到图3
  • 图1作为基层,图3作为混合层,与图3做颜色减淡,得到最终图像

其中,颜色减淡的计算公式是:

  • 基色=0时,结果色=0;
  • 基色=255时,结果色=255;
  • 结果色 = MAX(基色 / (255 - 混合色), 255)

实现

下面给出MATLAB的实现:

1
2
3
4
5
6
7
8
9
% roundMask.m
function [ M ] = roundMask( radius )
r = floor(radius);
axis = -r: r;
[x, y] = meshgrid(axis, axis);
distance = x .^ 2 + y .^ 2;
M = zeros(2 * r + 1);
M(distance <= radius ^ 2) = 1;
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
% sketch.m
function [ PIM ] = sketch( IM, radius )
r = floor(radius);
IM = im2double(rgb2gray(IM));
RIM = 1 - IM;
templateM = roundMask(radius);
[width, height] = size(IM);
PIM = zeros(width, height);
for y = 1: height
for x = 1: width
sx = max(1, x - r);
ex = min(width, x + r);
sy = max(1, y - r);
ey = min(height, y + r);
domain = RIM(sx:ex, sy:ey);
maskM = templateM(sx + r - x + 1:ex + r - x + 1,...
sy + r - y + 1:ey + r - y + 1);
PIM(x, y) = min(domain(maskM ~= 0));
end
end
PIM = min(IM ./ (1 - PIM), 1);
PIM(IM == 0) = 0;
end

roundMask.m是得到一个圆形领域模版,sketch.m才是提取线稿的部分。它接受图片矩阵和一个领域半径,返回经过处理的double型矩阵。这里不用ordfilt2等函数是因为他们都不能很好的处理图像边缘,会有黑边。所以我就自己实现了最小值滤波器。

处理的效果如下图所示:

原图与经过处理的图像

改进

但是仔细观察会发现黑色线条的周围会有灰色的阴影,像下图这样。

注意箭头所指的位置

之后我想了很多办法去除他们,受到一个叫选择性模糊的算法的启发,我决定使用如下方法。对于一个领域内的像素,只选择与该像素的灰度接近的某些像素计算平均值;如果值大于某一阈值,则取某些像素的最大值作为结果;如果值小于某一阈值,则取某些像素的最小值作为结果。这样可以有效的保留边缘。下面是MATLAB实现。

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
% removeNoise.m
function [ PIM ] = removeNoise( IM, radius, distance, threshold )
r = floor(radius);
[width, height] = size(IM);
templateM = roundMask(radius);
PIM = zeros(width, height);
for y = 1: height
for x = 1: width
sx = max(1, x - r);
ex = min(width, x + r);
sy = max(1, y - r);
ey = min(height, y + r);
domain = IM(sx:ex, sy:ey);
maskM = templateM(sx + r - x + 1:ex + r - x + 1,...
sy + r - y + 1:ey + r - y + 1);
distances = abs(domain - IM(x, y));
validPt = domain((maskM ~= 0) & (distances < distance/255));
if mean(validPt) < threshold/255
PIM(x, y) = min(validPt);
else
PIM(x, y) = max(validPt);
end
end
end
end

函数的第一个参数是图像,第二个是领域半径,第三个是容差,最后的是阈值。

这样处理完的线稿图像算是比较完美的了~

最终的结果

Contents
  1. 1. 创意
  2. 2. 实现
  3. 3. 改进