暗通道去雾算法 python实现

深度神经网络教你如何图像去雾:

https://www.leiphone.com/news/201610/RC7IylYdZge7idm9.html

参考:http://blog.csdn.net/songhhll/article/details/12612681

  关于何博士的一些资料和论文,大家可以访问这里:http://research.microsoft.com/en-us/um/people/kahe/

     最开始接触何的这篇论文是在2011年,说实在的那个时候,只是随便浏览了下,看到里面的soft matting过程比较复杂,并且执行速度非常慢,就没有什么大的兴趣。最近又偶尔拾起,仔细研读,觉得论文的推理步骤特别清晰,讲解很到位。恰好适逢浏览到其另外一篇文章《Guided Image Filtering》 ,其中提到了可以用导向滤波来代替soft matting的过程,且速度很快,因此,我对去雾的兴趣算法又大大提高了。  

     本文主要上是对《Single Image Haze Removal Using Dark Channel Prior》的翻译、整理、及部分解释。如果您的英文水平好,建议看原文可能来的更爽些。

    一、论文思想的简单描述 

     首先看看暗通道先验是什么:

       在绝大多数非天空的局部区域里,某一些像素总会有至少一个颜色通道具有很低的值。换言之,该区域光强度的最小值是个很小的数。

  我们给暗通道一个数学定义,对于任意的输入图像J,其暗通道可以用下式表达:

                                    

      式中Jc表示彩色图像的每个通道 ,Ω(x)表示以像素X为中心的一个窗口。 

    式(5)的意义用代码表达也很简单,首先求出每个像素RGB分量中的最小值,存入一副和原始图像大小相同的灰度图中,然后再对这幅灰度图进行最小值滤波,滤波的半径由窗口大小决定,一般有WindowSize = 2 * Radius + 1;          

      暗通道先验的理论指出:

                                                                       

     实际生活中造成暗原色中低通道值主要有三个因素:a)汽车、建筑物和城市中玻璃窗户的阴影,或者是树叶、树与岩石等自然景观的投影;b)色彩鲜艳的物体或表面,在RGB的三个通道中有些通道的值很低(比如绿色的草地/树/植物,红色或黄色的花朵/叶子,或者蓝色的水面);c)颜色较暗的物体或者表面,例如灰暗色的树干和石头。总之,自然景物中到处都是阴影或者彩色,这些景物的图像的暗原色总是很灰暗的。


这个有完整代码,有导向滤波器(guided filter)代码:

http://blog.csdn.net/chongshangyunxiao321/article/details/50997111



python3版本:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from PIL import Image
from guidedfilter import *

def getDark(input_img, filter, frame):
  """get dark image from the input image"""
  size = input_img.size
  output = []

  for x in range(size[1]):
    temp = []
    for y in range(size[0]):
      temp.append(min(input_img.getpixel((y, x))))

    output.append(temp)

  output = filter2d(output, filter, frame)

  output_img = Image.new('L', size)

  for x in range(size[1]):
    for y in range(size[0]):
      output_img.putpixel((y, x), output[x][y])

  return output_img

def getLight(srcImage, darkImage, cut):
  """get atmospheric light from the picture"""
  size = darkImage.size
  light = []

  for x in range(size[0]):
    for y in range(size[1]):
      light.append(darkImage.getpixel((x, y)))

  light.sort()
  light.reverse()

  threshold = light[int(cut * len(light))]

  atmosphere = {}

  for x in range(size[0]):
    for y in range(size[1]):
      if darkImage.getpixel((x, y)) >= threshold:
        atmosphere.update({(x, y): sum(srcImage.getpixel((x, y))) / 3.0})

  pos = sorted(atmosphere.items(), key = lambda item: item[1], reverse = True)[0][0]

  return srcImage.getpixel(pos)

def getTransmission(input_img, light, omiga):
  """get transmission from the picture"""
  size = input_img.size
  output = []

  for x in range(size[1]):
    temp = []
    for y in range(size[0]):
      temp.append(min(input_img.getpixel((y, x))) / float(min(light)))

    output.append(temp)

  transmission = []

  for x in range(size[1]):
    temp = []
    for y in range(size[0]):
      temp.append(1 - omiga * minimizeFilter(output, (x, y), (10, 10)))

    transmission.append(temp)

  return transmission

def getRadiance(input_img, transmission, light, t0):
  """get radiance from the picture"""
  size = input_img.size
  output = Image.new('RGB', size)

  for x in range(size[1]):
    for y in range(size[0]):
      r, g, b = input_img.getpixel((y, x))

      r = int((r - light[0]) / float(max(t0, transmission[x][y])) + light[0])
      g = int((g - light[1]) / float(max(t0, transmission[x][y])) + light[1])
      b = int((b - light[2]) / float(max(t0, transmission[x][y])) + light[2])

      output.putpixel((y, x), (r, g, b))

  return output

def ensure(n):
  if n < 0:
    n = 0

  if n > 255:
    n = 255

  return int(n)

if __name__ == '__main__':
  image = Image.open('image/huoche.jpg')
  image = image.convert('RGB')

  dark = getDark(image, minimizeFilter, (10, 10))

  dark.save('3_dark.png')

  light = getLight(image, dark, 0.001)

  transmission = getTransmission(image, light, 0.9)

  tranImage = Image.new('L', image.size)
  grayImage = image.convert('L')

  for x in range(image.size[0]):
    for y in range(image.size[1]):
      tranImage.putpixel((x, y), int(transmission[y][x] * 255))

  guided = guidedFilter(grayImage, tranImage, 25, 0.001)

  guidedImage = Image.new('L', image.size)

  for x in range(image.size[0]):
    for y in range(image.size[1]):
      guidedImage.putpixel((x, y), ensure(guided[y][x]))
      guided[y][x] /= 255.0

  #guidedImage.show()
  guidedImage.save('3_guided.png')

  output = getRadiance(image, guided, light, 0.1)

  output.save('3_haze.png')


#!/usr/bin/env python
# -*- coding: utf-8 -*-

from PIL import Image
import numpy as np

def filter2d(input_img, filter, frame):
  """filter of the 2-dimension picture"""
  size = len(input_img), len(input_img[0])
  output = []

  for i in range(size[0]):
    temp = []
    for j in range(size[1]):
      temp.append(filter(input_img, (i, j), frame))

    output.append(temp)

  return output

def minimizeFilter(input_img, point, size):
  """minimize filter for the input image"""
  begin = np.int32((point[0] - size[0] / 2, point[0] + size[0] / 2 + 1))
  end = np.int32((point[1] - size[1] / 2, point[1] + size[1] / 2 + 1))

  l = []

  for i in range(*begin):
    for j in range(*end):
      if (i >= 0 and i < len(input_img)) and (j >= 0 and j < len(input_img[0])):
        l.append(input_img[i][j])

  return min(l)

def convertImageToMatrix(image):
  size = image.size
  out = []

  for x in range(size[1]):
    temp = []
    for y in range(size[0]):
      temp.append(image.getpixel((y, x)))

    out.append(temp)

  return out

def boxFilter(im, radius):
  """box filter for the image of the radius"""
  height, width = len(im), len(im[0])

  imDst = []
  imCum = []

  for x in range(height):
    imDst.append([0.0] * width)
    imCum.append([0.0] * width)

  #cumulative sum over Y axis
  for i in range(width):
    for j in range(height):
      if j == 0:
        imCum[j][i] = im[j][i]
      else:
        imCum[j][i] = im[j][i] + imCum[j - 1][i]

  #difference over Y axis
  for j in range(radius + 1):
    for i in range(width):
      imDst[j][i] = imCum[j + radius][i]

  for j in range(radius + 1, height - radius):
    for i in range(width):
      imDst[j][i] = imCum[j + radius][i] - imCum[j - radius - 1][i]

  for j in range(height - radius, height):
    for i in range(width):
      imDst[j][i] = imCum[height - 1][i] - imCum[j - radius - 1][i]

  #cumulative sum over X axis
  for j in range(height):
    for i in range(width):
      if i == 0:
        imCum[j][i] = imDst[j][i]
      else:
        imCum[j][i] = imDst[j][i] + imCum[j][i - 1]

  #difference over X axis
  for j in range(height):
    for i in range(radius + 1):
      imDst[j][i] = imCum[j][i + radius]

  for j in range(height):
    for i in range(radius + 1, width - radius):
      imDst[j][i] = imCum[j][i + radius] - imCum[j][i - radius - 1]

  for j in range(height):
    for i in range(width - radius, width):
      imDst[j][i] = imCum[j][width - 1] - imCum[j][i - radius - 1]

  return imDst

def dot(matrix1, matrix2, operation):
  """dot operation for the matrix1 and matrix2"""
  out = []
  size = len(matrix1), len(matrix1[0])

  for x in range(size[0]):
    temp = []
    for y in range(size[1]):
      temp.append(operation(matrix1[x][y], matrix2[x][y]))

    out.append(temp)

  return out

def guidedFilter(srcImage, guidedImage, radius, epsilon):
  """guided filter for the image
     src image must be gray image
     guided image must be gray image
  """

  size = srcImage.size
  src = convertImageToMatrix(srcImage)
  guided = convertImageToMatrix(guidedImage)

  one = []

  for x in range(size[1]):
    one.append([1.0] * size[0])

  n = boxFilter(one, radius)

  plus = lambda x, y: x + y
  minus = lambda x, y: x - y
  multiple = lambda x, y: x * y
  divide = lambda x, y: x / y

  meanI = dot(boxFilter(src, radius), n, divide)
  meanP = dot(boxFilter(guided, radius), n, divide)
  meanIP = dot(boxFilter(dot(src, guided, multiple), radius), n, divide)

  covIP = dot(meanIP, dot(meanI, meanP, multiple), minus)

  meanII = dot(boxFilter(dot(src, src, multiple), radius), n, divide)
  varI = dot(meanII, dot(meanI, meanI, multiple), minus)

  epsilonMatrix = []

  for x in range(size[1]):
    epsilonMatrix.append([epsilon] * size[0])

  a = dot(covIP, dot(varI, epsilonMatrix, plus), divide)
  b = dot(meanP, dot(a, meanI, multiple), minus)

  meanA = dot(boxFilter(a, radius), n, divide)
  meanB = dot(boxFilter(b, radius), n, divide)

  return dot(dot(meanA, src, multiple), meanB, plus)

if __name__ == '__main__':
  a=(-5,6)
  import numpy as np
  a=np.int32(a)
  for i in range(*a):
    print(i)



原文:http://blog.csdn.net/zmshy2128/article/details/53443227

这个代码有问题,下面一行报错了:

res = np.minimum(I, I[[0] + range(h - 1), :])

    何凯明博士的去雾文章和算法实现已经漫天飞了,我今天也就不啰里啰唆,直接给出自己python实现的完整版本,全部才60多行代码,简单易懂,并有简要注释,去雾效果也很不错。

    在这个python版本中,计算量最大的就是最小值滤波,纯python写的,慢,可以进一步使用C优化,其他部分都是使用numpy和opencv的现成东东,效率还行。

 

[python]  view plain  copy
  1. import cv2  
  2. import numpy as np  
  3.    
  4. def zmMinFilterGray(src, r=7):  
  5.     '''''最小值滤波,r是滤波器半径'''  
  6.     if r <= 0:  
  7.         return src  
  8.     h, w = src.shape[:2]  
  9.     I = src  
  10.     res = np.minimum(I  , I[[0]+range(h-1)  , :])  
  11.     res = np.minimum(res, I[range(1,h)+[h-1], :])  
  12.     I = res  
  13.     res = np.minimum(I  , I[:, [0]+range(w-1)])  
  14.     res = np.minimum(res, I[:, range(1,w)+[w-1]])  
  15.     return zmMinFilterGray(res, r-1)  
  16.    
  17. def guidedfilter(I, p, r, eps):  
  18.     '''''引导滤波,直接参考网上的matlab代码'''  
  19.     height, width = I.shape  
  20.     m_I = cv2.boxFilter(I, -1, (r,r))  
  21.     m_p = cv2.boxFilter(p, -1, (r,r))  
  22.     m_Ip = cv2.boxFilter(I*p, -1, (r,r))  
  23.     cov_Ip = m_Ip-m_I*m_p  
  24.    
  25.     m_II = cv2.boxFilter(I*I, -1, (r,r))  
  26.     var_I = m_II-m_I*m_I  
  27.    
  28.     a = cov_Ip/(var_I+eps)  
  29.     b = m_p-a*m_I  
  30.    
  31.     m_a = cv2.boxFilter(a, -1, (r,r))  
  32.     m_b = cv2.boxFilter(b, -1, (r,r))  
  33.     return m_a*I+m_b  
  34.    
  35. def getV1(m, r, eps, w, maxV1):  #输入rgb图像,值范围[0,1]  
  36.     '''''计算大气遮罩图像V1和光照值A, V1 = 1-t/A'''  
  37.     V1 = np.min(m,2)                                         #得到暗通道图像  
  38.     V1 = guidedfilter(V1, zmMinFilterGray(V1,7), r, eps)     #使用引导滤波优化  
  39.     bins = 2000  
  40.     ht = np.histogram(V1, bins)                              #计算大气光照A  
  41.     d = np.cumsum(ht[0])/float(V1.size)  
  42.     for lmax in range(bins-10, -1):  
  43.         if d[lmax]<=0.999:  
  44.             break  
  45.     A  = np.mean(m,2)[V1>=ht[1][lmax]].max()  
  46.            
  47.     V1 = np.minimum(V1*w, maxV1)                   #对值范围进行限制  
  48.        
  49.     return V1,A  
  50.    
  51. def deHaze(m, r=81, eps=0.001, w=0.95, maxV1=0.80, bGamma=False):  
  52.     Y = np.zeros(m.shape)  
  53.     V1,A = getV1(m, r, eps, w, maxV1)               #得到遮罩图像和大气光照  
  54.     for k in range(3):  
  55.         Y[:,:,k] = (m[:,:,k]-V1)/(1-V1/A)           #颜色校正  
  56.     Y =  np.clip(Y, 01)  
  57.     if bGamma:  
  58.         Y = Y**(np.log(0.5)/np.log(Y.mean()))       #gamma校正,默认不进行该操作  
  59.     return Y  
  60.    
  61. if __name__ == '__main__':  
  62.     m = deHaze(cv2.imread('land.jpg')/255.0)*255  
  63.     cv2.imwrite('defog.jpg', m)  

你可能感兴趣的:(python,图像增强)