├── ImageDenoising ├── SimCo.pyc ├── KSVDDL.py ├── MODDL.py ├── GDDL.py ├── PSimCo.py ├── RSimCo.py ├── README.md ├── im2col.py ├── omperr.py ├── GDBTLS.py ├── RGDBTLS.py ├── KSVD.py └── DenoiseImage.py ├── SimCo ├── README.md ├── fg_tilde_eval01.py ├── DictUpdate03.py └── DictLineSearch03.py ├── KSVD ├── README.md ├── omp.py └── KSVD.py ├── README.md ├── omp.py ├── FindDistanceBetweenDictionaries.py └── Dictionary_learning_v2.py /ImageDenoising/SimCo.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rehan-Ahmad/Dictionary-Learning-Algorithms/HEAD/ImageDenoising/SimCo.pyc -------------------------------------------------------------------------------- /SimCo/README.md: -------------------------------------------------------------------------------- 1 | Reference: 2 | W. Dai, T. Xu, and W. Wang, "Simultaneous Codeword Optimization (SimCO) for Dictionary Update and Learning," submitted to IEEE Transactions on Signal Processing, October 2011. 3 | Full text is available at http://arxiv.org/abs/1109.5302 4 | -------------------------------------------------------------------------------- /KSVD/README.md: -------------------------------------------------------------------------------- 1 | Reference: 2 | "The K-SVD: An Algorithm for Designing of Overcomplete Dictionaries for Sparse Representation", written by M. Aharon, M. Elad, and A.M. Bruckstein and 3 | appeared in the IEEE Trans. On Signal Processing, Vol. 54, no. 11, pp. 4311-4322, November 2006. 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dictionary-Learning-Algorithms 2 | Main File: **Dictionary_Learning_v2.py**
3 | 4 | This file execute all the algorithms consisting of Gradient Descent based Dictionary Learning (variants), KSVD, MOD and SimCo.
5 | It will plot the error curve after comparing the original and learned Dictionary on the synthetic Dataset.
6 | 7 | Implementation of Dictionary Learning algorithms on real data can be found in DenoiseImage folder. It uses learned dictionary for image denoising. 8 | -------------------------------------------------------------------------------- /ImageDenoising/KSVDDL.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Aug 09 16:13:55 2017 4 | 5 | KSVD based Dictionary Learning algorithm. 6 | 7 | @author: Rehan 8 | """ 9 | import numpy as np 10 | from copy import deepcopy 11 | from omperr import omperr 12 | from KSVD import KSVD 13 | 14 | def KSVDDL(Y,param): 15 | D = deepcopy(param.initialDictionary) 16 | iterations = param.itN 17 | errglobal = param.errorGoal 18 | 19 | for j in range(iterations): 20 | X = omperr(D,Y,errglobal) 21 | D,X = KSVD(Y,D,X) 22 | 23 | return D -------------------------------------------------------------------------------- /ImageDenoising/MODDL.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Aug 09 16:10:33 2017 4 | 5 | MOD algorithm for Dictionary Learning 6 | 7 | @author: Rehan 8 | """ 9 | import numpy as np 10 | from copy import deepcopy 11 | from omperr import omperr 12 | from sklearn import preprocessing 13 | 14 | def MODDL(Y,param): 15 | D = deepcopy(param.initialDictionary) 16 | iterations = param.itN 17 | errglobal = param.errorGoal 18 | 19 | for j in range(iterations): 20 | X = omperr(D,Y,errglobal) 21 | D = np.dot(Y,np.linalg.pinv(X)) 22 | D = preprocessing.normalize(D,norm='l2',axis=0) 23 | return D 24 | -------------------------------------------------------------------------------- /ImageDenoising/GDDL.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Aug 09 14:00:03 2017 4 | 5 | @author: Rehan 6 | 7 | Gradient Descent Dictionary Learning (GDDL) with Momentum term 8 | 9 | """ 10 | import numpy as np 11 | from copy import deepcopy 12 | from omperr import omperr 13 | from sklearn import preprocessing 14 | 15 | def GDDL(Y,param): 16 | iterations = param.itN 17 | D = deepcopy(param.initialDictionary) 18 | errglobal = param.errorGoal 19 | gamma = param.MomentumGamma 20 | alpha = param.alpha 21 | 22 | v = np.zeros(D.shape) 23 | for j in range(iterations): 24 | X = omperr(D,Y,errglobal) 25 | v = gamma*v - alpha*np.dot(Y-np.dot(D,X),X.T) 26 | D = D - v 27 | D = preprocessing.normalize(D,norm='l2',axis=0) 28 | return D -------------------------------------------------------------------------------- /ImageDenoising/PSimCo.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Aug 06 11:34:26 2017 4 | 5 | @author: Rehan 6 | """ 7 | from copy import deepcopy 8 | from DictUpdate03 import DictUpdate03 9 | from omperr import omperr 10 | 11 | def PSimCo(Y,param): 12 | class IPar(): 13 | pass 14 | IPara = IPar() 15 | itN = param.itN 16 | D = deepcopy(param.initialDictionary) 17 | errglobal = param.errorGoal 18 | IPara.mu = 0 19 | IPara.I = param.I 20 | IPara.dispN = 20 21 | IPara.DebugFlag = 0 22 | IPara.itN = 1 23 | IPara.gmin = 1e-5 # the minimum value of gradient 24 | IPara.Lmin = 1e-6 # t4-t1 should be larger than Lmin 25 | IPara.t4 = 1e-2 # the initial value of t4 26 | IPara.rNmax = 3 # the number of iterative refinement in Part B in DictLineSearch03.m 27 | 28 | for itn in range(itN): 29 | X = omperr(D,Y,errglobal) 30 | D,X,_ = DictUpdate03(Y,D,X,IPara) 31 | 32 | return D -------------------------------------------------------------------------------- /ImageDenoising/RSimCo.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Aug 06 11:34:26 2017 4 | 5 | @author: Rehan 6 | """ 7 | from copy import deepcopy 8 | from DictUpdate03 import DictUpdate03 9 | from omperr import omperr 10 | 11 | def RSimCo(Y,param): 12 | class IPar(): 13 | pass 14 | IPara = IPar() 15 | itN = param.itN 16 | D = deepcopy(param.initialDictionary) 17 | errglobal = param.errorGoal 18 | IPara.mu = 0.05 19 | IPara.I = param.I 20 | IPara.dispN = 20 21 | IPara.DebugFlag = 0 22 | IPara.itN = 1 23 | IPara.gmin = 1e-5 # the minimum value of gradient 24 | IPara.Lmin = 1e-6 # t4-t1 should be larger than Lmin 25 | IPara.t4 = 1e-2 # the initial value of t4 26 | IPara.rNmax = 3 # the number of iterative refinement in Part B in DictLineSearch03.m 27 | 28 | for itn in range(itN): 29 | # X = omp(D,Y,param.sparsity) 30 | X = omperr(D,Y,errglobal) 31 | D,X,_ = DictUpdate03(Y,D,X,IPara) 32 | 33 | return D -------------------------------------------------------------------------------- /omp.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jun 12 12:08:22 2017 4 | 5 | @author: Rehan Ahmad 6 | 7 | Orthogonal Matching Persuit (OMP) algorithm for sparse representation 8 | 9 | D: Dictionary, columns must be normalized (by l2 norm) 10 | X: input signal to represent 11 | L: max. no. of coefficients for each signal 12 | A: Sparse coefficient Matrix 13 | 14 | """ 15 | import numpy as np 16 | from copy import deepcopy 17 | 18 | def omp(D,X,L): 19 | n,P = X.shape 20 | n,K = D.shape 21 | A = np.ndarray((D.shape[1],X.shape[1])) 22 | for k in range(P): 23 | a = 0 24 | x = deepcopy(X[:,k]) 25 | residual = deepcopy(x) 26 | indx = np.zeros((L,),dtype = int) 27 | for j in range(L): 28 | proj = np.dot(D.T,residual) 29 | pos = np.argmax(np.abs(proj)) 30 | indx[j] = int(pos) 31 | a = np.dot(np.linalg.pinv(D[:,indx[0:j+1]]),x) 32 | residual = x-np.dot(D[:,indx[0:j+1]],a) 33 | if np.sum(residual**2) < 1e-6: 34 | break 35 | temp = np.zeros((K,)) 36 | temp[indx[0:j+1]] = deepcopy(a) 37 | A[:,k] = temp 38 | return A 39 | -------------------------------------------------------------------------------- /KSVD/omp.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jun 12 12:08:22 2017 4 | 5 | @author: Rehan Ahmad 6 | 7 | Orthogonal Matching Persuit (OMP) algorithm for sparse representation 8 | 9 | D: Dictionary, columns must be normalized (by l2 norm) 10 | X: input signal to represent 11 | L: max. no. of coefficients for each signal 12 | A: Sparse coefficient Matrix 13 | 14 | """ 15 | import numpy as np 16 | from copy import deepcopy 17 | 18 | def omp(D,X,L): 19 | n,P = X.shape 20 | n,K = D.shape 21 | A = np.ndarray((D.shape[1],X.shape[1])) 22 | for k in range(P): 23 | a = 0 24 | x = deepcopy(X[:,k]) 25 | residual = deepcopy(x) 26 | indx = np.zeros((L,),dtype = int) 27 | for j in range(L): 28 | proj = np.dot(D.T,residual) 29 | pos = np.argmax(np.abs(proj)) 30 | indx[j] = int(pos) 31 | a = np.dot(np.linalg.pinv(D[:,indx[0:j+1]]),x) 32 | residual = x-np.dot(D[:,indx[0:j+1]],a) 33 | if np.sum(residual**2) < 1e-6: 34 | break 35 | temp = np.zeros((K,)) 36 | temp[indx[0:j+1]] = deepcopy(a) 37 | A[:,k] = temp 38 | return A 39 | -------------------------------------------------------------------------------- /ImageDenoising/README.md: -------------------------------------------------------------------------------- 1 | # Image Dnoising using Dictionary learning techniques. 2 | 3 | Main File: **DenoiseImage.py**
4 | 5 | **GDDL**: Simple Gradient Descent with Momentum accelearation for Dictionary Learning .
6 | **GDBTLS**: Gradient Descent based Back Tracking Line Search Algorithm for Dictionary Learning.
7 | **RGDBTLS**: Regularized Gradient Descent based Back Tracking Line Search Algorithm for Dictionary Learning.
8 | **MODDL**: Method of Optimal Directions for Dictionary Learning.
9 | 10 | Gradient Descent based dictionary learning algorithms are self written. While implementation of KSVD and SimCo are taken from following references.
11 | Some required files for RSimCo and PSimCo can be found in SimCo folder.
12 | 13 | ## References: 14 | 1. W. Dai, T. Xu, and W. Wang, "Simultaneous Codeword Optimization (SimCO) for Dictionary Update and Learning," submitted to IEEE Transactions on Signal Processing, October 2011. Full text is available at http://arxiv.org/abs/1109.5302.
15 | 2. "The K-SVD: An Algorithm for Designing of Overcomplete Dictionaries for Sparse Representation", written by M. Aharon, M. Elad, and A.M. Bruckstein and appeared in the IEEE Trans. On Signal Processing, Vol. 54, no. 11, pp. 4311-4322, November 2006.
16 | -------------------------------------------------------------------------------- /SimCo/fg_tilde_eval01.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Jul 07 17:13:53 2017 4 | 5 | @author: Rehan Ahmad 6 | 7 | fg_tilde_eval01 computes the gradient descent direction for LineSearch 8 | in regularized SimCO version 9 | 10 | References: 11 | W. Dai, T. Xu, and W. Wang, 12 | "Simultaneous Codeword Optimization (SimCO) for Dictionary Update and Learning," 13 | submitted to IEEE Transactions on Signal Processing, October 2011. 14 | Full text is available at http://arxiv.org/abs/1109.5302 15 | 16 | """ 17 | import numpy as np 18 | from copy import deepcopy 19 | import pdb 20 | 21 | def fg_tilde_eval01(Y,D,Omega,IPara): 22 | m,n = Y.shape 23 | d = D.shape[1] 24 | X = np.zeros((d,n)) 25 | OmegaL = np.sum(Omega,axis = 0) 26 | mu = IPara.mu #the parameter of regularized item 27 | mu_sqrt = np.sqrt(mu) 28 | for cn in range(n): 29 | L = deepcopy(OmegaL[cn]) 30 | X[Omega[:,cn],cn] = np.linalg.lstsq(np.append(D[:,Omega[:,cn]],np.diag(mu_sqrt*np.ones((L,))),axis=0),\ 31 | np.append(Y[:,cn],np.zeros((L,)),axis=0))[0] 32 | 33 | Yr = Y - np.dot(D,X) 34 | # the cost function with regularized term 35 | f = np.sum(Yr*Yr) + mu*np.sum(X*X) 36 | freal = np.sum(Yr*Yr) 37 | 38 | g = -2*np.dot(Yr,X.T) 39 | # additional steps to make sure the orthoganilty 40 | DGcorr = np.sum(D*g, axis = 0) 41 | g = g - D*np.tile(DGcorr,(m,1)) 42 | 43 | return f,X,g,freal 44 | -------------------------------------------------------------------------------- /FindDistanceBetweenDictionaries.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Jul 07 11:42:02 2017 4 | 5 | @author: Rehan Ahmad 6 | 7 | D : Original Dictionary 8 | Dhat: Predicted/Estimated Dictionary 9 | 10 | Finding the matched atoms between original and predicted Dictionary. 11 | Sweeping through the columns of original Dictionary and finding closest 12 | column in predicted Dictionary. 13 | 14 | Reference: 15 | "The K-SVD: An Algorithm for Designing of Overcomplete Dictionaries for Sparse 16 | Representation", written by M. Aharon, M. Elad, and A.M. Bruckstein and 17 | appeared in the IEEE Trans. On Signal Processing, Vol. 54, no. 11, 18 | pp. 4311-4322, November 2006. 19 | 20 | """ 21 | import numpy as np 22 | from copy import deepcopy 23 | def FindDistanceBetweenDictionaries(D,Dhat): 24 | catchCounter = 0 25 | totalDistances = 0 26 | Dnew = np.ndarray((D.shape[0],D.shape[1])) 27 | for i in range(Dhat.shape[1]): 28 | Dnew[:,i] = deepcopy(np.sign(Dhat[0,i])*Dhat[:,i]) 29 | for i in range(Dhat.shape[1]): 30 | d = deepcopy(np.sign(D[0,i])*D[:,i]) 31 | distances = np.sum((Dnew-np.tile(np.reshape(d,(-1,1)),(1,Dhat.shape[1])))**2,axis=0) 32 | index = np.argmin(distances) 33 | errorOfElement = 1 - np.abs(np.dot(Dnew[:,index],d)) 34 | totalDistances = totalDistances + errorOfElement 35 | catchCounter = catchCounter + (errorOfElement < 0.01) 36 | return catchCounter 37 | -------------------------------------------------------------------------------- /ImageDenoising/im2col.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Aug 07 23:56:42 2017 4 | 5 | Implementation taken from Matlab im2col.py 6 | This version contains sliding order only. 7 | 8 | @author: Rehan 9 | """ 10 | import numpy as np 11 | from copy import deepcopy 12 | 13 | def im2col(a,block): 14 | ma,na = a.shape 15 | m = block[0] 16 | n = block[1] 17 | 18 | if (ma E2 and j < maxNumCoef: 34 | j = j+1 35 | proj = np.dot(D.T,residual) 36 | pos = np.argmax(np.abs(proj)) 37 | indx = np.append(indx,int(pos)) 38 | a = np.dot(pinv(D[:,indx[0:j]]),x) 39 | residual = x-np.dot(D[:,indx[0:j]],a) 40 | currResNorm2 = np.sum(residual**2) 41 | if (len(indx) > 0): 42 | A[indx,k] = deepcopy(a) 43 | return A 44 | -------------------------------------------------------------------------------- /ImageDenoising/GDBTLS.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Aug 09 14:24:07 2017 4 | Gradient Descent Back Tracking Line Search (GDBTLS) Algorithm 5 | @author: Rehan 6 | """ 7 | import numpy as np 8 | from omperr import omperr 9 | from copy import deepcopy 10 | from sklearn import preprocessing 11 | 12 | def GDBTLS(Y,param): 13 | D = deepcopy(param.initialDictionary) 14 | errglobal = param.errorGoal 15 | iterations = param.itN 16 | np.random.seed(3) 17 | beta = np.random.rand() 18 | np.random.seed(3) 19 | eta = np.random.rand()*0.5 20 | Grad = np.zeros(D.shape) 21 | 22 | for j in range(iterations): 23 | alpha = 1 24 | X = omperr(D,Y,errglobal) 25 | Dhat_GDtemp = deepcopy(D) 26 | 27 | ################################################################# 28 | # Back Tracking line search Algorithm (BTLS) to find optimal # 29 | # value of alpha # 30 | ################################################################# 31 | Grad = -np.dot(Y-np.dot(D,X),X.T) 32 | oldfunc = np.linalg.norm(Y-np.dot(D,X),'fro')**2 33 | newfunc = np.linalg.norm(Y-np.dot(Dhat_GDtemp,X),'fro')**2 34 | while(~(newfunc <= oldfunc-eta*alpha*np.sum(Grad**2))): 35 | alpha = beta*alpha 36 | Dhat_GDtemp = deepcopy(D) 37 | Dhat_GDtemp = Dhat_GDtemp + alpha*np.dot(Y-np.dot(Dhat_GDtemp,X),X.T) 38 | Dhat_GDtemp = preprocessing.normalize(Dhat_GDtemp,norm='l2',axis=0) 39 | newfunc = np.linalg.norm(Y-np.dot(Dhat_GDtemp,X),'fro')**2 40 | if(alpha < 1e-9): 41 | break 42 | ################################################################# 43 | ################################################################# 44 | D = D + alpha*np.dot(Y-np.dot(D,X),X.T) 45 | D = preprocessing.normalize(D,norm='l2',axis=0) 46 | 47 | return D -------------------------------------------------------------------------------- /ImageDenoising/RGDBTLS.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Aug 09 14:35:43 2017 4 | 5 | Regularized Gradient Descent based Back Tracking Line Search (RGDBTLS) 6 | Algorithm. Regularization on Sparse matrix X. 7 | 8 | @author: Rehan 9 | """ 10 | import numpy as np 11 | from sklearn import preprocessing 12 | from omperr import omperr 13 | from copy import deepcopy 14 | 15 | def RGDBTLS(Y,param): 16 | mu = param.mu 17 | D = deepcopy(param.initialDictionary) 18 | iterations = param.itN 19 | errglobal = param.errorGoal 20 | np.random.seed(3) 21 | beta = np.random.rand() 22 | np.random.seed(3) 23 | eta = np.random.rand()*0.5 24 | Grad = np.zeros(D.shape) 25 | 26 | for j in range(iterations): 27 | alpha = 1 28 | X = omperr(D,Y,errglobal) 29 | Dhat_RGDtemp = deepcopy(D) 30 | 31 | ################################################################# 32 | # Back Tracking line search Algorithm (BTLS) to find optimal # 33 | # value of alpha # 34 | ################################################################# 35 | Grad = -np.dot(Y-np.dot(D,X),X.T) 36 | oldfunc = np.linalg.norm(Y-np.dot(D,X),'fro')**2 + mu*np.linalg.norm(X,'fro')**2 37 | newfunc = np.linalg.norm(Y-np.dot(Dhat_RGDtemp,X),'fro')**2 + mu*np.linalg.norm(X,'fro')**2 38 | while(~(newfunc <= oldfunc-eta*alpha*np.sum(Grad**2))): 39 | alpha = beta*alpha 40 | Dhat_RGDtemp = deepcopy(D) 41 | Dhat_RGDtemp = Dhat_RGDtemp + alpha*np.dot(Y-np.dot(Dhat_RGDtemp,X),X.T) 42 | Dhat_RGDtemp = preprocessing.normalize(Dhat_RGDtemp,norm='l2',axis=0) 43 | newfunc = np.linalg.norm(Y-np.dot(Dhat_RGDtemp,X),'fro')**2 + mu*np.linalg.norm(X,'fro')**2 44 | if(alpha < 1e-9): 45 | break 46 | ################################################################# 47 | ################################################################# 48 | D = D + alpha*np.dot(Y-np.dot(D,X),X.T) 49 | D = preprocessing.normalize(D,norm='l2',axis=0) 50 | ########## Update X Considering same sparsity pattern ########### 51 | Omega = X!=0 52 | ColUpdate = np.sum(Omega,axis=0)!=0 53 | YI = deepcopy(Y[:,ColUpdate]) 54 | DI = deepcopy(D) 55 | OmegaI = deepcopy(Omega[:,ColUpdate]) 56 | OmegaL = np.sum(OmegaI,axis=0) 57 | mu_sqrt = np.sqrt(mu) 58 | 59 | for cn in range(YI.shape[1]): 60 | L = deepcopy(OmegaL[cn]) 61 | X[OmegaI[:,cn],cn] = np.linalg.lstsq(np.append(DI[:,OmegaI[:,cn]],\ 62 | np.diag(mu_sqrt*np.ones((L,))),axis=0),\ 63 | np.append(YI[:,cn],np.zeros((L,)),axis=0))[0] 64 | 65 | return D -------------------------------------------------------------------------------- /SimCo/DictUpdate03.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Jul 07 12:23:12 2017 4 | 5 | @author: Rehan Ahmad 6 | 7 | DictUpdate03 is the dictionary update function in SimCO. 8 | Given the initial dictionary D, initial sparse coefficient matrix X and 9 | the traning data matrix Y, this function produces the updated D and X 10 | through itN iterations of line search algorithm in DictLineSearch03 11 | 12 | References: 13 | W. Dai, T. Xu, and W. Wang, 14 | "Simultaneous Codeword Optimization (SimCO) for Dictionary Update and Learning," 15 | submitted to IEEE Transactions on Signal Processing, October 2011. 16 | Full text is available at http://arxiv.org/abs/1109.5302 17 | 18 | """ 19 | import numpy as np 20 | from copy import deepcopy 21 | from DictLineSearch03 import DictLineSearch03 22 | import pdb 23 | 24 | def DictUpdate03(Y, Dhat, Xhat, IPara): 25 | D = deepcopy(Dhat) 26 | X = deepcopy(Xhat) 27 | 28 | class OPara(): 29 | pass 30 | OPara = OPara() 31 | I = deepcopy(IPara.I) 32 | itN = deepcopy(IPara.itN) 33 | OPara.Flag = np.zeros((itN,)) 34 | OPara.f0 = np.zeros((itN,)) 35 | OPara.f1 = np.zeros((itN,)) 36 | OPara.f0real = np.zeros((itN,)) 37 | OPara.f1real = np.zeros((itN,)) 38 | OPara.gn2 = np.zeros((itN,)) 39 | OPara.topt = np.zeros((itN,)) 40 | d = X.shape[0] 41 | m = Y.shape[0] 42 | 43 | #Ic is the complementary set of I 44 | I = np.intersect1d(range(d),I) 45 | Ic = np.setdiff1d(range(d),I) 46 | Yp = Y - np.dot(D[:,Ic],X[Ic,:]) 47 | Omega = deepcopy(X!=0) 48 | ColUpdate = np.sum(Omega[I,:],axis=0)!=0 49 | YI = deepcopy(Yp[:,ColUpdate]) 50 | DI = deepcopy(D[:,I]) 51 | XI = deepcopy(X[:,ColUpdate][I,:]) # XI = X[I,ColUpdate] 52 | OmegaI = deepcopy(Omega[:,ColUpdate][I,:]) 53 | f_YIComp = np.linalg.norm(Yp[:,~ColUpdate],'fro')**2 54 | 55 | # gradient descent line search 56 | 57 | for itn in range(itN): 58 | if itn == 0: 59 | OPara.f0real[itn] = np.linalg.norm(Y-np.dot(D,X),'fro')**2 60 | else: 61 | OPara.f0real[itn] = OPara.f1real[itn-1] 62 | 63 | # use the line search mechanism for dictionary update 64 | DI,XI,OParaLS = DictLineSearch03(YI,DI,OmegaI,IPara) 65 | D[:,I] = deepcopy(DI) 66 | X[np.ix_(I,ColUpdate)] = deepcopy(XI) 67 | OPara.Flag[itn] = OParaLS.Flag 68 | OPara.f0[itn] = OParaLS.f0 + f_YIComp 69 | OPara.f1[itn] = OParaLS.f1 + f_YIComp 70 | OPara.f1real[itn] = np.linalg.norm(Y-np.dot(D,X),'fro')**2 71 | OPara.gn2[itn] = deepcopy(OParaLS.gn2) 72 | OPara.topt[itn] = deepcopy(OParaLS.topt) 73 | 74 | if OParaLS.Flag != 0: 75 | OPara.Flag = OPara.Flag[0:itn] 76 | OPara.f0 = OPara.f0[0:itn] 77 | OPara.f1 = OPara.f1[0:itn] 78 | OPara.f0real = OPara.f0real[0:itn] 79 | OPara.f1real = OPara.f1real[0:itn] 80 | OPara.gn2 = OPara.gn2[0:itn] 81 | OPara.topt = OPara.topt[0:itn] 82 | break; 83 | 84 | IPara.t4 = deepcopy(OParaLS.topt) 85 | 86 | # finalize 87 | D[:,I] = deepcopy(DI) 88 | X[np.ix_(I,ColUpdate)] = deepcopy(XI) 89 | 90 | return D,X,OPara 91 | 92 | -------------------------------------------------------------------------------- /KSVD/KSVD.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jun 12 23:23:38 2017 4 | 5 | @author: Rehan Ahmad 6 | 7 | K-SVD Algorithm for Dictionary Learning. 8 | 9 | Data (nxk) : Input Data / Signal. 10 | Dictionary (nxm): Dictionary as an input which will be updated by the algorithm. 11 | Dictionary columns must be normalized by l2 norm. 12 | CoefMat (mxk) : Coefficient Matrix as an input generated by sparse algorithm 13 | (OMP/BP/MP etc). 14 | 15 | This implementation is taken from matlab code KSVD.m by: 16 | "The K-SVD: An Algorithm for Designing of Overcomplete Dictionaries for Sparse 17 | Representation", written by M. Aharon, M. Elad, and A.M. Bruckstein and 18 | appeared in the IEEE Trans. On Signal Processing, Vol. 54, no. 11, 19 | pp. 4311-4322, November 2006. 20 | 21 | """ 22 | import numpy as np 23 | from scipy.sparse.linalg import svds 24 | from numpy.linalg import svd 25 | from copy import deepcopy 26 | import pdb 27 | 28 | def KSVD(Data, Dictionary, CoefMat): 29 | 30 | Dict = deepcopy(Dictionary) 31 | CoefMatrix = deepcopy(CoefMat) 32 | rPerm = np.random.permutation(Dict.shape[1]) 33 | 34 | for j in rPerm: 35 | # the data indices that uses the j'th dictionary element. 36 | relevantDataIndices = np.nonzero(CoefMatrix[j,:])[0] 37 | if (len(relevantDataIndices)<1): 38 | ErrorMat = Data-np.dot(Dict,CoefMatrix) 39 | ErrorNormVec = np.sum(ErrorMat**2, axis = 0) 40 | d,i = np.max(ErrorNormVec),np.argmax(ErrorNormVec) 41 | betterDictionaryElement = deepcopy(Data[:,i]) 42 | betterDictionaryElement = betterDictionaryElement/np.sqrt(\ 43 | np.dot(betterDictionaryElement.T,betterDictionaryElement)) 44 | betterDictionaryElement = betterDictionaryElement*\ 45 | np.sign(betterDictionaryElement[0]) 46 | CoefMatrix[j,:] = np.zeros((CoefMatrix.shape[1],)) 47 | Dict[:,j] = deepcopy(np.reshape(betterDictionaryElement,(-1,))) 48 | return Dict,CoefMatrix 49 | 50 | tmpCoefMatrix = deepcopy(CoefMatrix[:,relevantDataIndices]) 51 | # the coeffitients of the element we now improve are not relevant. 52 | tmpCoefMatrix[j,:] = np.zeros((tmpCoefMatrix.shape[1])) 53 | # vector of errors that we want to minimize with the new element 54 | errors = Data[:,relevantDataIndices] - np.dot(Dict,tmpCoefMatrix) 55 | # the better dictionary element and the values of beta are found using svd. 56 | # This is because we would like to minimize || errors - beta*element ||_F^2. 57 | # that is, to approximate the matrix 'errors' with a one-rank matrix. This 58 | # is done using the largest singular value. 59 | try: 60 | uu,ss,vv = svd(errors) 61 | betterDictionaryElement,singularValue,betaVector = uu[:,0],ss[0],vv[0,:] 62 | except: 63 | print "Error has occured in svd...You are now in pdb mode" 64 | pdb.set_trace() 65 | 66 | CoefMatrix[j,relevantDataIndices] = np.dot(singularValue,betaVector) 67 | Dict[:,j] = deepcopy(np.reshape(betterDictionaryElement,(-1,))) 68 | 69 | return Dict, CoefMatrix 70 | -------------------------------------------------------------------------------- /ImageDenoising/KSVD.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jun 12 23:23:38 2017 4 | 5 | @author: Rehan Ahmad 6 | 7 | K-SVD Algorithm for Dictionary Learning. 8 | 9 | Data (nxk) : Input Data / Signal. 10 | Dictionary (nxm): Dictionary as an input which will be updated by the algorithm. 11 | Dictionary columns must be normalized by l2 norm. 12 | CoefMat (mxk) : Coefficient Matrix as an input generated by sparse algorithm 13 | (OMP/BP/MP etc). 14 | 15 | This implementation is taken from matlab code KSVD.m by: 16 | "The K-SVD: An Algorithm for Designing of Overcomplete Dictionaries for Sparse 17 | Representation", written by M. Aharon, M. Elad, and A.M. Bruckstein and 18 | appeared in the IEEE Trans. On Signal Processing, Vol. 54, no. 11, 19 | pp. 4311-4322, November 2006. 20 | 21 | """ 22 | import numpy as np 23 | from scipy.sparse.linalg import svds 24 | from numpy.linalg import svd 25 | from copy import deepcopy 26 | import pdb 27 | 28 | def KSVD(Data, Dictionary, CoefMat): 29 | 30 | Dict = deepcopy(Dictionary) 31 | CoefMatrix = deepcopy(CoefMat) 32 | rPerm = np.random.permutation(Dict.shape[1]) 33 | 34 | for j in rPerm: 35 | # the data indices that uses the j'th dictionary element. 36 | relevantDataIndices = np.nonzero(CoefMatrix[j,:])[0] 37 | if (len(relevantDataIndices)<1): 38 | ErrorMat = Data-np.dot(Dict,CoefMatrix) 39 | ErrorNormVec = np.sum(ErrorMat**2, axis = 0) 40 | d,i = np.max(ErrorNormVec),np.argmax(ErrorNormVec) 41 | betterDictionaryElement = deepcopy(Data[:,i]) 42 | betterDictionaryElement = betterDictionaryElement/np.sqrt(\ 43 | np.dot(betterDictionaryElement.T,betterDictionaryElement)) 44 | betterDictionaryElement = betterDictionaryElement*\ 45 | np.sign(betterDictionaryElement[0]) 46 | CoefMatrix[j,:] = np.zeros((CoefMatrix.shape[1],)) 47 | Dict[:,j] = deepcopy(np.reshape(betterDictionaryElement,(-1,))) 48 | return Dict,CoefMatrix 49 | 50 | tmpCoefMatrix = deepcopy(CoefMatrix[:,relevantDataIndices]) 51 | # the coeffitients of the element we now improve are not relevant. 52 | tmpCoefMatrix[j,:] = np.zeros((tmpCoefMatrix.shape[1])) 53 | # vector of errors that we want to minimize with the new element 54 | errors = Data[:,relevantDataIndices] - np.dot(Dict,tmpCoefMatrix) 55 | # the better dictionary element and the values of beta are found using svd. 56 | # This is because we would like to minimize || errors - beta*element ||_F^2. 57 | # that is, to approximate the matrix 'errors' with a one-rank matrix. This 58 | # is done using the largest singular value. 59 | try: 60 | uu,ss,vv = svd(errors) 61 | betterDictionaryElement,singularValue,betaVector = uu[:,0],ss[0],vv[0,:] 62 | except: 63 | print "Error has occured in svd...You are now in pdb mode" 64 | pdb.set_trace() 65 | 66 | CoefMatrix[j,relevantDataIndices] = np.dot(singularValue,betaVector) 67 | Dict[:,j] = deepcopy(np.reshape(betterDictionaryElement,(-1,))) 68 | 69 | return Dict, CoefMatrix 70 | -------------------------------------------------------------------------------- /SimCo/DictLineSearch03.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Jul 07 15:10:42 2017 4 | 5 | @author: Rehan Ahmad 6 | 7 | DictUpdate03 is the dictionary update function in SimCO. 8 | Use line search mechanism to update the dictionary D. This is one 9 | iteration of the line search algorithm for dictionary update 10 | 11 | References: 12 | W. Dai, T. Xu, and W. Wang, 13 | "Simultaneous Codeword Optimization (SimCO) for Dictionary Update and Learning," 14 | submitted to IEEE Transactions on Signal Processing, October 2011. 15 | Full text is available at http://arxiv.org/abs/1109.5302 16 | 17 | """ 18 | import numpy as np 19 | from copy import deepcopy 20 | from fg_tilde_eval01 import fg_tilde_eval01 21 | 22 | def DictLineSearch03(Y,Dhat,Omega,IPara): 23 | D = deepcopy(Dhat) 24 | class OPara(): 25 | pass 26 | OPara = OPara() 27 | c = (np.sqrt(5)-1)/2.0 28 | fv = np.zeros((100,)) 29 | tv = np.zeros((100,)) 30 | f4v = np.zeros((4,)) 31 | t4v = np.zeros((4,)) 32 | m,n = Y.shape 33 | d = D.shape[1] 34 | 35 | gmin = IPara.gmin 36 | Lmin = IPara.Lmin 37 | rNmax = IPara.rNmax 38 | t4v[3] = IPara.t4 39 | 40 | # compute the direction and corresponding gradient 41 | f,X,g,_ = fg_tilde_eval01(Y,D,Omega,IPara) 42 | evaln = 0 43 | fv[evaln] = deepcopy(f) 44 | tv[evaln] = 0 45 | 46 | # look at the magnitude of the gradient 47 | OPara.gn2 = np.linalg.norm(g,'fro')/np.linalg.norm(Y,'fro')**2 48 | gColn2 = np.sqrt(np.sum(g*g,axis = 0)) 49 | gZero = gColn2 < gmin*np.linalg.norm(Y,'fro')**2/n 50 | # if the the magnitude of the gradient is less than the minimum threshold 51 | # value, then quit and return D and X 52 | if np.sum(gZero) == D.shape[1]: 53 | OPara.Flag = 1 54 | OPara.fv = fv[0:evaln] 55 | OPara.tv = tv[0:evaln] 56 | OPara.topt = 0 57 | OPara.f0 = deepcopy(f) 58 | OPara.f1 = deepcopy(f) 59 | return D,X,OPara 60 | 61 | gColn2[gZero] = 0 62 | H = np.zeros((m,d)) 63 | H[0,gZero] = 1 64 | H[1:m,gZero] = 0 65 | H[:,~gZero] = g[:,~gZero]*np.tile(-1/gColn2[~gZero],(m,1)) 66 | Step = gColn2/np.mean(gColn2) 67 | 68 | # Part A : find a good t4 69 | # set t4v and f4v; 70 | t4v[2] = t4v[3]*c 71 | t4v[1] = t4v[3]*(1-c) 72 | f4v[0] = fv[0] 73 | for evaln in range(1,4): 74 | t = t4v[evaln] 75 | Dt = D*np.tile(np.cos(Step*t),(m,1)) + H*np.tile(np.sin(Step*t),(m,1)) 76 | f4v[evaln],_,_,_ = fg_tilde_eval01(Y,Dt,Omega,IPara) 77 | 78 | fv[1:4] = f4v[1:4] 79 | tv[1:4] = t4v[1:4] 80 | # loop to find a good t4 81 | while t4v[3]-t4v[0] >= Lmin: 82 | # if f(D(t1)) is not greater than f(D(t2)), then t4=t2, t3=c*t4, 83 | # t2=(1-c)*t4 84 | if f4v[0] <= f4v[1]: 85 | t4v[3] = t4v[1] 86 | t4v[2] = t4v[3]*c 87 | t4v[1] = t4v[3]*(1-c) 88 | f4v[3] = f4v[1] 89 | evaln = evaln + 1 90 | t = t4v[1] 91 | tv[evaln] = t 92 | Dt = D*np.tile(np.cos(Step*t),(m,1)) + H*np.tile(np.sin(Step*t),(m,1)) 93 | ft,_,_,_ = fg_tilde_eval01(Y,Dt,Omega,IPara) 94 | f4v[1] = ft 95 | fv[evaln] = ft 96 | evaln = evaln + 1 97 | t = t4v[2] 98 | tv[evaln] = t 99 | Dt = D*np.tile(np.cos(Step*t),(m,1)) + H*np.tile(np.sin(Step*t),(m,1)) 100 | ft,_,_,_ = fg_tilde_eval01(Y,Dt,Omega,IPara) 101 | f4v[2] = ft 102 | fv[evaln] = ft 103 | 104 | # if f(D(t2)) is not greater than f(D(t3)), then t4=t3, t3=t2, 105 | # t2=(1-c)*t4 106 | elif f4v[1] <= f4v[2]: 107 | t4v[3] = t4v[2]; t4v[2] = t4v[1]; t4v[1] = t4v[3]*(1-c); 108 | f4v[3] = f4v[2]; f4v[2] = f4v[1] 109 | evaln = evaln + 1 110 | t = t4v[1]; tv[evaln] = t 111 | Dt = D*np.tile(np.cos(Step*t),(m,1)) + H*np.tile(np.sin(Step*t),(m,1)) 112 | ft,_,_,_ = fg_tilde_eval01(Y,Dt,Omega,IPara) 113 | f4v[1] = ft; fv[evaln] = ft; 114 | # if f(D(t3)) is greater than f(D(t4)), then t2=t3, t3=t4 115 | # t4=t3/c 116 | elif f4v[2]>f4v[3]: 117 | t4v[1] = t4v[2]; t4v[2] = t4v[3]; t4v[3] = t4v[2]/c; 118 | f4v[1] = f4v[2]; f4v[2] = f4v[3]; 119 | evaln = evaln + 1 120 | t = t4v[3]; tv[evaln] = t; 121 | Dt = D*np.tile(np.cos(Step*t),(m,1)) + H*np.tile(np.sin(Step*t),(m,1)) 122 | ft,_,_,_ = fg_tilde_eval01(Y,Dt,Omega,IPara); 123 | f4v[3] = ft; fv[evaln] = ft; 124 | else: 125 | #quit 126 | break 127 | 128 | # if t is too small, fet Flag to minus 1 129 | # if t4v[3]-t4v[0] < Lmin: 130 | # Flag = -1 131 | 132 | # Part B: refine the segment 133 | evalN = evaln 134 | # iterate until t4-t1 is small enough 135 | while (t4v[3]-t4v[0]) >= Lmin and evaln-evalN <= rNmax: 136 | # if f(D(t1))>f(D(t2))>f(D(t3)), then t1=t2, t2=t3, t3=t1+c*(t4-t1) 137 | if f4v[0]>f4v[1] and f4v[1]>f4v[2]: 138 | t4v[0] = t4v[1]; t4v[1] = t4v[2]; t4v[2] = t4v[0]+c*(t4v[3]-t4v[0]); 139 | f4v[0] = f4v[1]; f4v[1] = f4v[2] 140 | evaln = evaln + 1 141 | t = t4v[2]; tv[evaln] = t; 142 | Dt = D*np.tile(np.cos(Step*t),(m,1)) + H*np.tile(np.sin(Step*t),(m,1)) 143 | ft,_,_,_ = fg_tilde_eval01(Y,Dt,Omega,IPara); 144 | f4v[2] = ft; fv[evaln] = ft; 145 | # otherwise, t4=43, t3=t2, t2=t1+(1-c)(t4-t1) 146 | else: 147 | t4v[3] = t4v[2]; t4v[2] = t4v[1]; t4v[1] = t4v[0]+(1-c)*(t4v[3]-t4v[0]); 148 | f4v[3] = f4v[2]; f4v[2] = f4v[1]; 149 | evaln = evaln + 1; 150 | t = t4v[1]; tv[evaln] = t; 151 | Dt = D*np.tile(np.cos(Step*t),(m,1)) + H*np.tile(np.sin(Step*t),(m,1)) 152 | ft,_,_,_ = fg_tilde_eval01(Y,Dt,Omega,IPara) 153 | f4v[1] = ft; fv[evaln] = ft; 154 | 155 | # finalize 156 | fv = fv[0:evaln] 157 | tv = tv[0:evaln] 158 | findex = np.argmin(fv) 159 | t = tv[findex] 160 | D = D*np.tile(np.cos(Step*t),(m,1)) + H*np.tile(np.sin(Step*t),(m,1)) 161 | # compute X 162 | f,X,_,_ = fg_tilde_eval01(Y,D,Omega,IPara) 163 | 164 | OPara.f0 = fv[0] 165 | OPara.f1 = deepcopy(f) 166 | OPara.fv = deepcopy(fv) 167 | OPara.tv = deepcopy(tv) 168 | OPara.topt = deepcopy(t) 169 | OPara.Flag = 0 170 | 171 | return D,X,OPara 172 | -------------------------------------------------------------------------------- /ImageDenoising/DenoiseImage.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Aug 05 16:34:13 2017 4 | 5 | References: 6 | 1. W. Dai, T. Xu, and W. Wang, 7 | "Simultaneous Codeword Optimization (SimCO) for Dictionary Update and Learning," 8 | submitted to IEEE Transactions on Signal Processing, October 2011. 9 | Full text is available at http://arxiv.org/abs/1109.5302 10 | 11 | 2. The methods implemented here are the same one as described in 12 | "Image Denoising Via Sparse and Redundant representations over Learned Dictionaries", 13 | (IEEE Trans. on Image Processing, Vol. 15, no. 12, December 2006). 14 | 15 | @author: Rehan 16 | """ 17 | import cv2 18 | import numpy as np 19 | from RSimCo import RSimCo 20 | from PSimCo import PSimCo 21 | import matplotlib.pylab as plt 22 | from time import time 23 | from im2col import im2col 24 | from omperr import omperr 25 | from GDDL import GDDL 26 | from GDBTLS import GDBTLS 27 | from RGDBTLS import RGDBTLS 28 | from MODDL import MODDL 29 | from KSVDDL import KSVDDL 30 | 31 | def DenoiseImage(Image,Param,seed): 32 | NN1,NN2 = Image.shape 33 | C = 1.15 34 | bb = 8 35 | maxNumBlocksToTrainOn = 1000 36 | sigma = Param.noise 37 | K = Param.k 38 | class Par(): 39 | pass 40 | param=Par() 41 | param.K = K 42 | param.I = range(K) 43 | param.itN = 10 44 | param.errorGoal = sigma*C 45 | 46 | # first, train a dictionary on blocks from the noisy image 47 | if np.prod(np.array([NN1,NN2])-bb+1) > maxNumBlocksToTrainOn: 48 | np.random.seed(seed) 49 | randPermutation = np.random.permutation(np.prod(np.array([NN1,NN2])-bb+1)) 50 | selectedBlocks = randPermutation[0:maxNumBlocksToTrainOn] 51 | blkMatrix = np.zeros((bb**2,maxNumBlocksToTrainOn)) 52 | for i in range(maxNumBlocksToTrainOn): 53 | row,col = np.unravel_index(selectedBlocks[i],tuple(np.array(Image.shape)-bb+1),order='F') 54 | currBlock = Image[row:row+bb,col:col+bb] 55 | blkMatrix[:,i] = np.reshape(currBlock,(-1,),order='F') 56 | else: 57 | blkMatrix = im2col(Image,(bb,bb)) 58 | 59 | ######## Make initial dictionary from DCT ########### 60 | Pn = int(np.ceil(np.sqrt(K))) 61 | DCT = np.zeros((bb,Pn)) 62 | for k in range(Pn): 63 | V = np.cos(np.array(range(bb))*k*np.pi/Pn) 64 | if k > 0: 65 | V = V-np.mean(V) 66 | DCT[:,k] = V/np.linalg.norm(V) 67 | DCT = np.kron(DCT,DCT) 68 | ##################################################### 69 | param.initialDictionary = DCT[:,0:param.K] 70 | 71 | # reducedc 72 | vecOfMeans = np.mean(blkMatrix,axis=0) 73 | blkMatrix = blkMatrix-np.dot(np.ones((blkMatrix.shape[0],1)),np.reshape(vecOfMeans,(1,-1),order='F')) 74 | 75 | if(Param.method == 'RSimCo'): 76 | print 'Executing RSimCo...' 77 | Dictionary = RSimCo(blkMatrix,param) 78 | 79 | elif(Param.method == 'PSimCo'): 80 | print 'Executing PSimCo...' 81 | Dictionary = PSimCo(blkMatrix,param) 82 | 83 | elif(Param.method == 'GDDL'): 84 | print 'Executing GDDL...' 85 | param.MomentumGamma = 0.5 86 | param.alpha = 0.005 87 | Dictionary = GDDL(blkMatrix,param) 88 | 89 | elif(Param.method == 'GDBTLS'): 90 | print 'Executing GDBTLS...' 91 | param.alpha = 0.005 92 | Dictionary = GDBTLS(blkMatrix,param) 93 | 94 | elif(Param.method == 'RGDBTLS'): 95 | print 'Executing RGDBTLS...' 96 | param.alpha = 0.005 97 | param.mu = 0.05 98 | Dictionary = RGDBTLS(blkMatrix,param) 99 | 100 | elif(Param.method == 'MODDL'): 101 | print 'Executing MODDL...' 102 | Dictionary = MODDL(blkMatrix,param) 103 | 104 | elif(Param.method == 'KSVDDL'): 105 | print 'Executing KSVDDL...' 106 | Dictionary = KSVDDL(blkMatrix,param) 107 | else: 108 | raise('No Method Defined') 109 | 110 | #denoise the image using the resulted dictionary 111 | errT = sigma*C 112 | blocks = im2col(Image,(bb,bb)) 113 | idx = range(blocks.shape[1]) 114 | # go with jumps of 30000 115 | for jj in range(0,blocks.shape[1],30000): 116 | jumpSize = min(jj+30000,blocks.shape[1]) 117 | #reduceDC 118 | vecOfMeans = np.mean(blocks[:,jj:jumpSize],axis=0) 119 | blocks[:,jj:jumpSize] = blocks[:,jj:jumpSize] - np.tile(vecOfMeans,(blocks.shape[0],1)) 120 | Coefs = omperr(Dictionary,blocks[:,jj:jumpSize],errT) 121 | #reducedc 122 | blocks[:,jj:jumpSize] = np.dot(Dictionary,Coefs) + \ 123 | np.dot(np.ones((blocks.shape[0],1)),np.reshape(vecOfMeans,(1,-1),order='F')) 124 | 125 | count = 0 126 | Weight = np.zeros((NN1,NN2)) 127 | IMout = np.zeros((NN1,NN2)) 128 | rows,cols = np.unravel_index(idx,tuple(np.array(Image.shape)-bb+1),order='F') 129 | for i in range(len(cols)): 130 | col = cols[i] 131 | row = rows[i] 132 | block = np.reshape(blocks[:,count],(bb,bb),order='F') 133 | IMout[row:row+bb,col:col+bb] = IMout[row:row+bb,col:col+bb] + block 134 | Weight[row:row+bb,col:col+bb] = Weight[row:row+bb,col:col+bb] + np.ones((bb,bb)) 135 | count = count+1 136 | 137 | IOut = (Image+0.034*sigma*IMout)/(1.0+0.034*sigma*Weight) 138 | return IOut 139 | 140 | if __name__ == "__main__": 141 | tic = time() 142 | class Para(): 143 | pass 144 | Param = Para() 145 | Param.k = 256 #dictionary atoms 146 | Param.noise = 25 147 | Param.method = 'PSimCo' 148 | ImageName = 'camera.png' 149 | seed = 1 150 | 151 | Img = cv2.imread(ImageName,0) 152 | OriginalImage = np.float64(Img) 153 | np.random.seed(seed) 154 | NoisedImage = OriginalImage + Param.noise*np.random.standard_normal(OriginalImage.shape) 155 | 156 | # Denoise the corrupted image using learned dicitionary from corrupted image 157 | DenoisedImage = DenoiseImage(NoisedImage, Param,seed) 158 | 159 | NoisedPSNR = 20.0*np.log10(255.0/np.sqrt(np.mean((NoisedImage-OriginalImage)**2))) 160 | DenoisedPSNR = 20.0*np.log10(255.0/np.sqrt(np.mean((DenoisedImage-OriginalImage)**2))) 161 | 162 | # Display the results 163 | plt.close('all') 164 | plt.figure() 165 | plt.subplot(131); plt.imshow(OriginalImage,cmap='gray') 166 | plt.title('Original Image') 167 | plt.subplot(132); plt.imshow(NoisedImage,cmap='gray') 168 | plt.title('Noisy image with PSNR %f' %NoisedPSNR) 169 | plt.subplot(133); plt.imshow(DenoisedImage,cmap='gray'); 170 | plt.title('Denoised Image by trained dictionary PSNR %f' %DenoisedPSNR) 171 | print 'Total Time: %.2f min' %((time()-tic)/60.0) -------------------------------------------------------------------------------- /Dictionary_learning_v2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jun 05 23:31:51 2017 4 | 5 | @author: Rehan Ahmad 6 | 7 | Back Tracking Line Search taken from: 8 | http://users.ece.utexas.edu/~cmcaram/EE381V_2012F/Lecture_4_Scribe_Notes.final.pdf 9 | 10 | """ 11 | import numpy as np 12 | from sklearn import preprocessing 13 | import matplotlib.pylab as plt 14 | from copy import deepcopy 15 | import time 16 | from omp import omp 17 | from KSVD import KSVD 18 | from FindDistanceBetweenDictionaries import FindDistanceBetweenDictionaries 19 | from DictUpdate03 import DictUpdate03 20 | import pdb 21 | 22 | def awgn(x,snr_db): 23 | L = len(x) 24 | Es = np.sum(np.abs(x)**2)/L 25 | snr_lin = 10**(snr_db/10.0) 26 | noise = np.sqrt(Es/snr_lin)*np.random.randn(L) 27 | y = x + noise 28 | return y 29 | 30 | if __name__ == "__main__": 31 | tic = time.time() 32 | 33 | FlagPGD = True; FlagPGDMom = True; FlagMOD = False; FlagKSVD = True; 34 | FlagRSimCo = True; FlagPSimCo = True; FlagGDBTLS = True; FlagRGDBTLS = True 35 | 36 | drows = 16 #20 #16 37 | dcols = 32 #50 #32 38 | ycols = 78 #1500 #78 39 | alpha = 0.005 40 | 41 | iterations = 1000 42 | SNR = 20 43 | epochs = 1 44 | sparsity = 4 45 | 46 | count_success = np.ndarray((iterations,epochs)) 47 | count_success_momen = np.ndarray((iterations,epochs)) 48 | count_success_MOD = np.ndarray((iterations,epochs)) 49 | count_success_KSVD = np.ndarray((iterations,epochs)) 50 | count_success_RSimCo = np.ndarray((iterations,epochs)) 51 | count_success_PSimCo = np.ndarray((iterations,epochs)) 52 | count_success_GDBTLS = np.ndarray((iterations,epochs)) 53 | count_success_RGDBTLS = np.ndarray((iterations,epochs)) 54 | 55 | e = np.ndarray((iterations,epochs)) 56 | e_momen = np.ndarray((iterations,epochs)) 57 | e_GDBTLS = np.ndarray((iterations,epochs)) 58 | e_MOD = np.ndarray((iterations,epochs)) 59 | e_KSVD = np.ndarray((iterations,epochs)) 60 | e_RSimCo = np.ndarray((iterations,epochs)) 61 | e_PSimCo = np.ndarray((iterations,epochs)) 62 | e_RGDBTLS = np.ndarray((iterations,epochs)) 63 | 64 | for epoch in range(epochs): 65 | alpha = 0.005 66 | # np.random.seed(epoch) 67 | 68 | ################# make initial dictionary ############################# 69 | # Pn=ceil(sqrt(K)); 70 | # DCT=zeros(bb,Pn); 71 | # for k=0:1:Pn-1, 72 | # V=cos([0:1:bb-1]'*k*pi/Pn); 73 | # if k>0, V=V-mean(V); end; 74 | # DCT(:,k+1)=V/norm(V); 75 | # end; 76 | # DCT=kron(DCT,DCT); 77 | ###################################################################### 78 | 79 | # Creating dictionary from uniform iid random distribution 80 | # and normalizing atoms by l2-norm 81 | D = np.random.rand(drows,dcols) 82 | D = preprocessing.normalize(D,norm='l2',axis=0) 83 | # Creating data Y by linear combinations of randomly selected 84 | # atoms and iid uniform coefficients 85 | Y = np.ndarray((drows,ycols)) 86 | for i in range(ycols): 87 | PermIndx = np.random.permutation(dcols) 88 | Y[:,i] = np.random.rand()*D[:,PermIndx[0]] + \ 89 | np.random.rand()*D[:,PermIndx[1]] + \ 90 | np.random.rand()*D[:,PermIndx[2]] + \ 91 | np.random.rand()*D[:,PermIndx[3]] 92 | 93 | # Add awgn noise in data Y 94 | # for i in range(ycols): 95 | # Y[:,i] = awgn(Y[:,i],SNR) 96 | 97 | Dhat = np.ndarray((drows,dcols)) 98 | Dhat = deepcopy(Y[:,np.random.permutation(ycols)[0:dcols]]) 99 | Dhat = preprocessing.normalize(Dhat,norm='l2',axis=0) 100 | Dhat_momen = deepcopy(Dhat) 101 | Dhat_MOD = deepcopy(Dhat) 102 | Dhat_KSVD = deepcopy(Dhat) 103 | Dhat_RSimCo = deepcopy(Dhat) 104 | Dhat_PSimCo = deepcopy(Dhat) 105 | Dhat_GDBTLS = deepcopy(Dhat) 106 | Dhat_RGDBTLS = deepcopy(Dhat) 107 | 108 | ######################################################## 109 | # Applying Projected Gradient Descent without momentum # 110 | ######################################################## 111 | if(FlagPGD==True): 112 | X = omp(D,Y,sparsity) 113 | for j in range(iterations): 114 | # X = omp(Dhat,Y,sparsity) 115 | # for i in range(dcols): 116 | # R = Y-np.dot(Dhat,X) 117 | # Dhat[:,i] = Dhat[:,i] + alpha*np.dot(R,X[i,:]) 118 | Dhat = Dhat + alpha*np.dot(Y-np.dot(Dhat,X),X.T) #Parallel dictionary update... 119 | Dhat = preprocessing.normalize(Dhat,norm='l2',axis=0) 120 | 121 | e[j,epoch] = np.linalg.norm(Y-np.dot(Dhat,X),'fro')**2 122 | count = FindDistanceBetweenDictionaries(D,Dhat) 123 | count_success[j,epoch] = count 124 | ##################################################### 125 | # Applying Projected Gradient Descent with momentum # 126 | ##################################################### 127 | if(FlagPGDMom==True): 128 | v = np.zeros((drows,dcols)) 129 | gamma = 0.5 130 | X = omp(D,Y,sparsity) 131 | for j in range(iterations): 132 | # X = omp(Dhat_momen,Y,sparsity) 133 | # for i in range(dcols): 134 | # R = Y-np.dot(Dhat_momen,X) 135 | # v[:,i] = gamma*v[:,i] + alpha*np.dot(R,X[i,:]) 136 | # Dhat_momen[:,i] = Dhat_momen[:,i] + v[:,i] 137 | v = gamma*v - alpha*np.dot(Y-np.dot(Dhat_momen,X),X.T) 138 | Dhat_momen = Dhat_momen - v 139 | 140 | Dhat_momen = preprocessing.normalize(Dhat_momen,norm='l2',axis=0) 141 | e_momen[j,epoch] = np.linalg.norm(Y-np.dot(Dhat_momen,X),'fro')**2 142 | count_momen = FindDistanceBetweenDictionaries(D,Dhat_momen) 143 | count_success_momen[j,epoch] = count_momen 144 | ##################################################### 145 | # Applying Gradient Descent with back tracking line # 146 | # search algorithm # 147 | ##################################################### 148 | if(FlagGDBTLS==True): 149 | alpha = 1 150 | beta = np.random.rand() 151 | eta = np.random.rand()*0.5 152 | Grad = np.zeros((drows,dcols)) 153 | 154 | X = omp(D,Y,sparsity) 155 | for j in range(iterations): 156 | alpha = 1 157 | # X = omp(Dhat_GDBTLS,Y,sparsity) 158 | Dhat_GDtemp = deepcopy(Dhat_GDBTLS) 159 | 160 | ################################################################# 161 | # Back Tracking line search Algorithm (BTLS) to find optimal # 162 | # value of alpha # 163 | ################################################################# 164 | Grad = -np.dot(Y-np.dot(Dhat_GDBTLS,X),X.T) 165 | oldfunc = np.linalg.norm(Y-np.dot(Dhat_GDBTLS,X),'fro')**2 166 | newfunc = np.linalg.norm(Y-np.dot(Dhat_GDtemp,X),'fro')**2 167 | while(~(newfunc <= oldfunc-eta*alpha*np.sum(Grad**2))): 168 | alpha = beta*alpha 169 | Dhat_GDtemp = deepcopy(Dhat_GDBTLS) 170 | Dhat_GDtemp = Dhat_GDtemp + alpha*np.dot(Y-np.dot(Dhat_GDtemp,X),X.T) 171 | Dhat_GDtemp = preprocessing.normalize(Dhat_GDtemp,norm='l2',axis=0) 172 | newfunc = np.linalg.norm(Y-np.dot(Dhat_GDtemp,X),'fro')**2 173 | if(alpha < 1e-9): 174 | break 175 | ################################################################# 176 | ################################################################# 177 | Dhat_GDBTLS = Dhat_GDBTLS + alpha*np.dot(Y-np.dot(Dhat_GDBTLS,X),X.T) 178 | Dhat_GDBTLS = preprocessing.normalize(Dhat_GDBTLS,norm='l2',axis=0) 179 | 180 | e_GDBTLS[j,epoch] = np.linalg.norm(Y-np.dot(Dhat_GDBTLS,X),'fro')**2 181 | count_GDBTLS = FindDistanceBetweenDictionaries(D,Dhat_GDBTLS) 182 | count_success_GDBTLS[j,epoch] = count_GDBTLS 183 | 184 | ##################################################### 185 | # Applying Gradient Descent with back tracking line # 186 | # search algorithm with regularization on X # 187 | ##################################################### 188 | if(FlagRGDBTLS==True): 189 | alpha = 1 190 | mu = 0.01 191 | # beta = np.random.rand() 192 | # eta = np.random.rand()*0.5 193 | # Grad = np.zeros((drows,dcols)) 194 | # mu = 0.01 195 | 196 | X = omp(D,Y,sparsity) 197 | for j in range(iterations): 198 | alpha = 1 199 | # X = omp(Dhat_RGDBTLS,Y,sparsity) 200 | Dhat_RGDtemp = deepcopy(Dhat_RGDBTLS) 201 | 202 | ################################################################# 203 | # Back Tracking line search Algorithm (BTLS) to find optimal # 204 | # value of alpha # 205 | ################################################################# 206 | Grad = -np.dot(Y-np.dot(Dhat_RGDBTLS,X),X.T) 207 | oldfunc = np.linalg.norm(Y-np.dot(Dhat_RGDBTLS,X),'fro')**2 + mu*np.linalg.norm(X,'fro')**2 208 | newfunc = np.linalg.norm(Y-np.dot(Dhat_RGDtemp,X),'fro')**2 + mu*np.linalg.norm(X,'fro')**2 209 | while(~(newfunc <= oldfunc-eta*alpha*np.sum(Grad**2))): 210 | alpha = beta*alpha 211 | Dhat_RGDtemp = deepcopy(Dhat_RGDBTLS) 212 | Dhat_RGDtemp = Dhat_RGDtemp + alpha*np.dot(Y-np.dot(Dhat_RGDtemp,X),X.T) 213 | Dhat_RGDtemp = preprocessing.normalize(Dhat_RGDtemp,norm='l2',axis=0) 214 | newfunc = np.linalg.norm(Y-np.dot(Dhat_RGDtemp,X),'fro')**2 + mu*np.linalg.norm(X,'fro')**2 215 | if(alpha < 1e-9): 216 | break 217 | ################################################################# 218 | ################################################################# 219 | Dhat_RGDBTLS = Dhat_RGDBTLS + alpha*np.dot(Y-np.dot(Dhat_RGDBTLS,X),X.T) 220 | Dhat_RGDBTLS = preprocessing.normalize(Dhat_RGDBTLS,norm='l2',axis=0) 221 | ########## Update X Considering same sparsity pattern############ 222 | Omega = X!=0 223 | ColUpdate = np.sum(Omega,axis=0)!=0 224 | YI = deepcopy(Y[:,ColUpdate]) 225 | DI = deepcopy(Dhat_RGDBTLS) 226 | XI = deepcopy(X[:,ColUpdate]) 227 | OmegaI = deepcopy(Omega[:,ColUpdate]) 228 | OmegaL = np.sum(Omega,axis=0) 229 | mu_sqrt = np.sqrt(mu) 230 | 231 | for cn in range(ycols): 232 | L = deepcopy(OmegaL[cn]) 233 | X[OmegaI[:,cn],cn] = np.linalg.lstsq(np.append(DI[:,OmegaI[:,cn]],\ 234 | np.diag(mu_sqrt*np.ones((L,))),axis=0),\ 235 | np.append(YI[:,cn],np.zeros((L,)),axis=0))[0] 236 | ################################################################# 237 | e_RGDBTLS[j,epoch] = np.linalg.norm(Y-np.dot(Dhat_RGDBTLS,X),'fro')**2 238 | count_RGDBTLS = FindDistanceBetweenDictionaries(D,Dhat_RGDBTLS) 239 | count_success_RGDBTLS[j,epoch] = count_RGDBTLS 240 | ############################################ 241 | # Applying MOD Algorithm # 242 | ############################################ 243 | if(FlagMOD==True): 244 | X = omp(D,Y,sparsity) 245 | for j in range(iterations): 246 | # X = omp(Dhat_MOD,Y,sparsity) 247 | Dhat_MOD = np.dot(Y,np.linalg.pinv(X)) 248 | Dhat_MOD = preprocessing.normalize(Dhat_MOD,norm='l2',axis=0) 249 | 250 | count_MOD = FindDistanceBetweenDictionaries(D,Dhat_MOD) 251 | count_success_MOD[j,epoch] = count_MOD 252 | e_MOD[j,epoch] = np.linalg.norm(Y-np.dot(Dhat_MOD,X),'fro')**2 253 | ############################################ 254 | # Applying KSVD Algorithm # 255 | ############################################ 256 | if(FlagKSVD==True): 257 | X = omp(D,Y,sparsity) 258 | for j in range(iterations): 259 | # X = omp(Dhat_KSVD,Y,sparsity) 260 | Dhat_KSVD,X = KSVD(Y,Dhat_KSVD,X) 261 | 262 | count_KSVD = FindDistanceBetweenDictionaries(D,Dhat_KSVD) 263 | count_success_KSVD[j,epoch] = count_KSVD 264 | e_KSVD[j,epoch] = np.linalg.norm(Y-np.dot(Dhat_KSVD,X),'fro')**2 265 | 266 | ############################################# 267 | # Applying Regularized SimCo Algorithm # 268 | ############################################# 269 | if(FlagRSimCo==True): 270 | class IPara(): 271 | pass 272 | IPara = IPara() 273 | IPara.I = range(D.shape[1]) 274 | IPara.mu = 0.01 275 | IPara.dispN = 20 276 | IPara.DebugFlag = 0 277 | IPara.itN = 1 278 | IPara.gmin = 1e-5; # the minimum value of gradient 279 | IPara.Lmin = 1e-6; # t4-t1 should be larger than Lmin 280 | IPara.t4 = 1e-2; # the initial value of t4 281 | IPara.rNmax = 3; # the number of iterative refinement in Part B in DictLineSearch03.m 282 | 283 | X = omp(D,Y,sparsity) 284 | for j in range(iterations): 285 | # X = omp(Dhat_RSimCo,Y,sparsity) 286 | Dhat_RSimCo,X,_ = DictUpdate03(Y,Dhat_RSimCo,X,IPara) 287 | 288 | count_RSimCo = FindDistanceBetweenDictionaries(D,Dhat_RSimCo) 289 | count_success_RSimCo[j,epoch] = count_RSimCo 290 | e_RSimCo[j,epoch] = np.linalg.norm(Y-np.dot(Dhat_RSimCo,X),'fro')**2 291 | ############################################# 292 | # Applying Primitive SimCo Algorithm # 293 | ############################################# 294 | if(FlagPSimCo==True): 295 | IPara.mu = 0 296 | X = omp(D,Y,sparsity) 297 | for j in range(iterations): 298 | # X = omp(Dhat_PSimCo,Y,sparsity) 299 | Dhat_PSimCo,X,_ = DictUpdate03(Y,Dhat_PSimCo,X,IPara) 300 | 301 | count_PSimCo = FindDistanceBetweenDictionaries(D,Dhat_PSimCo) 302 | count_success_PSimCo[j,epoch] = count_PSimCo 303 | e_PSimCo[j,epoch] = np.linalg.norm(Y-np.dot(Dhat_PSimCo,X),'fro')**2 304 | ############################################# 305 | ############################################# 306 | print 'epoch: ',epoch,'completed' 307 | 308 | plt.close('all') 309 | if FlagPGD==True: plt.plot(np.sum(count_success,axis=1)/epochs,'b',label = 'PGD') 310 | if FlagPGDMom==True: plt.plot(np.sum(count_success_momen,axis=1)/epochs,'r',label = 'PGD_Momentum') 311 | if FlagMOD==True: plt.plot(np.sum(count_success_MOD,axis=1)/epochs,'g',label = 'MOD') 312 | if FlagKSVD==True: plt.plot(np.sum(count_success_KSVD,axis=1)/epochs,'y',label = 'KSVD') 313 | if FlagRSimCo==True: plt.plot(np.sum(count_success_RSimCo,axis=1)/epochs,'m',label = 'RSimCo') 314 | if FlagPSimCo==True: plt.plot(np.sum(count_success_PSimCo,axis=1)/epochs,'c',label = 'PSimCo') 315 | if FlagGDBTLS==True: plt.plot(np.sum(count_success_GDBTLS,axis=1)/epochs,':',label = 'GDBTLS') 316 | if FlagRGDBTLS==True: plt.plot(np.sum(count_success_RGDBTLS,axis=1)/epochs,'--',label = 'R_GDBTLS') 317 | 318 | plt.legend() 319 | plt.xlabel('iteration number') 320 | plt.ylabel('Success Counts in iteration') 321 | plt.title('Dictionary Learning Algorithms applied on Syhthetic data') 322 | 323 | plt.figure() 324 | if FlagPGD==True: plt.plot(np.sum(e,axis=1)/epochs,'b',label = 'PGD') 325 | if FlagPGDMom==True: plt.plot(np.sum(e_momen,axis=1)/epochs,'r',label = 'PGD_Momentum') 326 | if FlagMOD==True: plt.plot(np.sum(e_MOD,axis=1)/epochs,'g',label = 'MOD') 327 | if FlagKSVD==True: plt.plot(np.sum(e_KSVD,axis=1)/epochs,'y',label = 'KSVD') 328 | if FlagRSimCo==True: plt.plot(np.sum(e_RSimCo,axis=1)/epochs,'m',label = 'RSimCo') 329 | if FlagPSimCo==True: plt.plot(np.sum(e_PSimCo,axis=1)/epochs,'c',label = 'PSimCo') 330 | if FlagGDBTLS==True: plt.plot(np.sum(e_GDBTLS,axis=1)/epochs,':',label = 'GDBTLS') 331 | if FlagRGDBTLS==True: plt.plot(np.sum(e_RGDBTLS,axis=1)/epochs,'--',label = 'R_GDBTLS') 332 | 333 | plt.legend() 334 | plt.xlabel('iteration number') 335 | plt.ylabel('Error: Sum of squares') 336 | 337 | toc = time.time() 338 | print 'Total Time Taken by code: ','%.2f' %((toc-tic)/60.0),'min' 339 | --------------------------------------------------------------------------------