我猜想我需要:
- 将文件分成它的颜色通道。
- 为该通道生成单色半色调图像。
- 将生成的半色调图像按照通道数目 * 角度数量 倾斜。
我曾经经营过一个丝网印刷工作室(规模相当小),虽然我实际上从未进行过色彩分离印刷,但对原理有一定的了解。这是我的处理方法:
现在你就得到了你的分色图像。正如你所提到的,旋转步骤可以减少点对齐问题(否则会搞乱一切),而诸如Moiré pattern effects之类的问题也会被合理地减少。
使用PIL编写代码非常容易。
更新2:
我写了一些快速的代码,可以为您完成这项任务,它还包括一个 GCR
函数(下面详细说明):
import Image, ImageDraw, ImageStat
def gcr(im, percentage):
'''basic "Gray Component Replacement" function. Returns a CMYK image with
percentage gray component removed from the CMY channels and put in the
K channel, ie. for percentage=100, (41, 100, 255, 0) >> (0, 59, 214, 41)'''
cmyk_im = im.convert('CMYK')
if not percentage:
return cmyk_im
cmyk_im = cmyk_im.split()
cmyk = []
for i in xrange(4):
cmyk.append(cmyk_im[i].load())
for x in xrange(im.size[0]):
for y in xrange(im.size[1]):
gray = min(cmyk[0][x,y], cmyk[1][x,y], cmyk[2][x,y]) * percentage / 100
for i in xrange(3):
cmyk[i][x,y] = cmyk[i][x,y] - gray
cmyk[3][x,y] = gray
return Image.merge('CMYK', cmyk_im)
def halftone(im, cmyk, sample, scale):
'''Returns list of half-tone images for cmyk image. sample (pixels),
determines the sample box size from the original image. The maximum
output dot diameter is given by sample * scale (which is also the number
of possible dot sizes). So sample=1 will presevere the original image
resolution, but scale must be >1 to allow variation in dot size.'''
cmyk = cmyk.split()
dots = []
angle = 0
for channel in cmyk:
channel = channel.rotate(angle, expand=1)
size = channel.size[0]*scale, channel.size[1]*scale
half_tone = Image.new('L', size)
draw = ImageDraw.Draw(half_tone)
for x in xrange(0, channel.size[0], sample):
for y in xrange(0, channel.size[1], sample):
box = channel.crop((x, y, x + sample, y + sample))
stat = ImageStat.Stat(box)
diameter = (stat.mean[0] / 255)**0.5
edge = 0.5*(1-diameter)
x_pos, y_pos = (x+edge)*scale, (y+edge)*scale
box_edge = sample*diameter*scale
draw.ellipse((x_pos, y_pos, x_pos + box_edge, y_pos + box_edge), fill=255)
half_tone = half_tone.rotate(-angle, expand=1)
width_half, height_half = half_tone.size
xx=(width_half-im.size[0]*scale) / 2
yy=(height_half-im.size[1]*scale) / 2
half_tone = half_tone.crop((xx, yy, xx + im.size[0]*scale, yy + im.size[1]*scale))
dots.append(half_tone)
angle += 15
return dots
im = Image.open("1_tree.jpg")
cmyk = gcr(im, 0)
dots = halftone(im, cmyk, 10, 1)
im.show()
new = Image.merge('CMYK', dots)
new.show()
这将会将这个:
转变成这个(眯起眼睛并离开监视器):
请注意,图像采样可以是逐像素的(因此在最终图像中保留原始图像的分辨率)。通过设置sample = 1
来实现这一点,在这种情况下,您需要将scale
设置为一个更大的数字,以便有多个可能的点大小。这也会导致更大的输出图像大小(原始图像大小* scale ** 2,所以要小心!)。
默认情况下,从RGB
转换为CMYK
时,K
通道(黑色通道)为空。是否需要K
通道取决于您的印刷过程。您可能希望这样做的原因有很多:获得比 CMY
重叠更好的黑色,节省墨水,改善干燥时间,减少墨水渗出等。无论如何,我还编写了一个小的GCR(灰色组件替换)函数GCR
,因此您可以设置要替换的CMY
重叠的K
通道的百分比(我在代码注释中进一步解释了这一点)。
下面是几个示例来说明。使用sample = 1
和scale = 8
对图像中的letter F
进行处理,因此分辨率相当高。
4个CMYK
通道,其中percentage = 0
,因此为空的K
通道:
合并成:
CMYK
通道,其中percentage = 100
,因此使用了K
通道。您可以看到青色通道完全被压制,洋红色和黄色通道在图像底部的黑色带中使用了更少的墨水:
我的解决方案也使用了PIL,但依赖于内部支持的抖动方法(Floyd-Steinberg)。然而,这样会产生伪像,因此我正在考虑重写其C代码。
from PIL import Image
im = Image.open('tree.jpg') # open RGB image
cmyk= im.convert('CMYK').split() # RGB contone RGB to CMYK contone
c = cmyk[0].convert('1').convert('L') # and then halftone ('1') each plane
m = cmyk[1].convert('1').convert('L') # ...and back to ('L') mode
y = cmyk[2].convert('1').convert('L')
k = cmyk[3].convert('1').convert('L')
new_cmyk = Image.merge('CMYK',[c,m,y,k]) # put together all 4 planes
new_cmyk.save('tree-cmyk.jpg') # and save to file
使用有序(Bayer)Dither和D4模式,我们可以修改@fraxel的代码,最终得到一个输出抖动图像,其高度和宽度是输入彩色图像的4倍,如下所示
import matplotlib.pylab as plt
def ordered_dither(im, D4):
im = (15 * (im / im.max())).astype(np.uint8)
h, w = im.shape
im_out = np.zeros((4 * h, 4 * w), dtype = np.uint8)
x, y = 0, 0
for i in range(h):
for j in range(w):
im_out[x:x + 4, y:y + 4] = 255 * (D4 < im[i, j])
y = (y + 4) % (4 * w)
x = (x + 4) % (4 * h)
return im_out
def color_halftoning_Bayer(cmyk, angles, D4):
out_channels = []
for i in range(4):
out_channel = Image.fromarray(
ordered_dither(np.asarray(cmyk[i].rotate(angles[i],expand=1)), D4)
).rotate(-angles[i], expand=1)
x = (out_channel.size[0] - cmyk[i].size[0]*4) // 2
y = (out_channel.size[1] - cmyk[i].size[1]*4) // 2
out_channel = out_channel.crop((x, y, x + cmyk[i].size[0]*4, y + cmyk[i].size[1]*4))
out_channels.append(out_channel)
return Image.merge('CMYK',out_channels)
image = Image.open('images/tree.jpg')
cmyk = gcr(image,100).split()
D4 = np.array([[ 0, 8, 2, 10],
[12, 4, 14, 6],
[ 3, 11, 1, 9],
[15, 7, 13, 5]], dtype=np.uint8)
out = np.asarray(color_halftoning_Bayer(cmyk, np.linspace(15, 60, 4), D4).convert('RGB'))
plt.figure(figsize=(20,20))
plt.imshow(out)
plt.show()
它生成以下抖动输出: