eari-package 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,9 @@
1
+ Metadata-Version: 2.4
2
+ Name: eari_package
3
+ Version: 0.1.0
4
+ Summary: EARI image demosaicking implementation
5
+ Requires-Python: >=3.8
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: numpy>=2.4.4
8
+ Requires-Dist: opencv-python>=4.13.0.92
9
+ Requires-Dist: scipy>=1.17.1
File without changes
@@ -0,0 +1,2 @@
1
+ from .eari import CEARI, MEARI
2
+ from .ri import ri, make_cfa
@@ -0,0 +1,141 @@
1
+ import numpy as np
2
+ import cv2
3
+ from fast_ri import guidedfilter, bilinear_RB, ri,standardize_input
4
+ def MEARI(MPFA):
5
+ eps=1e-32
6
+ Fn=np.array([[1,2,1],[1,2,1],[0,0,0]], dtype=np.float32)/8
7
+ Fs=np.array([[0,0,0],[1,2,1],[1,2,1]], dtype=np.float32)/8
8
+ Fw=Fn.T
9
+ Fe=Fs.T
10
+
11
+ Hn=np.array([[-1,2,-1],[1,-2,1],[0,0,0]], dtype=np.float32)/2
12
+ Hs=np.array([[0,0,0],[1,-2,1],[-1,2,-1]], dtype=np.float32)/2
13
+ Hw=Hn.T
14
+ He=Hs.T
15
+
16
+ Mn=np.ones((5,5), dtype=np.float32)/15
17
+ Mn[3:,:]=0
18
+ Ms=Mn[::-1,:]
19
+ Mw=Mn.T
20
+ Me=Ms.T
21
+
22
+ MPFA32 = standardize_input(MPFA)
23
+
24
+ Xn = cv2.filter2D(MPFA32, -1, Fn, borderType=cv2.BORDER_REFLECT_101)
25
+ Xs = cv2.filter2D(MPFA32, -1, Fs, borderType=cv2.BORDER_REFLECT_101)
26
+ Xe = cv2.filter2D(MPFA32, -1, Fe, borderType=cv2.BORDER_REFLECT_101)
27
+ Xw = cv2.filter2D(MPFA32, -1, Fw, borderType=cv2.BORDER_REFLECT_101)
28
+
29
+ Dn = cv2.filter2D(MPFA32, -1, Hn, borderType=cv2.BORDER_REFLECT_101)
30
+ Ds = cv2.filter2D(MPFA32, -1, Hs, borderType=cv2.BORDER_REFLECT_101)
31
+ De = cv2.filter2D(MPFA32, -1, He, borderType=cv2.BORDER_REFLECT_101)
32
+ Dw = cv2.filter2D(MPFA32, -1, Hw, borderType=cv2.BORDER_REFLECT_101)
33
+
34
+ sDn = cv2.filter2D(np.abs(Dn), -1, Mn, borderType=cv2.BORDER_REFLECT_101)
35
+ sDs = cv2.filter2D(np.abs(Ds), -1, Ms, borderType=cv2.BORDER_REFLECT_101)
36
+ sDe = cv2.filter2D(np.abs(De), -1, Me, borderType=cv2.BORDER_REFLECT_101)
37
+ sDw = cv2.filter2D(np.abs(Dw), -1, Mw, borderType=cv2.BORDER_REFLECT_101)
38
+
39
+ Wn = 1 / (sDn + eps)
40
+ Ws = 1 / (sDs + eps)
41
+ We = 1 / (sDe + eps)
42
+ Ww = 1 / (sDw + eps)
43
+
44
+ W = Wn + Ws + We + Ww
45
+
46
+ G = (Wn*Xn + Ws*Xs + We*Xe + Ww*Xw) / W
47
+
48
+ M = np.zeros_like(MPFA)
49
+ M90 = np.copy(M)
50
+ M00 = np.copy(M)
51
+ M45 = np.copy(M)
52
+ M135 = np.copy(M)
53
+
54
+ M90[0::2,0::2] = 1
55
+ M00[1::2,1::2] = 1
56
+ M45[0::2,1::2] = 1
57
+ M135[1::2,0::2] = 1
58
+
59
+ I90_sparse = np.copy(M)
60
+ I00_sparse = np.copy(M)
61
+ I45_sparse = np.copy(M)
62
+ I135_sparse = np.copy(M)
63
+
64
+ I90_sparse[0::2,0::2] = MPFA[0::2,0::2]
65
+ I00_sparse[1::2,1::2] = MPFA[1::2,1::2]
66
+ I45_sparse[0::2,1::2] = MPFA[0::2,1::2]
67
+ I135_sparse[1::2,0::2] = MPFA[1::2,0::2]
68
+
69
+ I90_t = guidedfilter(G, I90_sparse, M90, 5, 5)
70
+ I00_t = guidedfilter(G, I00_sparse, M00, 5, 5)
71
+ I45_t = guidedfilter(G, I45_sparse, M45, 5, 5)
72
+ I135_t = guidedfilter(G, I135_sparse, M135, 5, 5)
73
+
74
+ I90_res = M90 * (MPFA - I90_t)
75
+ I00_res = M00 * (MPFA - I00_t)
76
+ I45_res = M45 * (MPFA - I45_t)
77
+ I135_res = M135 * (MPFA - I135_t)
78
+
79
+ I90 = bilinear_RB(I90_res) + I90_t
80
+ I00 = bilinear_RB(I00_res) + I00_t
81
+ I45 = bilinear_RB(I45_res) + I45_t
82
+ I135 = bilinear_RB(I135_res) + I135_t
83
+
84
+ return I90, I00, I45, I135
85
+
86
+ def make_mpfa(rgb90,rgb00,rgb45,rgb135):
87
+ R90,G90,B90=rgb90[:,:,0],rgb90[:,:,1],rgb90[:,:,2]
88
+ R00,G00,B00=rgb00[:,:,0],rgb00[:,:,1],rgb00[:,:,2]
89
+ R45,G45,B45=rgb45[:,:,0],rgb45[:,:,1],rgb45[:,:,2]
90
+ R135,G135,B135=rgb135[:,:,0],rgb135[:,:,1],rgb135[:,:,2]
91
+ x,y=rgb90.shape[:2]
92
+
93
+ RMPFA=np.empty((2*x,2*y), dtype=np.float32)
94
+ GMPFA=np.empty((2*x,2*y), dtype=np.float32)
95
+ BMPFA=np.empty((2*x,2*y), dtype=np.float32)
96
+
97
+ RMPFA[::2,::2]=R90
98
+ RMPFA[1::2,1::2]=R00
99
+ RMPFA[0::2,1::2]=R45
100
+ RMPFA[1::2,0::2]=R135
101
+
102
+ GMPFA[::2,::2]=G90
103
+ GMPFA[1::2,1::2]=G00
104
+ GMPFA[0::2,1::2]=G45
105
+ GMPFA[1::2,0::2]=G135
106
+
107
+ BMPFA[::2,::2]=B90
108
+ BMPFA[1::2,1::2]=B00
109
+ BMPFA[0::2,1::2]=B45
110
+ BMPFA[1::2,0::2]=B135
111
+
112
+ return RMPFA,GMPFA,BMPFA
113
+
114
+ def CEARI(CPFA,pattern):
115
+ CPFA_standard = standardize_input(CPFA)
116
+ cfa90=CPFA_standard[0::2,0::2]
117
+ cfa00=CPFA_standard[1::2,1::2]
118
+ cfa45=CPFA_standard[0::2,1::2]
119
+ cfa135=CPFA_standard[1::2,0::2]
120
+ #if speed is importnat you can use a faster color demosaicking algo
121
+ rgb90=ri(cfa90,pattern)
122
+ rgb00=ri(cfa00,pattern)
123
+ rgb45=ri(cfa45,pattern)
124
+ rgb135=ri(cfa135,pattern)
125
+
126
+ RMPFA,GMPFA,BMPFA=make_mpfa(rgb90,rgb00,rgb45,rgb135)
127
+
128
+ R90,R00,R45,R135=MEARI(RMPFA)
129
+ G90,G00,G45,G135=MEARI(GMPFA)
130
+ B90,B00,B45,B135=MEARI(BMPFA)
131
+
132
+ RGB90=np.stack((R90,G90,B90),axis=-1)
133
+ RGB00=np.stack((R00,G00,B00),axis=-1)
134
+ RGB45=np.stack((R45,G45,B45),axis=-1)
135
+ RGB135=np.stack((R135,G135,B135),axis=-1)
136
+
137
+ RGB90=np.clip(RGB90,0,1)
138
+ RGB00=np.clip(RGB00,0,1)
139
+ RGB45=np.clip(RGB45,0,1)
140
+ RGB135=np.clip(RGB135,0,1)
141
+ return RGB90,RGB00,RGB45,RGB135
@@ -0,0 +1,231 @@
1
+ import numpy as np
2
+ from scipy import ndimage as nd
3
+ import cv2
4
+
5
+ offsets = {
6
+ 'RGGB': {'R': (0,0), 'Gr': (0,1), 'Gb': (1,0), 'B': (1,1)},
7
+ 'BGGR': {'R': (1,1), 'Gr': (1,0), 'Gb': (0,1), 'B': (0,0)},
8
+ 'GRBG': {'R': (0,1), 'Gr': (0,0), 'Gb': (1,1), 'B': (1,0)},
9
+ 'GBRG': {'R': (1,0), 'Gr': (1,1), 'Gb': (0,0), 'B': (0,1)},
10
+ }
11
+ def standardize_input(img):
12
+ if img.dtype == np.uint8:
13
+ return img.astype(np.float32) / 255.0
14
+ elif img.dtype == np.float32:
15
+ return img
16
+ else:
17
+ return img.astype(np.float32)
18
+
19
+ def getRGB_known(cfa,pattern):
20
+ R=np.zeros(cfa.shape, dtype=np.float32)
21
+ G=np.copy(R)
22
+ B=np.copy(R)
23
+ channels={'R':R,'G':G,'B':B}
24
+ for ch, (r0, c0) in offsets[pattern].items():
25
+ channels[ch[0]][r0::2,c0::2]=cfa[r0::2,c0::2]
26
+ return R,G,B
27
+
28
+ def make_cfa(img_path, pattern):
29
+ channel={'B':0,'Gr':1,'Gb':1,'R':2}
30
+ bgr_img=cv2.imread(img_path).astype(np.float32)/255.0
31
+ cfa=np.zeros(bgr_img.shape[:2], dtype=np.float32)
32
+ for ch, (r0, c0) in offsets[pattern].items():
33
+ cfa[r0::2,c0::2]=bgr_img[r0::2,c0::2,channel[ch]]
34
+ return cfa
35
+
36
+ def bilinear_1D(C_known_h,C_known_v,channel,pattern):
37
+ C_h=np.zeros_like(C_known_h)
38
+ C_v=np.zeros_like(C_known_v)
39
+ r,c=offsets[pattern][channel]
40
+
41
+ kernel_h = np.array([[0.5,1.0,0.5]], dtype=np.float32)
42
+ kernel_v=kernel_h.T
43
+
44
+ C_h[r::2,:]=cv2.filter2D(C_known_h[r::2,:].astype(np.float32), -1, kernel_h, borderType=cv2.BORDER_REFLECT_101)
45
+ C_v[:,c::2]=cv2.filter2D(C_known_v[:,c::2].astype(np.float32), -1, kernel_v, borderType=cv2.BORDER_REFLECT_101)
46
+
47
+ return C_h,C_v
48
+
49
+ def get_residuals(C_known,C_tent,channel,pattern):
50
+ r,c=offsets[pattern][channel]
51
+ C_res=np.zeros(C_known.shape, dtype=np.float32)
52
+ C_res[r::2,c::2]=C_known[r::2,c::2]-C_tent[r::2,c::2]
53
+ return C_res
54
+
55
+ def overlap(C,channel,pattern):
56
+ r,c=offsets[pattern][channel]
57
+ C_overlap=np.zeros(C.shape, dtype=np.float32)
58
+ C_overlap[r::2,c::2]=C[r::2,c::2]
59
+ return C_overlap
60
+
61
+ def half_gaussian_kernel(size, sigma):
62
+ x = np.arange(size)
63
+ kernel = np.exp(-x**2 / (2 * sigma**2))
64
+ return kernel / kernel.sum()
65
+
66
+ def get_delta(delta_h,delta_v,channel,pattern,N=5,sigma=1e+8):
67
+ delta_h_pad=np.pad(delta_h,((0,0),(1,1)),mode='symmetric')
68
+ delta_v_pad=np.pad(delta_v,((1,1),(0,0)),mode='symmetric')
69
+
70
+ Dh=np.abs(delta_h_pad[:,:-2]-delta_h_pad[:,2:])
71
+ Dv=np.abs(delta_v_pad[:-2,:]-delta_v_pad[2:,:])
72
+ eps=1e-5
73
+
74
+ wn = 1/(nd.uniform_filter(Dv, size=N, mode='mirror', origin=(2, 0))+eps)**2
75
+ ws = 1/(nd.uniform_filter(Dv, size=N, mode='mirror', origin=(-2, 0))+eps)**2
76
+ ww = 1/(nd.uniform_filter(Dh, size=N, mode='mirror', origin=(0, 2))+eps)**2
77
+ we = 1/(nd.uniform_filter(Dh, size=N, mode='mirror', origin=(0, -2))+eps)**2
78
+
79
+ wt=wn+ws+we+ww
80
+ f=half_gaussian_kernel(N, sigma)
81
+
82
+ delta_s=ws*nd.correlate1d(delta_v,f,origin=-2,axis=0,mode='mirror')
83
+ delta_e=we*nd.correlate1d(delta_h,f,origin=-2,axis=1,mode='mirror')
84
+ delta_w=ww*nd.correlate1d(delta_h,f[::-1],origin=2,axis=1,mode='mirror')
85
+ delta_n=wn*nd.correlate1d(delta_v,f[::-1],origin=2,axis=0,mode='mirror')
86
+
87
+ delta=np.zeros(delta_h.shape, dtype=np.float32)
88
+ r,c=offsets[pattern][channel]
89
+ delta[r::2,c::2]=((delta_n+delta_s+delta_e+delta_w)/wt)[r::2,c::2]
90
+
91
+ return delta
92
+
93
+ def guidedfilter(I,p,M,h,v,eps=0):
94
+ size_v=2*v+1 if v!=0 else 1
95
+ size_h=2*h+1 if h!=0 else 1
96
+
97
+ I32 = I.astype(np.float32, copy=False)
98
+ p32 = p.astype(np.float32, copy=False)
99
+ M32 = M.astype(np.float32, copy=False)
100
+
101
+ def boxfilter(img):
102
+ return cv2.boxFilter(img, cv2.CV_32F, (size_h, size_v), normalize=False, borderType=cv2.BORDER_REFLECT_101)
103
+
104
+ N = boxfilter(M32)
105
+ N[N==0]=1
106
+ N2 = boxfilter(np.ones_like(I32))
107
+
108
+ mean_I = boxfilter(I32*M32)/N
109
+ mean_p = boxfilter(p32)/N
110
+ mean_Ip = boxfilter(I32*p32)/N
111
+
112
+ cov_Ip = mean_Ip - mean_I*mean_p
113
+
114
+ mean_II = boxfilter(I32*I32*M32)/N
115
+ var_I = mean_II - mean_I*mean_I
116
+
117
+ th=1e-5
118
+ var_I = np.maximum(var_I, th)
119
+
120
+ a = cov_Ip/(var_I+eps)
121
+ b = mean_p - a*mean_I
122
+
123
+ mean_a = boxfilter(a)/N2
124
+ mean_b = boxfilter(b)/N2
125
+
126
+ q = mean_a*I32 + mean_b
127
+ return q
128
+
129
+ def guided_1D(G_d,C_known,d,channel,pattern,ep=0):
130
+ r,c=offsets[pattern][channel]
131
+ M=np.zeros_like(C_known)
132
+ M[r::2,c::2]=1.0
133
+ p=C_known*M
134
+
135
+ h=5 if d==1 else 0
136
+ v=5 if d==0 else 0
137
+
138
+ C_tent=guidedfilter(G_d,p,M,h,v,ep)
139
+
140
+ C_d=np.zeros_like(G_d)
141
+ if d==0:
142
+ C_d[:,c::2]=C_tent[:,c::2]
143
+ elif d==1:
144
+ C_d[r::2,:]=C_tent[r::2,:]
145
+
146
+ return C_d
147
+
148
+ def guided_upsampling(C,G,channel,pattern,ep=0):
149
+ r,c=offsets[pattern][channel]
150
+ M=np.zeros_like(C)
151
+ M[r::2,c::2]=1.0
152
+ p=C*M
153
+ return guidedfilter(G,p,M,5,5,ep)
154
+
155
+ def bilinear_RB(C):
156
+ kernel=np.array([[.25,.5,.25],
157
+ [.5,1,.5],
158
+ [.25,.5,.25]], dtype=np.float32)
159
+ return cv2.filter2D(C.astype(np.float32), -1, kernel, borderType=cv2.BORDER_REFLECT_101)
160
+
161
+ def ri(cfa,pattern):
162
+ cfa_standard = standardize_input(cfa)
163
+ R,G,B=getRGB_known(cfa_standard, pattern)
164
+
165
+ Grh_lin,Grv_lin=bilinear_1D(G,G,'R',pattern)
166
+ Rgh_lin,Rgv_lin=bilinear_1D(R,R,'R',pattern)
167
+ Gbh_lin,Gbv_lin=bilinear_1D(G,G,'B',pattern)
168
+ Bgh_lin,Bgv_lin=bilinear_1D(B,B,'B',pattern)
169
+
170
+ Rh_tent=guided_1D(Grh_lin,R,1,'R',pattern)
171
+ Rv_tent=guided_1D(Grv_lin,R,0,'R',pattern)
172
+ Grh_tent=guided_1D(Rgh_lin,G,1,'Gr',pattern)
173
+ Grv_tent=guided_1D(Rgv_lin,G,0,'Gb',pattern)
174
+ Bh_tent=guided_1D(Gbh_lin,B,1,'B',pattern)
175
+ Bv_tent=guided_1D(Gbv_lin,B,0,'B',pattern)
176
+ Gbh_tent=guided_1D(Bgh_lin,G,1,'Gb',pattern)
177
+ Gbv_tent=guided_1D(Bgv_lin,G,0,'Gr',pattern)
178
+
179
+ Rh_res=get_residuals(R,Rh_tent,'R',pattern)
180
+ Rv_res=get_residuals(R,Rv_tent,'R',pattern)
181
+ Grh_res=get_residuals(G,Grh_tent,'Gr',pattern)
182
+ Grv_res=get_residuals(G,Grv_tent,'Gb',pattern)
183
+ Bh_res=get_residuals(B,Bh_tent,'B',pattern)
184
+ Bv_res=get_residuals(B,Bv_tent,'B',pattern)
185
+ Gbh_res=get_residuals(G,Gbh_tent,'Gb',pattern)
186
+ Gbv_res=get_residuals(G,Gbv_tent,'Gr',pattern)
187
+
188
+ Rh_resin,Rv_resin=bilinear_1D(Rh_res,Rv_res,'R',pattern)
189
+ Grh_resin,Grv_resin=bilinear_1D(Grh_res,Grv_res,'R',pattern)
190
+ Bh_resin,Bv_resin=bilinear_1D(Bh_res,Bv_res,'B',pattern)
191
+ Gbh_resin,Gbv_resin=bilinear_1D(Gbh_res,Gbv_res,'B',pattern)
192
+
193
+ Rh=Rh_resin+Rh_tent
194
+ Rv=Rv_resin+Rv_tent
195
+ Grh=Grh_resin+Grh_tent
196
+ Grv=Grv_resin+Grv_tent
197
+ Bh=Bh_resin+Bh_tent
198
+ Bv=Bv_resin+Bv_tent
199
+ Gbh=Gbh_resin+Gbh_tent
200
+ Gbv=Gbv_resin+Gbv_tent
201
+
202
+ delta_gr_h=overlap(Grh,'R',pattern)-R+overlap(G,'Gr',pattern)-overlap(Rh,'Gr',pattern)
203
+ delta_gr_v=overlap(Grv,'R',pattern)-R+overlap(G,'Gb',pattern)-overlap(Rv,'Gb',pattern)
204
+ delta_gb_h=overlap(Gbh,'B',pattern)-B+overlap(G,'Gb',pattern)-overlap(Bh,'Gb',pattern)
205
+ delta_gb_v=overlap(Gbv,'B',pattern)-B+overlap(G,'Gr',pattern)-overlap(Bv,'Gr',pattern)
206
+
207
+ delta_gr=get_delta(delta_gr_h,delta_gr_v,'R',pattern)
208
+ delta_gb=get_delta(delta_gb_h,delta_gb_v,'B',pattern)
209
+
210
+ G_demosaicked=(R+delta_gr)+(B+delta_gb)+G
211
+ G_demosaicked = np.clip(G_demosaicked, 0, 1)
212
+
213
+ R_tent=guided_upsampling(R,G_demosaicked,'R',pattern)
214
+ B_tent=guided_upsampling(B,G_demosaicked,'B',pattern)
215
+
216
+ R_res=R-overlap(R_tent,'R',pattern)
217
+ B_res=B-overlap(B_tent,'B',pattern)
218
+
219
+ R_resin=bilinear_RB(R_res)
220
+ B_resin=bilinear_RB(B_res)
221
+
222
+ R_demosaicked=R_resin+R_tent
223
+ R_demosaicked=np.clip(R_demosaicked,0,1)
224
+
225
+ B_demosaicked=B_resin+B_tent
226
+ B_demosaicked=np.clip(B_demosaicked,0,1)
227
+
228
+ color_image=np.stack((R_demosaicked,G_demosaicked,B_demosaicked),axis=-1)
229
+
230
+ return color_image
231
+
@@ -0,0 +1,9 @@
1
+ Metadata-Version: 2.4
2
+ Name: eari_package
3
+ Version: 0.1.0
4
+ Summary: EARI image demosaicking implementation
5
+ Requires-Python: >=3.8
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: numpy>=2.4.4
8
+ Requires-Dist: opencv-python>=4.13.0.92
9
+ Requires-Dist: scipy>=1.17.1
@@ -0,0 +1,10 @@
1
+ README.md
2
+ pyproject.toml
3
+ eari_package/__init__.py
4
+ eari_package/eari.py
5
+ eari_package/ri.py
6
+ eari_package.egg-info/PKG-INFO
7
+ eari_package.egg-info/SOURCES.txt
8
+ eari_package.egg-info/dependency_links.txt
9
+ eari_package.egg-info/requires.txt
10
+ eari_package.egg-info/top_level.txt
@@ -0,0 +1,3 @@
1
+ numpy>=2.4.4
2
+ opencv-python>=4.13.0.92
3
+ scipy>=1.17.1
@@ -0,0 +1 @@
1
+ eari_package
@@ -0,0 +1,15 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "eari_package"
7
+ version = "0.1.0"
8
+ description = "EARI image demosaicking implementation"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ dependencies = [
12
+ "numpy>=2.4.4",
13
+ "opencv-python>=4.13.0.92",
14
+ "scipy>=1.17.1"
15
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+