Spaces:
No application file
No application file
Commit
·
0518690
1
Parent(s):
75e08cf
commit tạm
Browse files- api.py +17 -8
- common/config.yaml +1 -355
api.py
CHANGED
@@ -16,18 +16,21 @@ from common.utils import (
|
|
16 |
|
17 |
app = Flask(__name__)
|
18 |
|
19 |
-
# Load configuration and matcher
|
20 |
config = load_config(ROOT / "common/config.yaml")
|
21 |
matcher_zoo_restored = get_matcher_zoo(config["matcher_zoo"])
|
22 |
-
|
23 |
-
|
|
|
|
|
|
|
24 |
|
25 |
# Set up logging
|
26 |
logging.basicConfig(level=logging.INFO)
|
27 |
logger = logging.getLogger(__name__)
|
28 |
|
29 |
-
def match_images(img_path1, img_path2):
|
30 |
-
logger.info(f"Matching images with {DEVICE}")
|
31 |
try:
|
32 |
# Read the images using OpenCV
|
33 |
image0 = cv2.imread(img_path1)
|
@@ -40,7 +43,7 @@ def match_images(img_path1, img_path2):
|
|
40 |
image0 = cv2.cvtColor(image0, cv2.COLOR_BGR2RGB)
|
41 |
image1 = cv2.cvtColor(image1, cv2.COLOR_BGR2RGB)
|
42 |
|
43 |
-
results =
|
44 |
|
45 |
return {
|
46 |
"num_keypoints0": len(results["keypoints0_orig"]),
|
@@ -52,7 +55,7 @@ def match_images(img_path1, img_path2):
|
|
52 |
"matches0": results["mkeypoints0_orig"].tolist(),
|
53 |
"matches1": results["mmkeypoints0_orig"].tolist(),
|
54 |
"inliers0": results["mmkeypoints0_orig"].tolist(),
|
55 |
-
"inliers1": results["
|
56 |
"confidence": results["mconf"].tolist(),
|
57 |
"inlier_confidence": results["mmconf"].tolist(),
|
58 |
}
|
@@ -73,6 +76,7 @@ def match_images_endpoint():
|
|
73 |
|
74 |
img1_file = request.files['img1']
|
75 |
img2_file = request.files['img2']
|
|
|
76 |
|
77 |
# Validate file types
|
78 |
valid_extensions = {'png', 'jpg', 'jpeg', 'bmp', 'tiff'}
|
@@ -88,8 +92,13 @@ def match_images_endpoint():
|
|
88 |
img1_file.save(img_path1)
|
89 |
img2_file.save(img_path2)
|
90 |
|
|
|
|
|
|
|
|
|
|
|
91 |
# Run the matching task synchronously
|
92 |
-
result = match_images(img_path1, img_path2)
|
93 |
except Exception as e:
|
94 |
logger.error("Error processing images: %s", str(e))
|
95 |
return jsonify({"error": str(e)}), 500
|
|
|
16 |
|
17 |
app = Flask(__name__)
|
18 |
|
19 |
+
# Load configuration and matcher zoo
|
20 |
config = load_config(ROOT / "common/config.yaml")
|
21 |
matcher_zoo_restored = get_matcher_zoo(config["matcher_zoo"])
|
22 |
+
|
23 |
+
# Initialize API instances for each model
|
24 |
+
api_instances = {}
|
25 |
+
for model_name, conf in matcher_zoo_restored.items():
|
26 |
+
api_instances[model_name] = ImageMatchingAPI(conf=conf, device=DEVICE)
|
27 |
|
28 |
# Set up logging
|
29 |
logging.basicConfig(level=logging.INFO)
|
30 |
logger = logging.getLogger(__name__)
|
31 |
|
32 |
+
def match_images(img_path1, img_path2, api_instance):
|
33 |
+
logger.info(f"Matching images with {DEVICE} using provided API instance")
|
34 |
try:
|
35 |
# Read the images using OpenCV
|
36 |
image0 = cv2.imread(img_path1)
|
|
|
43 |
image0 = cv2.cvtColor(image0, cv2.COLOR_BGR2RGB)
|
44 |
image1 = cv2.cvtColor(image1, cv2.COLOR_BGR2RGB)
|
45 |
|
46 |
+
results = api_instance(image0, image1)
|
47 |
|
48 |
return {
|
49 |
"num_keypoints0": len(results["keypoints0_orig"]),
|
|
|
55 |
"matches0": results["mkeypoints0_orig"].tolist(),
|
56 |
"matches1": results["mmkeypoints0_orig"].tolist(),
|
57 |
"inliers0": results["mmkeypoints0_orig"].tolist(),
|
58 |
+
"inliers1": results["mmkeypoints0_orig"].tolist(),
|
59 |
"confidence": results["mconf"].tolist(),
|
60 |
"inlier_confidence": results["mmconf"].tolist(),
|
61 |
}
|
|
|
76 |
|
77 |
img1_file = request.files['img1']
|
78 |
img2_file = request.files['img2']
|
79 |
+
model_name = request.form.get('model', 'superpoint+superglue')
|
80 |
|
81 |
# Validate file types
|
82 |
valid_extensions = {'png', 'jpg', 'jpeg', 'bmp', 'tiff'}
|
|
|
92 |
img1_file.save(img_path1)
|
93 |
img2_file.save(img_path2)
|
94 |
|
95 |
+
# Get the pre-initialized API instance for the selected model
|
96 |
+
api_instance = api_instances.get(model_name)
|
97 |
+
if not api_instance:
|
98 |
+
raise ValueError(f"Model '{model_name}' not found in matcher zoo.")
|
99 |
+
|
100 |
# Run the matching task synchronously
|
101 |
+
result = match_images(img_path1, img_path2, api_instance)
|
102 |
except Exception as e:
|
103 |
logger.error("Error processing images: %s", str(e))
|
104 |
return jsonify({"error": str(e)}), 500
|
common/config.yaml
CHANGED
@@ -27,123 +27,6 @@ matcher_zoo:
|
|
27 |
paper: https://arxiv.org/abs/2405.12979
|
28 |
project: https://hwjiang1510.github.io/OmniGlue
|
29 |
display: true
|
30 |
-
DUSt3R:
|
31 |
-
# TODO: duster is under development
|
32 |
-
enable: true
|
33 |
-
matcher: duster
|
34 |
-
dense: true
|
35 |
-
info:
|
36 |
-
name: DUSt3R #dispaly name
|
37 |
-
source: "CVPR 2024"
|
38 |
-
github: https://github.com/naver/dust3r
|
39 |
-
paper: https://arxiv.org/abs/2312.14132
|
40 |
-
project: https://dust3r.europe.naverlabs.com
|
41 |
-
display: true
|
42 |
-
GIM(dkm):
|
43 |
-
enable: true
|
44 |
-
matcher: gim(dkm)
|
45 |
-
dense: true
|
46 |
-
info:
|
47 |
-
name: GIM(DKM) #dispaly name
|
48 |
-
source: "ICLR 2024"
|
49 |
-
github: https://github.com/xuelunshen/gim
|
50 |
-
paper: https://arxiv.org/abs/2402.11095
|
51 |
-
project: https://xuelunshen.com/gim
|
52 |
-
display: true
|
53 |
-
RoMa:
|
54 |
-
matcher: roma
|
55 |
-
dense: true
|
56 |
-
info:
|
57 |
-
name: RoMa #dispaly name
|
58 |
-
source: "CVPR 2024"
|
59 |
-
github: https://github.com/Parskatt/RoMa
|
60 |
-
paper: https://arxiv.org/abs/2305.15404
|
61 |
-
project: https://parskatt.github.io/RoMa
|
62 |
-
display: true
|
63 |
-
dkm:
|
64 |
-
matcher: dkm
|
65 |
-
dense: true
|
66 |
-
info:
|
67 |
-
name: DKM #dispaly name
|
68 |
-
source: "CVPR 2023"
|
69 |
-
github: https://github.com/Parskatt/DKM
|
70 |
-
paper: https://arxiv.org/abs/2202.00667
|
71 |
-
project: https://parskatt.github.io/DKM
|
72 |
-
display: true
|
73 |
-
loftr:
|
74 |
-
matcher: loftr
|
75 |
-
dense: true
|
76 |
-
info:
|
77 |
-
name: LoFTR #dispaly name
|
78 |
-
source: "CVPR 2021"
|
79 |
-
github: https://github.com/zju3dv/LoFTR
|
80 |
-
paper: https://arxiv.org/pdf/2104.00680
|
81 |
-
project: https://zju3dv.github.io/loftr
|
82 |
-
display: true
|
83 |
-
cotr:
|
84 |
-
enable: false
|
85 |
-
skip_ci: true
|
86 |
-
matcher: cotr
|
87 |
-
dense: true
|
88 |
-
info:
|
89 |
-
name: CoTR #dispaly name
|
90 |
-
source: "ICCV 2021"
|
91 |
-
github: https://github.com/ubc-vision/COTR
|
92 |
-
paper: https://arxiv.org/abs/2103.14167
|
93 |
-
project: null
|
94 |
-
display: true
|
95 |
-
topicfm:
|
96 |
-
matcher: topicfm
|
97 |
-
dense: true
|
98 |
-
info:
|
99 |
-
name: TopicFM #dispaly name
|
100 |
-
source: "AAAI 2023"
|
101 |
-
github: https://github.com/TruongKhang/TopicFM
|
102 |
-
paper: https://arxiv.org/abs/2307.00485
|
103 |
-
project: null
|
104 |
-
display: true
|
105 |
-
aspanformer:
|
106 |
-
matcher: aspanformer
|
107 |
-
dense: true
|
108 |
-
info:
|
109 |
-
name: ASpanformer #dispaly name
|
110 |
-
source: "ECCV 2022"
|
111 |
-
github: https://github.com/Vincentqyw/ml-aspanformer
|
112 |
-
paper: https://arxiv.org/abs/2208.14201
|
113 |
-
project: null
|
114 |
-
display: true
|
115 |
-
xfeat(sparse):
|
116 |
-
matcher: NN-mutual
|
117 |
-
feature: xfeat
|
118 |
-
dense: false
|
119 |
-
info:
|
120 |
-
name: XFeat #dispaly name
|
121 |
-
source: "CVPR 2024"
|
122 |
-
github: https://github.com/verlab/accelerated_features
|
123 |
-
paper: https://arxiv.org/abs/2404.19174
|
124 |
-
project: null
|
125 |
-
display: true
|
126 |
-
xfeat(dense):
|
127 |
-
matcher: xfeat_dense
|
128 |
-
dense: true
|
129 |
-
info:
|
130 |
-
name: XFeat #dispaly name
|
131 |
-
source: "CVPR 2024"
|
132 |
-
github: https://github.com/verlab/accelerated_features
|
133 |
-
paper: https://arxiv.org/abs/2404.19174
|
134 |
-
project: null
|
135 |
-
display: false
|
136 |
-
dedode:
|
137 |
-
matcher: Dual-Softmax
|
138 |
-
feature: dedode
|
139 |
-
dense: false
|
140 |
-
info:
|
141 |
-
name: DeDoDe #dispaly name
|
142 |
-
source: "3DV 2024"
|
143 |
-
github: https://github.com/Parskatt/DeDoDe
|
144 |
-
paper: https://arxiv.org/abs/2308.08479
|
145 |
-
project: null
|
146 |
-
display: true
|
147 |
superpoint+superglue:
|
148 |
matcher: superglue
|
149 |
feature: superpoint_max
|
@@ -154,241 +37,4 @@ matcher_zoo:
|
|
154 |
github: https://github.com/magicleap/SuperGluePretrainedNetwork
|
155 |
paper: https://arxiv.org/abs/1911.11763
|
156 |
project: null
|
157 |
-
display: true
|
158 |
-
superpoint+lightglue:
|
159 |
-
matcher: superpoint-lightglue
|
160 |
-
feature: superpoint_max
|
161 |
-
dense: false
|
162 |
-
info:
|
163 |
-
name: LightGlue #dispaly name
|
164 |
-
source: "ICCV 2023"
|
165 |
-
github: https://github.com/cvg/LightGlue
|
166 |
-
paper: https://arxiv.org/pdf/2306.13643
|
167 |
-
project: null
|
168 |
-
display: true
|
169 |
-
disk:
|
170 |
-
matcher: NN-mutual
|
171 |
-
feature: disk
|
172 |
-
dense: false
|
173 |
-
info:
|
174 |
-
name: DISK
|
175 |
-
source: "NeurIPS 2020"
|
176 |
-
github: https://github.com/cvlab-epfl/disk
|
177 |
-
paper: https://arxiv.org/abs/2006.13566
|
178 |
-
project: null
|
179 |
-
display: true
|
180 |
-
disk+dualsoftmax:
|
181 |
-
matcher: Dual-Softmax
|
182 |
-
feature: disk
|
183 |
-
dense: false
|
184 |
-
info:
|
185 |
-
name: DISK
|
186 |
-
source: "NeurIPS 2020"
|
187 |
-
github: https://github.com/cvlab-epfl/disk
|
188 |
-
paper: https://arxiv.org/abs/2006.13566
|
189 |
-
project: null
|
190 |
-
display: false
|
191 |
-
superpoint+dualsoftmax:
|
192 |
-
matcher: Dual-Softmax
|
193 |
-
feature: superpoint_max
|
194 |
-
dense: false
|
195 |
-
info:
|
196 |
-
name: SuperPoint
|
197 |
-
source: "CVPRW 2018"
|
198 |
-
github: https://github.com/magicleap/SuperPointPretrainedNetwork
|
199 |
-
paper: https://arxiv.org/abs/1712.07629
|
200 |
-
project: null
|
201 |
-
display: false
|
202 |
-
sift+lightglue:
|
203 |
-
matcher: sift-lightglue
|
204 |
-
feature: sift
|
205 |
-
dense: false
|
206 |
-
info:
|
207 |
-
name: LightGlue #dispaly name
|
208 |
-
source: "ICCV 2023"
|
209 |
-
github: https://github.com/cvg/LightGlue
|
210 |
-
paper: https://arxiv.org/pdf/2306.13643
|
211 |
-
project: null
|
212 |
-
display: true
|
213 |
-
disk+lightglue:
|
214 |
-
matcher: disk-lightglue
|
215 |
-
feature: disk
|
216 |
-
dense: false
|
217 |
-
info:
|
218 |
-
name: LightGlue
|
219 |
-
source: "ICCV 2023"
|
220 |
-
github: https://github.com/cvg/LightGlue
|
221 |
-
paper: https://arxiv.org/pdf/2306.13643
|
222 |
-
project: null
|
223 |
-
display: true
|
224 |
-
superpoint+mnn:
|
225 |
-
matcher: NN-mutual
|
226 |
-
feature: superpoint_max
|
227 |
-
dense: false
|
228 |
-
info:
|
229 |
-
name: SuperPoint #dispaly name
|
230 |
-
source: "CVPRW 2018"
|
231 |
-
github: https://github.com/magicleap/SuperPointPretrainedNetwork
|
232 |
-
paper: https://arxiv.org/abs/1712.07629
|
233 |
-
project: null
|
234 |
-
display: true
|
235 |
-
sift+sgmnet:
|
236 |
-
matcher: sgmnet
|
237 |
-
feature: sift
|
238 |
-
dense: false
|
239 |
-
info:
|
240 |
-
name: SGMNet #dispaly name
|
241 |
-
source: "ICCV 2021"
|
242 |
-
github: https://github.com/vdvchen/SGMNet
|
243 |
-
paper: https://arxiv.org/abs/2108.08771
|
244 |
-
project: null
|
245 |
-
display: true
|
246 |
-
sosnet:
|
247 |
-
matcher: NN-mutual
|
248 |
-
feature: sosnet
|
249 |
-
dense: false
|
250 |
-
info:
|
251 |
-
name: SOSNet #dispaly name
|
252 |
-
source: "CVPR 2019"
|
253 |
-
github: https://github.com/scape-research/SOSNet
|
254 |
-
paper: https://arxiv.org/abs/1904.05019
|
255 |
-
project: https://research.scape.io/sosnet
|
256 |
-
display: true
|
257 |
-
hardnet:
|
258 |
-
matcher: NN-mutual
|
259 |
-
feature: hardnet
|
260 |
-
dense: false
|
261 |
-
info:
|
262 |
-
name: HardNet #dispaly name
|
263 |
-
source: "NeurIPS 2017"
|
264 |
-
github: https://github.com/DagnyT/hardnet
|
265 |
-
paper: https://arxiv.org/abs/1705.10872
|
266 |
-
project: null
|
267 |
-
display: true
|
268 |
-
d2net:
|
269 |
-
matcher: NN-mutual
|
270 |
-
feature: d2net-ss
|
271 |
-
dense: false
|
272 |
-
info:
|
273 |
-
name: D2Net #dispaly name
|
274 |
-
source: "CVPR 2019"
|
275 |
-
github: https://github.com/Vincentqyw/d2-net
|
276 |
-
paper: https://arxiv.org/abs/1905.03561
|
277 |
-
project: https://dusmanu.com/publications/d2-net.html
|
278 |
-
display: true
|
279 |
-
rord:
|
280 |
-
matcher: NN-mutual
|
281 |
-
feature: rord
|
282 |
-
dense: false
|
283 |
-
info:
|
284 |
-
name: RoRD #dispaly name
|
285 |
-
source: "IROS 2021"
|
286 |
-
github: https://github.com/UditSinghParihar/RoRD
|
287 |
-
paper: https://arxiv.org/abs/2103.08573
|
288 |
-
project: https://uditsinghparihar.github.io/RoRD
|
289 |
-
display: true
|
290 |
-
alike:
|
291 |
-
matcher: NN-mutual
|
292 |
-
feature: alike
|
293 |
-
dense: false
|
294 |
-
info:
|
295 |
-
name: ALIKE #dispaly name
|
296 |
-
source: "TMM 2022"
|
297 |
-
github: https://github.com/Shiaoming/ALIKE
|
298 |
-
paper: https://arxiv.org/abs/2112.02906
|
299 |
-
project: null
|
300 |
-
display: true
|
301 |
-
lanet:
|
302 |
-
matcher: NN-mutual
|
303 |
-
feature: lanet
|
304 |
-
dense: false
|
305 |
-
info:
|
306 |
-
name: LANet #dispaly name
|
307 |
-
source: "ACCV 2022"
|
308 |
-
github: https://github.com/wangch-g/lanet
|
309 |
-
paper: https://openaccess.thecvf.com/content/ACCV2022/papers/Wang_Rethinking_Low-level_Features_for_Interest_Point_Detection_and_Description_ACCV_2022_paper.pdf
|
310 |
-
project: null
|
311 |
-
display: true
|
312 |
-
r2d2:
|
313 |
-
matcher: NN-mutual
|
314 |
-
feature: r2d2
|
315 |
-
dense: false
|
316 |
-
info:
|
317 |
-
name: R2D2 #dispaly name
|
318 |
-
source: "NeurIPS 2019"
|
319 |
-
github: https://github.com/naver/r2d2
|
320 |
-
paper: https://arxiv.org/abs/1906.06195
|
321 |
-
project: null
|
322 |
-
display: true
|
323 |
-
darkfeat:
|
324 |
-
matcher: NN-mutual
|
325 |
-
feature: darkfeat
|
326 |
-
dense: false
|
327 |
-
info:
|
328 |
-
name: DarkFeat #dispaly name
|
329 |
-
source: "AAAI 2023"
|
330 |
-
github: https://github.com/THU-LYJ-Lab/DarkFeat
|
331 |
-
paper: null
|
332 |
-
project: null
|
333 |
-
display: true
|
334 |
-
sift:
|
335 |
-
matcher: NN-mutual
|
336 |
-
feature: sift
|
337 |
-
dense: false
|
338 |
-
info:
|
339 |
-
name: SIFT #dispaly name
|
340 |
-
source: "IJCV 2004"
|
341 |
-
github: null
|
342 |
-
paper: https://www.cs.ubc.ca/~lowe/papers/ijcv04.pdf
|
343 |
-
project: null
|
344 |
-
display: true
|
345 |
-
gluestick:
|
346 |
-
enable: false
|
347 |
-
matcher: gluestick
|
348 |
-
dense: true
|
349 |
-
info:
|
350 |
-
name: GlueStick #dispaly name
|
351 |
-
source: "ICCV 2023"
|
352 |
-
github: https://github.com/cvg/GlueStick
|
353 |
-
paper: https://arxiv.org/abs/2304.02008
|
354 |
-
project: https://iago-suarez.com/gluestick
|
355 |
-
display: true
|
356 |
-
sold2:
|
357 |
-
enable: false
|
358 |
-
matcher: sold2
|
359 |
-
dense: true
|
360 |
-
info:
|
361 |
-
name: SOLD2 #dispaly name
|
362 |
-
source: "CVPR 2021"
|
363 |
-
github: https://github.com/cvg/SOLD2
|
364 |
-
paper: https://arxiv.org/abs/2104.03362
|
365 |
-
project: null
|
366 |
-
display: true
|
367 |
-
|
368 |
-
sfd2+imp:
|
369 |
-
matcher: imp
|
370 |
-
feature: sfd2
|
371 |
-
enable: false
|
372 |
-
dense: false
|
373 |
-
skip_ci: true
|
374 |
-
info:
|
375 |
-
name: SFD2+IMP #dispaly name
|
376 |
-
source: "CVPR 2023"
|
377 |
-
github: https://github.com/feixue94/imp-release
|
378 |
-
paper: https://arxiv.org/pdf/2304.14837
|
379 |
-
project: https://feixue94.github.io/
|
380 |
-
display: true
|
381 |
-
|
382 |
-
sfd2+mnn:
|
383 |
-
matcher: NN-mutual
|
384 |
-
feature: sfd2
|
385 |
-
enable: false
|
386 |
-
dense: false
|
387 |
-
skip_ci: true
|
388 |
-
info:
|
389 |
-
name: SFD2+MNN #dispaly name
|
390 |
-
source: "CVPR 2023"
|
391 |
-
github: https://github.com/feixue94/sfd2
|
392 |
-
paper: https://arxiv.org/abs/2304.14845
|
393 |
-
project: https://feixue94.github.io/
|
394 |
-
display: true
|
|
|
27 |
paper: https://arxiv.org/abs/2405.12979
|
28 |
project: https://hwjiang1510.github.io/OmniGlue
|
29 |
display: true
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
superpoint+superglue:
|
31 |
matcher: superglue
|
32 |
feature: superpoint_max
|
|
|
37 |
github: https://github.com/magicleap/SuperGluePretrainedNetwork
|
38 |
paper: https://arxiv.org/abs/1911.11763
|
39 |
project: null
|
40 |
+
display: true
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|