Python 還能實現圖片去霧?FFA 去霧算法、暗通道去霧算法用起來!(附代碼)

2021-02-19 數據派THU

在過去的幾十年中,單圖像去霧作為基本的低級視覺任務已引起了計算機視覺社區和人工智慧公司的越來越多的關注。其中最為典型的便是北大&北航提出FFA-Net去霧新網絡和何凱明博士提出的暗通道去霧算法,現所有源碼已開源。

https://arxiv.org/abs/1911.07559而今天我們就將針對這兩個項目進行實踐。其中得到的去霧效果如下:
實驗前的準備
首先我們使用的python版本是3.6.5所用到的模塊如下:Pytorch模塊用來模型訓練和網絡層建立;其底層和Torch框架一樣,但是使用Python重新寫了很多內容,不僅更加靈活,支持動態圖,而且提供了Python接口。不僅能夠實現強大的GPU加速,同時還支持動態神經網絡。FFA去霧算法
1.考慮到不同的通道特徵包含完全不同的加權信息並且不同圖像像素上的霧度分布不均勻,一種新穎的特徵注意(FA)模塊將通道注意與像素注意機制結合在一起。FA不平等地對待不同的特徵和像素,這在處理不同類型的信息時提供了額外的靈活性,從而擴展了CNN的表示能力。2.基本的塊結構包括本地殘差學習和功能注意,本地殘差學習允許較不重要的信息(例如薄霧區域或低頻)通過多個本地殘差連接被繞開,讓主網絡體系結構專注於更有效的信息。3.基於注意力的不同級別特徵融合(FFA)結構,可從特徵注意(FA)模塊中自適應學習特徵權重,從而為重要特徵賦予更多權重。這種結構還可以保留淺層信息,並將其傳遞到深層。實驗結果表明,提出的FFANet在數量和質量上都大大超過了現有的單圖像去霧方法,從而將SOTS室內測試數據集上最佳的PSNR度量從30.23db提高到35.77db。

def default_conv(in_channels, out_channels, kernel_size, bias=True):    return nn.Conv2d(in_channels, out_channels, kernel_size,padding=(kernel_sizeclass PALayer(nn.Module):    def __init__(self, channel):        super(PALayer, self).__init__()        self.pa = nn.Sequential(                nn.Conv2d(channel, channel                 nn.ReLU(inplace=True),                nn.Conv2d(channel                 nn.Sigmoid()        )    def forward(self, x):        y = self.pa(x)        return x * yclass CALayer(nn.Module):    def __init__(self, channel):        super(CALayer, self).__init__()        self.avg_pool = nn.AdaptiveAvgPool2d(1)        self.ca = nn.Sequential(                nn.Conv2d(channel, channel                 nn.ReLU(inplace=True),                nn.Conv2d(channel                 nn.Sigmoid()        )    def forward(self, x):        y = self.avg_pool(x)        y = self.ca(y)        return x * yclass Block(nn.Module):    def __init__(self, conv, dim, kernel_size,):        super(Block, self).__init__()        self.conv1=conv(dim, dim, kernel_size, bias=True)        self.act1=nn.ReLU(inplace=True)        self.conv2=conv(dim,dim,kernel_size,bias=True)        self.calayer=CALayer(dim)        self.palayer=PALayer(dim)    def forward(self, x):        res=self.act1(self.conv1(x))        res=res+x         res=self.conv2(res)        res=self.calayer(res)        res=self.palayer(res)        res += x         return resclass Group(nn.Module):    def __init__(self, conv, dim, kernel_size, blocks):        super(Group, self).__init__()        modules = [ Block(conv, dim, kernel_size)  for _ in range(blocks)]        modules.append(conv(dim, dim, kernel_size))        self.gp = nn.Sequential(*modules)    def forward(self, x):        res = self.gp(x)        res += x        return resclass FFA(nn.Module):    def __init__(self,gps,blocks,conv=default_conv):        super(FFA, self).__init__()        self.gps=gps        self.dim=64        kernel_size=3        pre_process = [conv(3, self.dim, kernel_size)]        assert self.gps==3        self.g1= Group(conv, self.dim, kernel_size,blocks=blocks)        self.g2= Group(conv, self.dim, kernel_size,blocks=blocks)        self.g3= Group(conv, self.dim, kernel_size,blocks=blocks)        self.ca=nn.Sequential(*[            nn.AdaptiveAvgPool2d(1),            nn.Conv2d(self.dim*self.gps,self.dim            nn.ReLU(inplace=True),            nn.Conv2d(self.dim            nn.Sigmoid()            ])        self.palayer=PALayer(self.dim)        post_precess = [            conv(self.dim, self.dim, kernel_size),            conv(self.dim, 3, kernel_size)]        self.pre = nn.Sequential(*pre_process)        self.post = nn.Sequential(*post_precess)    def forward(self, x1):        x = self.pre(x1)        res1=self.g1(x)        res2=self.g2(res1)        res3=self.g3(res2)        w=self.ca(torch.cat([res1,res2,res3],dim=1))        w=w.view(-1,self.gps,self.dim)[:,:,:,None,None]        out=w[:,0,::]*res1+w[:,1,::]*res2+w[:,2,::]*res3        out=self.palayer(out)        x=self.post(out)        return x + x1

python main.py --net='ffa' --crop --crop_size=240 --blocks=19--gps=3 --bs=2 --lr=0.0001 --trainset='its_train' --testset='its_test' --steps=500000--eval_step=5000

python test.py --task='its or ots' --test_imgs='test_imgs'

暗通道去霧算法搭建

何愷明的暗通道先驗(dark channel prior)去霧算法是CV界去霧領域很有名的算法,關於該算法的論文"Single Image Haze Removal Using DarkChannel Prior"一舉獲得2009年CVPR最佳論文。作者統計了大量的無霧圖像,發現一條規律:每一幅圖像的每一個像素的RGB三個顏色通道中,總有一個通道的灰度值很低。基於這個幾乎可以視作是定理的先驗知識,作者提出暗通道先驗的去霧算法。對於任意一幅輸入圖像,定義其暗通道的數學表達式為:
文章中介紹的方法是軟摳圖的方法,此方法過程複雜,速度緩慢,因此採用導向濾波對傳輸函數進行濾波。導向濾波的原理此處不再贅述,其偽代碼為:
1、濾波函數:


def zmMinFilterGray(src, r=7):    '''if r <= 0:        returnsrc    h, w =src.shape[:2]    I = src    res =np.minimum(I  , I[[0]+range(h-1)  , :])    res =np.minimum(res, I[range(1,h)+[h-1], :])    I = res    res =np.minimum(I  , I[:, [0]+range(w-1)])    res =np.minimum(res, I[:, range(1,w)+[w-1]])    returnzmMinFilterGray(res, r-1)'''    return cv2.erode(src,np.ones((2*r+1, 2*r+1)))

def guidedfilter(I, p, r, eps):    '''引導濾波,直接參考網上的matlab代碼'''    height, width = I.shape    m_I = cv2.boxFilter(I, -1, (r,r))    m_p = cv2.boxFilter(p, -1, (r,r))    m_Ip = cv2.boxFilter(I*p, -1, (r,r))    cov_Ip = m_Ip-m_I*m_p    m_II = cv2.boxFilter(I*I, -1, (r,r))    var_I = m_II-m_I*m_I    a = cov_Ip/(var_I+eps)    b = m_p-a*m_I    m_a = cv2.boxFilter(a, -1, (r,r))    m_b = cv2.boxFilter(b, -1, (r,r))    return m_a*I+m_b

計算大氣遮罩圖像V1和光照值A, V1 = 1-t/A

def getV1(m, r, eps, w, maxV1):  #輸入rgb圖像,值範圍[0,1]    '''計算大氣遮罩圖像V1和光照值A, V1 = 1-t/A'''    V1 = np.min(m,2)                                         #得到暗通道圖像    V1 = guidedfilter(V1, zmMinFilterGray(V1,7), r, eps)     #使用引導濾波優化    bins = 2000    ht = np.histogram(V1, bins)                              #計算大氣光照A    d = np.cumsum(ht[0])/float(V1.size)    for lmax in range(bins-1, 0, -1):        if d[lmax]<=0.999:            break    A  = np.mean(m,2)[V1>=ht[1][lmax]].max()
V1 = np.minimum(V1*w, maxV1) #對值範圍進行限制 return V1,A

通過調整代碼,將視頻分幀,可以達到視頻去霧的效果:

import cv2import numpy as npdef zmMinFilterGray(src, r=7):   '''最小值濾波,r是濾波器半徑'''   '''if r <= 0:       return src   h, w = src.shape[:2]    I= src   res = np.minimum(I  ,I[[0]+range(h-1)  , :])   res = np.minimum(res, I[range(1,h)+[h-1], :])    I= res   res = np.minimum(I  , I[:,[0]+range(w-1)])   res = np.minimum(res, I[:, range(1,w)+[w-1]])   return zmMinFilterGray(res, r-1)'''   return cv2.erode(src, np.ones((2 * r + 1, 2 * r + 1)))  # 使用opencv的erode函數更高效def guidedfilter(I, p, r, eps):    '''引導濾波'''   height, width = I.shape   m_I = cv2.boxFilter(I, -1, (r, r))   m_p = cv2.boxFilter(p, -1, (r, r))   m_Ip = cv2.boxFilter(I * p, -1, (r, r))   cov_Ip = m_Ip - m_I * m_p   m_II = cv2.boxFilter(I * I, -1, (r, r))   var_I = m_II - m_I * m_I    a= cov_Ip / (var_I + eps)    b= m_p - a * m_I   m_a = cv2.boxFilter(a, -1, (r, r))   m_b = cv2.boxFilter(b, -1, (r, r))   return m_a * I + m_bdef getV1(m, r, eps, w, maxV1):  # 輸入rgb圖像,值範圍[0,1]   '''計算大氣遮罩圖像V1和光照值A, V1 = 1-t/A'''    V1 = np.min(m, 2)  # 得到暗通道圖像   V1 = guidedfilter(V1, zmMinFilterGray(V1, 7), r, eps)  # 使用引導濾波優化   bins = 2000   ht = np.histogram(V1, bins)  # 計算大氣光照A    d= np.cumsum(ht[0]) / float(V1.size)   for lmax in range(bins - 1, 0, -1):       if d[lmax] <= 0.999:           break    A= np.mean(m, 2)[V1 >= ht[1][lmax]].max()   V1 = np.minimum(V1 * w, maxV1)  # 對值範圍進行限制   return V1, Adef deHaze(m, r=81, eps=0.001, w=0.95,maxV1=0.80, bGamma=False):    Y= np.zeros(m.shape)   V1, A = getV1(m, r, eps, w, maxV1) # 得到遮罩圖像和大氣光照   for k in range(3):       Y[:, :, k] = (m[:, :, k] - V1) / (1 - V1 / A)  # 顏色校正    Y= np.clip(Y, 0, 1)   if bGamma:       Y = Y ** (np.log(0.5) / np.log(Y.mean()))  # gamma校正,默認不進行該操作   return Yvideo = "1.mp4"cap = cv2.VideoCapture(video)while cap.isOpened():   _,frame = cap.read()   frame = cv2.flip(frame, -180)   cv2.imwrite("temp.jpg",frame)    m= deHaze(frame / 255.0) * 255   height, width = m.shape[:2]    #縮小圖像   size = (int(width * 0.5), int(height * 0.5))   shrink = cv2.resize(m, size, interpolation=cv2.INTER_AREA)   cv2.imwrite('defog.jpg', shrink)   img = cv2.imread("defog.jpg")   cv2.imshow("frame",img)   key = cv2.waitKey(1) & 0xFF   if key == ord("q"):       breakcap.release()cv2.destroyAllWindows()

作者介紹:

李秋鍵,CSDN 博客專家,CSDN達人課作者。碩士在讀於中國礦業大學,開發有taptap安卓武俠遊戲一部,vip視頻解析,文意轉換工具,寫作機器人等項目,發表論文若干,多次高數競賽獲獎等等。

https://github.com/zhilin007/FFA-Net

相關焦點

  • 【學術論文】基於ZYNQ的Retinex實時圖像去霧
    :基於圖像復原的去霧方法和基於圖像增強的去霧方法[3]。前者是從圖像退化的原因出發,建立去霧圖像的物理模型,根據該模型來恢復出清晰的去霧圖像。該類方法去霧效果良好,但算法複雜度較大,不便於在硬體平臺上實現。基於圖像增強的去霧方法不考慮圖像霧化的原因和機理,而是有選擇性地增強需要的細節信息。屬於此類的去霧方法常用的是Retinex算法[4]。Retinex算法能較好地保留圖像的邊緣等細節信息,處理後的圖像具有亮度適中、對比度高等優點。
  • 【圖像去霧】直方圖均衡化+Retinex理論圖像去霧含GUI matlab源碼
    一般來說,全局直方圖去霧算法可以實現含霧圖像的增強效果,處理前後的直方圖在分布上具有明顯變化,但在圖像整體上容易出現某些色彩失真的現象。2.3 Retinex增強處理基於全局直方圖、局部直方圖的圖像去霧算法在理論及實現上比較簡單,能起到一定的去霧處理效果。
  • 大賽創意階段一等獎作品賞析:面向智能成像載荷的圖像實時去霧APP軟體設計
    ,研究圖像四叉樹空間索引和圖像暗通道先驗方法,突破估計模型參數利用天空區域的局限性,重建場景深度,結合大氣散射模型復原低頻無霧圖像;同時針對目標復原過程中噪聲遺留問題,研究軟閾值去噪算法,結合低頻信息重構的透射率,以梯度增強方式豐富紋理細節,最後小波重構出清晰圖像。
  • MATLAB基於直方圖的圖像去霧
    下面就為大家介紹三種常用的圖像去霧方法。1.全局直方圖處理通過函數imread讀取RGB圖像,並通過維數m*n*3的矩陣來表示。其中,維數m*n表示圖像的行數、列數信息,維數3表示圖像的R、G、B三層通道數據。因此,全局直方圖處理通過對RGB圖像的R、G、B三層通道分別進行直方圖均衡化,再整合到新的圖像的方式來進行。
  • Python 中文圖片OCR
    有個需求,需要從一張圖片中識別出中文,通過python來實現,這種這麼高大上的黑科技我們普通人自然搞不了,去github找了一個似乎能滿足需求的開源庫
  • ps通道摳圖技巧_扣圖去背景方法(圖解)
    ps通道摳圖技巧所謂的ps通道摳圖,不是所有的圖片都適合用通道來摳圖的,一般你得根據通道裡面的R、G、B三個通道色彩信息來決定。那麼ps通道摳圖技巧有哪些呢?將人物從背景中摳出來,最主要的是頭髮絲的部分,也是難點。下面找一張圖片,給大家介紹一下ps通道摳圖技巧的操作方法。
  • 深度學習中的正則化技術概述(附Python+keras實現代碼)
    並附python+keras實戰。關注公眾號並發送關鍵字"正則化數據集"獲取數據集下載指引,發送關鍵字"正則化代碼"獲取完整代碼。歡迎大家點擊上方藍字關注我們的公眾號:磐創AI。介紹數據科學研究者們最常遇見的問題之一就是怎樣避免過擬合。
  • PyTorch 模型訓練實用教程(附代碼)
    PyTorch 模型訓練實用教程(附代碼)關注微信公眾號 datayx  然後回復  訓練  即可獲取。這裡以 Resnet34 為例介紹「複雜」模型的定義,這部分代碼從 github 上獲取。2.1.3 nn.Sequetialtorch.nn.Sequential 其實就是 Sequential 容器,該容器將一系列操作按先後順序給包起來,方便重複使用。例如 Resnet 中有很多重複的 block,就可以用 Sequential 容器把重複的地方包起來。
  • [去水印] 在線去水印怎麼不留痕跡?水印雲無痕去?
    [去水印] 在線去水印怎麼不留痕跡?水印雲無痕去?[去水印] 在線去水印怎麼不留痕跡?水印雲無痕去?
  • Python繪製時間演變圖工具匯總(附代碼)
    之前轉載了一篇使用Python製作時間演化圖的推文,後臺留言說想要代碼,但是我也沒有那篇推文的代碼。這次就把我平時用到的繪製時間動態變化圖的工具介紹一下,同時附上代碼。xmoviexmovie屬於更高級一些的工具,可直接兼容xarray對象,提供了非常方便的可視化方法,比如一行代碼動態可視化、自定義繪圖函數、並行繪圖等。
  • Unity怎麼去實現ACT戰鬥?
    在市面上有很多被稱作「動作遊戲」的遊戲,他們壓根就不是動作遊戲,從技術上區分動作遊戲,只有一個key——看他的實現方式,也就是如果實現的時候是通過幀作為單位,來制定每一幀的各種碰撞框,並且為每一幀制定數據的,他才是動作遊戲;反之,以動作為單位的,比如使用了motion matching、animator、行為樹之類,從邏輯上就是以動作和管理動作為根基的遊戲,他們都不是動作遊戲
  • 用PyTorch實現各種GANs(附論文和代碼地址)
    論文地址:arxiv.org/abs/1610.09585代碼地址:github.com/eriklindernoren/PyTorch-GAN/blob/master/implementations/acgan/acgan.py運行示例:$ cd implementations/acgan/$ python3 acgan.py
  • 運用python實現埠掃描
    前言最近在學習掃描器,web掃描器的內容,今天就分享一下python下的namp的埠掃描功能模塊的實現並在代碼中了解一下在
  • 用Python實現神奇切圖算法Seam Carving
    算法還能判斷出圖片裡哪部分重要哪部分不重要?簡單的說,利用這個技術我們可以在縮放時固定圖片中特定區域的大小,或者可以在縮小時讓特定的區塊被周圍圖像縫合消除,並且因為「seam carving」的縫補算法,你可以讓圖片縮放後仍然維持整體的完整性。
  • 用Python 代碼實現簡單圖片人像識別換臉
    來源:代碼灣連結:http://codebay.cn/post/8232.html在這篇文章中我將介紹如何寫一個簡短(200行)的 Python 腳本,來自動地將一幅圖片的臉替換為另一幅圖片的臉旋轉、縮放、平移和第二張圖片,以配合第一步。調整第二張圖片的色彩平衡,以適配第一張圖片。把第二張圖像的特性混合在第一張圖像中。1.使用 dlib 提取面部標記該腳本使用 dlib 的 Python 綁定來提取面部標記:
  • 圖像超解析度重建算法,讓模糊圖像變清晰(附數據和代碼)
    因此,超解析度重建的很多算法也被學者遷移到圖像修復領域中,完成一些諸如jpep壓縮去燥、去模糊等任務。假設,如果想對原圖放大3倍,那麼需要生成出3^2=9個同等大小的特徵圖,也就是通道數擴充了9倍(這個通過普通的卷積操作即可實現)。然後將九個同等大小的特徵圖拼成一個放大3倍的大圖,這就是子像素卷積操作了。實現時先將原始特徵圖通過卷積擴展其通道數,如果是想放大4倍,那麼就需要將通道數擴展為原來的16倍。特徵圖做完卷積後再按照特定的格式進行排列,即可得到一張大圖,這就是所謂的像素清洗。
  • 不會遊泳,能去宿霧浮潛嗎?
    去宿霧,一定要體驗一次浮潛!可是,不會遊泳的話,還能去浮潛嗎?接著往下看~
  • CVPR 2021論文盤點 | 去陰影、去反光、去高光、去偽影篇
    武漢大學;中山大學;劍橋大學;香港理工大學論文 | https://openaccess.thecvf.com/content/CVPR2021/papers/Fu_A_Multi-Task_Network_for_Joint_Specular_Highlight_Detection_and_Removal_CVPR_2021_paper.pdfHDR Deghosting去偽影
  • 基於ADSP-BF533處理器的去方塊濾波器的實現及優化
    我們使用的ADSP-BF533可以實現600 MHz的持續工作,具有:4 GB的統一尋址空間;80 kB SRAM的L1指令指令存儲器,其中16 kB可配置成4路的聯合Cache;2個32 kB SRAM的L1數據存儲器,其中一半可配置為Cache;集成豐富的外圍設備和接口。
  • Python圖片識別庫Tesseract實戰
    在Ui測試的時候,有時我們會遇到」元素以圖片的形式展示內容」(最經典的案例使驗證碼).這時傳統的文檔解析方案就失效了.但是,Python中你可以輕易的使用ocr(光學字符識別)技術.對圖片元素中的文字進行提取.從而解決技術難題.具體需要以下3步:①安裝Tesseract-ocr服務②安裝pytesseract-python驅動庫③識別圖片①Tesseract是一款由Google贊助的開源OCR。