关键词:Retinex 、 数据扩增 、 颜色恢复 、 对比度增强
前言
随着深度学习技术的发展,数据扩增已经成为了训练高质量模型的重要步骤之一。然而,传统的数据扩增方法往往不能完全满足需求,因此,研究人员开始探索更加高效和有效的数据扩增方法。本文将介绍一种基于retinex算法的数据扩增方法,该方法可以在不影响图像内容的前提下,提高图像的亮度、对比度和颜色饱和度等方面的表现,从而提高模型的训练效果。
retinex
基于视网膜理论公式:图像=反射率 * 亮度,我们需要的是估计亮度函数。该技术适用于高动态范围图像增强、水下图像增强、雾天图像增强和弱光图像增强。
在这里我们以下图作为原图进行图像变换:
retinex_FM实现
def retinex_FM(img,iter=4):
if len(img.shape)==2:
img=img[...,None]
ret=np.zeros(img.shape,dtype='uint8')
def update_OP(x,y):
nonlocal OP
IP=OP.copy()
if x>0 and y==0:
IP[:-x,:]=OP[x:,:]+R[:-x,:]-R[x:,:]
if x==0 and y>0:
IP[:,y:]=OP[:,:-y]+R[:,y:]-R[:,:-y]
if x<0 and y==0:
IP[-x:,:]=OP[:x,:]+R[-x:,:]-R[:x,:]
if x==0 and y<0:
IP[:,:y]=OP[:,-y:]+R[:,:y]-R[:,-y:]
IP[IP>maximum]=maximum
OP=(OP+IP)/2
for i in range(img.shape[-1]):
R=np.log(img[...,i].astype('double')+1)
maximum=np.max(R)
OP=maximum*np.ones(R.shape)
S=2**(int(np.log2(np.min(R.shape))-1))
while abs(S)>=1: #iterations is slow
for k in range(iter):
update_OP(S,0)
update_OP(0,S)
S=int(-S/2)
OP=np.exp(OP)
mmin=np.min(OP)
mmax=np.max(OP)
ret[...,i]=(OP-mmin)/(mmax-mmin)*255
return ret.squeeze()
retinex_SSR实现
def retinex_SSR(img,sigma):
if len(img.shape)==2:
img=img[...,None]
ret=np.zeros(img.shape,dtype='uint8')
for i in range(img.shape[-1]):
channel=img[...,i].astype('double')
S_log=np.log(channel+1)
gaussian=gauss_blur(channel,sigma)
#gaussian=cv2.filter2D(channel,-1,get_gauss_kernel(sigma)) #conv may be slow if size too big
#gaussian=cv2.GaussianBlur(channel,(0,0),sigma) #always slower
L_log=np.log(gaussian+1)
r=S_log-L_log
R=r #R=np.exp(r)?
mmin=np.min(R)
mmax=np.max(R)
stretch=(R-mmin)/(mmax-mmin)*255 #linear stretch
ret[...,i]=stretch
return ret.squeeze()
retinex_MSR实现
def retinex_MSR(img,sigmas=[15,80,250],weights=None):
'''r=∑(log(S)-log(S*G))w, MSR combines various SSR with different(or same) weights,
commonly we select 3 scales(sigma) and equal weights, (15,80,250) is a good
choice. If len(sigmas)=1, equal to SSR
args:
sigmas: a list
weights: None or a list, it represents the weight for each SSR, their sum should
be 1, if None, the weights will be [1/t, 1/t, ..., 1/t], t=len(sigmas)
'''
if weights==None:
weights=np.ones(len(sigmas))/len(sigmas)
elif not abs(sum(weights)-1)<0.00001:
raise ValueError('sum of weights must be 1!')
ret=np.zeros(img.shape,dtype='uint8')
if len(img.shape)==2:
img=img[...,None]
for i in range(img.shape[-1]):
channel=img[...,i].astype('double')
r=np.zeros_like(channel)
for k,sigma in enumerate(sigmas):
r+=(np.log(channel+1)-np.log(gauss_blur(channel,sigma,)+1))*weights[k]
mmin=np.min(r)
mmax=np.max(r)
stretch=(r-mmin)/(mmax-mmin)*255
ret[...,i]=stretch
return ret.squeeze()
retinex_gimp实现
def retinex_gimp(img,sigmas=[12,80,250],dynamic=2):
alpha=128
gain=1
offset=0
img=img.astype('double')+1 #
csum_log=np.log(np.sum(img,axis=2))
msr=MultiScaleRetinex(img-1,sigmas) #-1
r=gain*(np.log(alpha*img)-csum_log[...,None])*msr+offset
mean=np.mean(r,axis=(0,1),keepdims=True)
var=np.sqrt(np.sum((r-mean)**2,axis=(0,1),keepdims=True)/r[...,0].size)
mmin=mean-dynamic*var
mmax=mean+dynamic*var
stretch=(r-mmin)/(mmax-mmin)*255
stretch[stretch>255]=255
stretch[stretch<0]=0
return stretch.astype('uint8')
retinex_MSRCR实现
def retinex_MSRCR(img,sigmas=[12,80,250],s1=0.01,s2=0.01):
alpha=125
img=img.astype('double')+1 #
csum_log=np.log(np.sum(img,axis=2))
msr=MultiScaleRetinex(img-1,sigmas) #-1
r=(np.log(alpha*img)-csum_log[...,None])*msr
#beta=46;G=192;b=-30;r=G*(beta*r-b) #deprecated
#mmin,mmax=np.min(r),np.max(r)
#stretch=(r-mmin)/(mmax-mmin)*255 #linear stretch is unsatisfactory
for i in range(r.shape[-1]):
r[...,i]=simplest_color_balance(r[...,i],0.01,0.01)
return r.astype('uint8')
retinex_MSRCP算法
def retinex_MSRCP(img,sigmas=[12,80,250],s1=0.01,s2=0.01):
Int=np.sum(img,axis=2)/3
Diffs=[]
for sigma in sigmas:
Diffs.append(np.log(Int+1)-np.log(gauss_blur(Int,sigma)+1))
MSR=sum(Diffs)/3
Int1=simplest_color_balance(MSR,s1,s2)
B=np.max(img,axis=2)
A=np.min(np.stack((255/(B+eps),Int1/(Int+eps)),axis=2),axis=-1)
return (A[...,None]*img).astype('uint8')
cv2_heq实现
def cv2_heq(img,yuv=False):
if len(img.shape)==2:
img=img[...,None]
if yuv:
img=cv2.cvtColor(img,cv2.COLOR_BGR2YCrCb)
ret=img.copy()
for i in range(img.shape[-1]):
ret[...,i]=cv2.equalizeHist(img[...,i])
if yuv:
break
if yuv:
return cv2.cvtColor(ret,cv2.COLOR_YCrCb2BGR)
return ret.squeeze()
retinex_AMSR算法
def retinex_AMSR(img,sigmas=[12,80,250]):
img=img.astype('double')+1 #
msr=MultiScaleRetinex(img-1,sigmas,flag=False) #
y=0.05
for i in range(msr.shape[-1]):
v,c=np.unique((msr[...,i]*100).astype('int'),return_counts=True)
sort_v_index=np.argsort(v)
sort_v,sort_c=v[sort_v_index],c[sort_v_index] #plot hist
zero_ind=np.where(sort_v==0)[0][0]
zero_c=sort_c[zero_ind]
#
_=np.where(sort_c[:zero_ind]<=zero_c*y)[0]
if len(_)==0:
low_ind=0
else:
low_ind=_[-1]
_=np.where(sort_c[zero_ind+1:]<=zero_c*y)[0]
if len(_)==0:
up_ind=len(sort_c)-1
else:
up_ind=_[0]+zero_ind+1
#
low_v,up_v=sort_v[[low_ind,up_ind]]/100 #low clip value and up clip value
msr[...,i]=np.maximum(np.minimum(msr[:,:,i],up_v),low_v)
mmin=np.min(msr[...,i])
mmax=np.max(msr[...,i])
msr[...,i]=(msr[...,i]-mmin)/(mmax-mmin)*255
msr=msr.astype('uint8')
return msr
结尾
Retinex算法在数据扩增领域的应用是十分广泛的。通过对图像的处理,我们可以得到更加清晰、明亮、自然的图像,从而提升了机器学习模型的准确度和稳定性。而且,Retinex算法的优点在于可以针对不同的图像进行不同的处理,满足了数据扩增的个性化需求。因此,在进行图像数据扩增时,Retinex算法是一种十分有效的方法,值得我们深入研究和应用。