本文参考知乎博客:图像处理之 Dithering(https://zhuanlan.zhihu.com/p/110104674)
图像抖动(dithering)常用于颜色量化(color quantization)的后处理,即去除颜色量化产生的一些视觉上不合理的“色带”。如下所示:左侧是原图,右侧是颜色量化的结果,可以看到猫的脖子以及头部有很多不合理的色带,颜色抖动的目标就是去除这些色带,使之更加平滑。
 
 图像抖动最经典的技术为误差扩散方法,可以参考博客:Floyd-Steinberg扩散抖动算法。
 这里介绍另一种简单且好玩的抖动算法,随机抖动。
 
假设现在给定如上所示的图像,不失一般性,将颜色归一化到[0,1]范围,我们给定8种颜色的调色板正好对应RGB cube的8个顶点),分别为: ( 0 , 0 , 0 ) , ( 1 , 0 , 0 ) , ( 0 , 1 , 0 ) , ( 0 , 0 , 1 ) , ( 0 , 1 , 1 ) , ( 1 , 1 , 0 ) , ( 1 , 0 , 1 ) , ( 1 , 1 , 1 ) (0,0,0),(1,0,0),(0,1,0),(0,0,1),(0,1,1),(1,1,0),(1,0,1),(1,1,1) (0,0,0),(1,0,0),(0,1,0),(0,0,1),(0,1,1),(1,1,0),(1,0,1),(1,1,1), 也就是说8个颜色,每种颜色的三通道取值要么是0,要么是1。
1. 固定阈值抖动
给定固定值 
    
     
      
       
        x
       
       
        =
       
       
        0.5
       
      
      
       x = 0.5
      
     
    x=0.5, 对于每个像素点 
    
     
      
       
        
         I
        
        
         p
        
       
      
      
       I_p
      
     
    Ip, 分别检查它的三个通道,并重新赋值:
 
     
      
       
        
         
          I
         
         
          p
         
        
        
         (
        
        
         r
        
        
         /
        
        
         g
        
        
         /
        
        
         b
        
        
         )
        
        
         =
        
        
         
          {
         
         
          
           
            
             
              
               0
              
              
                   
              
              
               
                I
               
               
                p
               
              
              
               (
              
              
               r
              
              
               /
              
              
               g
              
              
               /
              
              
               b
              
              
               )
              
              
               <
              
              
               x
              
             
            
           
          
          
           
            
             
              
               1
              
              
                   
              
              
               
                I
               
               
                p
               
              
              
               (
              
              
               r
              
              
               /
              
              
               g
              
              
               /
              
              
               b
              
              
               )
              
              
               ≥
              
              
               x
              
             
            
           
          
         
        
       
       
         I_p(r/g/b)=\left\{ \begin{aligned} 0\ \ \ \ I_p(r/g/b) \lt x\\ 1\ \ \ \ I_p(r/g/b) \geq x \end{aligned} \right. 
       
      
     Ip(r/g/b)={0    Ip(r/g/b)<x1    Ip(r/g/b)≥x
固定阈值抖动代码:
def dithering_v1(input_img, save_path):
	quant_img = input_img.copy()
	quant_img[input_img > 0.5] = 1
	quant_img[input_img < 0.5] = 0
	cv2.imwrite(save_path, quant_img*255)
 
抖动效果如下所示,可以看到,这里原始细节信息损失很严重。
 
2. 独立随机抖动
对于每个像素点 
    
     
      
       
        
         I
        
        
         p
        
       
      
      
       I_p
      
     
    Ip, 生成对应的随机数值 
    
     
      
       
        x
       
       
        =
       
       
        r
       
       
        a
       
       
        n
       
       
        d
       
       
        (
       
       
        )
       
      
      
       x = rand()
      
     
    x=rand(), 注意这里每个像素生成一个独立的随机
    
     
      
       
        x
       
      
      
       x
      
     
    x. 分别检查它的三个通道,并重新赋值:
 
     
      
       
        
         
          I
         
         
          p
         
        
        
         (
        
        
         r
        
        
         /
        
        
         g
        
        
         /
        
        
         b
        
        
         )
        
        
         =
        
        
         
          {
         
         
          
           
            
             
              
               0
              
              
                   
              
              
               
                I
               
               
                p
               
              
              
               (
              
              
               r
              
              
               /
              
              
               g
              
              
               /
              
              
               b
              
              
               )
              
              
               <
              
              
               x
              
             
            
           
          
          
           
            
             
              
               1
              
              
                   
              
              
               
                I
               
               
                p
               
              
              
               (
              
              
               r
              
              
               /
              
              
               g
              
              
               /
              
              
               b
              
              
               )
              
              
               ≥
              
              
               x
              
             
            
           
          
         
        
       
       
         I_p(r/g/b)=\left\{ \begin{aligned} 0\ \ \ \ I_p(r/g/b) \lt x\\ 1\ \ \ \ I_p(r/g/b) \geq x \end{aligned} \right. 
       
      
     Ip(r/g/b)={0    Ip(r/g/b)<x1    Ip(r/g/b)≥x
随机抖动代码1:
def dithering_v2(input_img, save_path):
	shape = input_img.shape
	input_img = input_img.flatten()
	quant_img = input_img.copy()
	for i in range(input_img.shape[0]):
		x = np.random.rand()
		if quant_img[i] > x: quant_img[i] = 1
		else: quant_img[i] = 0
	cv2.imwrite(save_path,quant_img.reshape(shape)*255)
 
抖动效果如下所示,可以看到,效果比固定阈值抖动好很多,但有很多噪点。
 
3. 多次独立随机抖动
直接上代码,这里对每个像素生成20个随即数 x x x,生成20个颜色值并求平均。
def dithering_v3(input_img, save_path):
	shape = input_img.shape
	input_img = input_img.flatten()
	quant_img = input_img.copy()
	for i in range(input_img.shape[0]):
		color = 0
		n = 20
		for k in range(n):
			if quant_img[i] > np.random.rand(): color += 1
		quant_img[i] = color / n
	cv2.imwrite(save_path, quant_img.reshape(shape)*255)
 
结果如下所示,看起来非常接近原始图像,这是为什么呢?
 
注意到这里每个像素点 
    
     
      
       
        
         I
        
        
         p
        
       
      
      
       I_p
      
     
    Ip 的颜色是这样计算的:
 令: 
    
     
      
       
        c
       
       
        =
       
       
        0
       
      
      
       c = 0
      
     
    c=0
 第1次: 
    
     
      
       
        x
       
       
        1
       
       
        =
       
       
        r
       
       
        a
       
       
        n
       
       
        d
       
       
        (
       
       
        )
       
       
        ,
       
       
        c
       
       
        +
       
       
        =
       
       
        
         I
        
        
         p
        
       
       
        >
       
       
        x
       
       
        1
       
      
      
       x1 = rand(), c += I_p > x1
      
     
    x1=rand(),c+=Ip>x1
 第2次: 
    
     
      
       
        x
       
       
        2
       
       
        =
       
       
        r
       
       
        a
       
       
        n
       
       
        d
       
       
        (
       
       
        )
       
       
        ,
       
       
        c
       
       
        +
       
       
        =
       
       
        
         I
        
        
         p
        
       
       
        >
       
       
        x
       
       
        2
       
      
      
       x2 = rand(), c += I_p > x2
      
     
    x2=rand(),c+=Ip>x2
 第3次: 
    
     
      
       
        x
       
       
        3
       
       
        =
       
       
        r
       
       
        a
       
       
        n
       
       
        d
       
       
        (
       
       
        )
       
       
        ,
       
       
        c
       
       
        +
       
       
        =
       
       
        
         I
        
        
         p
        
       
       
        >
       
       
        x
       
       
        3
       
      
      
       x3 = rand(), c += I_p > x3
      
     
    x3=rand(),c+=Ip>x3
 …
 第n次: 
    
     
      
       
        x
       
       
        n
       
       
        =
       
       
        r
       
       
        a
       
       
        n
       
       
        d
       
       
        (
       
       
        )
       
       
        ,
       
       
        c
       
       
        +
       
       
        =
       
       
        
         I
        
        
         p
        
       
       
        >
       
       
        x
       
       
        n
       
      
      
       xn = rand(), c += I_p > xn
      
     
    xn=rand(),c+=Ip>xn
 最终, 
    
     
      
       
        
         I
        
        
         p
        
       
       
        =
       
       
        c
       
       
        /
       
       
        n
       
      
      
       I_p = c / n
      
     
    Ip=c/n
任意一次, I p > x i , x i = r a n d ( ) I_p > xi, xi = rand() Ip>xi,xi=rand(), 可以发现 x i = r a n d ( ) < I p xi = rand() < I_p xi=rand()<Ip 的概率就是 I p I_p Ip, 比如 I p = 0.8 I_p = 0.8 Ip=0.8, 那么在[0,1]范围生成随即数 x < 0.8 x < 0.8 x<0.8的概率就是0.8,为此, I p = 1 I_p = 1 Ip=1的概率就是 0.8. 通过多次随机采样,最终的期望值就等于 I p I_p Ip本身的像素值。
因此,这个算法其实并不实用,因为颜色值已经不再是调色板中的颜色值,而是接近像素值本身。。。










