纯Python综合图像处理小工具(4)自定义像素级处理(剪纸滤镜)

<背景>  

上一节介绍了python PIL库自带的10种滤镜处理,现成的库函数虽然用起来方便,可是对于图像处理的各类实际需求,还须要开发者开发自定义的滤镜算法。本文将给你们介绍如何使用PIL对图像进行自定义的像素级操做python

<效果>  

本文以剪纸风格图像处理做为例子:(算法借鉴了残阳似血的博客http://qinxuye.me/,特此鸣谢。)算法

原图:数组

 

处理后: app

 

<怎么作>

1.首先将处理参数预先设定好。设定阈值threshold,该阈值会用来区分做为目标颜色的前景色和将要被去除掉的的背景色的分界线。同时设置处理后前景色和后景色的颜色,用以呈现最终的分割效果。函数

    threshold = 150
    bg_color = (255, 255, 255, 0)
    fg_color = (255, 0, 0, 255)
    
     if len(sys.argv) >= 2:
        path  = sys.argv[1]
     if len(sys.argv) == 3:
        threshold = int(sys.argv[2])
     if len(sys.argv) == 5:
        bg_color = tuple(sys.argv[3])
        fg_color = tuple(sys.argv[4])

在这一步中,若是阈值threshold设定不一样数值,图片会呈现不一样的二值分界效果,以下图: spa

 

若是阈值太小,则图像中的斑马,会有部分颜色较浅的前景色,即灰色斑纹被误判成背景色而被滤除掉,从而形成前景图像轮廓的残缺。code

若是阈值过大,则与上述状况相反,会有背景色被误判成前景色,形成斑纹的边缘膨胀,视觉上看也相似膨胀的效果(虽然原理彻底不一样)。blog

因此,选择一个合适的阈值,对图像的分割效果相当重要。而这一点,又是随着图像不一样而变化的,阈值要适应图像内容。图片

对于这个问题,能够尝试使用动态阈值,结合图像统计来实现自匹配,本文未涉及,有兴趣的童鞋能够自行尝试。开发

 

2.将图片转换成能够作像素操做的二值像素集合,而后使用设定好的阈值threshold进行前景后景分割:


def Img2bin_arr(img, threshold):
     '''
    @将位图流转化为二维二值数组
    @param img: instance of Image
    @param threshold: 大小范围[0, 255]
    
'''
    threshold = max(0, threshold)
    threshold = min(255, threshold)
    
     if img.mode !=  ' L ':
        img = img.convert( ' L ')
        
    width, height = img.size
    pix = img.load()
    
    get_val =  lambda p: 255  if p >= threshold  else 0
        
     return [[get_val(pix[w, h])  for w  in xrange(width)]  for h  in xrange(height)]


3.将分割好的像素使用预先设定的颜色上色,而后将像素集合从新封装成图片格式,而后返回这个图片,用于保存或显示。

def bin_arr2Img(matrix, bg_color, fg_color):
     '''
    @将二维二值数组转化为位图流
    @param img: instance of Image
    @param bg_color: 背景色,元组类型,格式:(L)(灰度),(R, G, B),或者(R, G, B, A)
    @param fg_color: 前景色
    
'''
     def ensure_color(color):
         if len(color) == 1:
             return (color, color, color, 255)
         elif len(color) == 3:
            color = list(color)
            color.append(255)
             return tuple(color)
         elif len(color) == 4:
             return color
         else:
             raise ValueError,  ' len(color) cannot be %d ' % len(color)
        
    bg_color = ensure_color(bg_color)
    fg_color = ensure_color(fg_color)
    
    height, width = len(matrix), len(matrix[0])
    dst_img = Image.new( " RGBA ", (width, height))
    dst_pix = dst_img.load()
    
     for w  in xrange(width):
         for h  in xrange(height):
             if matrix[h][w] < 128:
                dst_pix[w, h] = fg_color
             else:
                dst_pix[w, h] = bg_color
                
     return dst_img

总结:使用python进行像素级图像处理,同其余平台的像素处理相似,整个过程很是清晰,大致上都是三步,拆包-处理-封包。鉴于python的处理速度实在是不敢恭维,因此它是一个很好的算法效果验证平台。 

<源码分享>  

完整代码分享以下:

# start
#
 -*- coding: cp936 -*-
import Image

img = Image.open( " 1.jpg ")

def Img2bin_arr(img, threshold):
     '''
    @将位图流转化为二维二值数组
    @param img: instance of Image
    @param threshold: 大小范围[0, 255]
    
'''
    threshold = max(0, threshold)
    threshold = min(255, threshold)
    
     if img.mode !=  ' L ':
        img = img.convert( ' L ')
        
    width, height = img.size
    pix = img.load()
    
    get_val =  lambda p: 255  if p >= threshold  else 0
        
     return [[get_val(pix[w, h])  for w  in xrange(width)]  for h  in xrange(height)]

def bin_arr2Img(matrix, bg_color, fg_color):
     '''
    @将二维二值数组转化为位图流
    @param img: instance of Image
    @param bg_color: 背景色,元组类型,格式:(L)(灰度),(R, G, B),或者(R, G, B, A)
    @param fg_color: 前景色
    
'''
     def ensure_color(color):
         if len(color) == 1:
             return (color, color, color, 255)
         elif len(color) == 3:
            color = list(color)
            color.append(255)
             return tuple(color)
         elif len(color) == 4:
             return color
         else:
             raise ValueError,  ' len(color) cannot be %d ' % len(color)
        
    bg_color = ensure_color(bg_color)
    fg_color = ensure_color(fg_color)
    
    height, width = len(matrix), len(matrix[0])
    dst_img = Image.new( " RGBA ", (width, height))
    dst_pix = dst_img.load()
    
     for w  in xrange(width):
         for h  in xrange(height):
             if matrix[h][w] < 128:
                dst_pix[w, h] = fg_color
             else:
                dst_pix[w, h] = bg_color
                
     return dst_img

def paper_cut(img, threshold, bg_color, fg_color):
     '''
    @效果:剪纸
    @param img: instance of Image
    @param threshold: 大小范围[0, 255]
    @param bg_color: 背景色,元组类型,格式:(L)(灰度),(R, G, B),或者(R, G, B, A)
    @param fg_color: 前景色
    @return: instance of Image
    
'''
    matrix = Img2bin_arr(img, threshold)  #  位图转化为二维二值数组
     return bin_arr2Img(matrix, bg_color, fg_color)  #  二维二值数组转化为位图

if  __name__ ==  " __main__ ":
     import sys, os, time

    path = os.path.dirname( __file__) + os.sep.join([ ''' 1.jpg '])
    threshold = 150
    bg_color = (255, 255, 255, 0)
    fg_color = (255, 0, 0, 255)
    
     if len(sys.argv) >= 2:
        path  = sys.argv[1]
     if len(sys.argv) == 3:
        threshold = int(sys.argv[2])
     if len(sys.argv) == 5:
        bg_color = tuple(sys.argv[3])
        fg_color = tuple(sys.argv[4])

    start = time.time()
    
    img = Image.open(path)
    img = paper_cut(img, threshold, bg_color, fg_color)
    img.save(os.path.splitext(path)[0]+ ' .papercut_ '+str(threshold)+ ' .png '' PNG ')

    end = time.time()
     print  ' It all spends %f seconds time ' % (end-start) 

# end
相关文章
相关标签/搜索