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