创意
这个方法其实很久以前我就了解了——用最小值和颜色减淡。我以前用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
| 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
| 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
| 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
|
函数的第一个参数是图像,第二个是领域半径,第三个是容差,最后的是阈值。
这样处理完的线稿图像算是比较完美的了~
