GitHub Action commited on
Commit
188604c
·
1 Parent(s): 5e25fa3

Auto-deploy from GitHub Actions

Browse files
README.md CHANGED
@@ -10,9 +10,12 @@ app_file: app.py
10
  pinned: false
11
  ---
12
 
13
- [![Codecov](https://codecov.io/gh/thearn/magiceye-solver/branch/main/graph/badge.svg)](https://codecov.io/gh/thearn/magiceye-solver)
 
 
14
  [![Hugging Face Spaces](https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face%20Spaces-Magic%20Eye%20Solver-blue)](https://huggingface.co/spaces/thearn/magiceye-solver)
15
  ![Build Status](https://github.com/thearn/magiceye-solver/actions/workflows/python-test.yml/badge.svg)
 
16
 
17
  ![Alt text](http://i.imgur.com/AUmpOSr.png "Example" )
18
 
 
10
  pinned: false
11
  ---
12
 
13
+ > 🚀 **Try the Magic Eye Solver instantly on [Hugging Face Spaces](https://huggingface.co/spaces/thearn/magiceye-solver)!**
14
+
15
+
16
  [![Hugging Face Spaces](https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face%20Spaces-Magic%20Eye%20Solver-blue)](https://huggingface.co/spaces/thearn/magiceye-solver)
17
  ![Build Status](https://github.com/thearn/magiceye-solver/actions/workflows/python-test.yml/badge.svg)
18
+ [![Codecov](https://codecov.io/gh/thearn/magiceye-solver/branch/main/graph/badge.svg)](https://codecov.io/gh/thearn/magiceye-solver)
19
 
20
  ![Alt text](http://i.imgur.com/AUmpOSr.png "Example" )
21
 
coverage.xml CHANGED
@@ -1,12 +1,12 @@
1
  <?xml version="1.0" ?>
2
- <coverage version="7.8.2" timestamp="1748026512837" lines-valid="295" lines-covered="254" line-rate="0.861" branches-covered="0" branches-valid="0" branch-rate="0" complexity="0">
3
  <!-- Generated by coverage.py: https://coverage.readthedocs.io/en/7.8.2 -->
4
  <!-- Based on https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd -->
5
  <sources>
6
  <source>/home/runner/work/magiceye-solver/magiceye-solver</source>
7
  </sources>
8
  <packages>
9
- <package name="magiceye_solve" line-rate="0.7532" branch-rate="0" complexity="0">
10
  <classes>
11
  <class name="__init__.py" filename="magiceye_solve/__init__.py" complexity="0" line-rate="1" branch-rate="0">
12
  <methods/>
@@ -14,7 +14,7 @@
14
  <line number="1" hits="1"/>
15
  </lines>
16
  </class>
17
- <class name="solver.py" filename="magiceye_solve/solver.py" complexity="0" line-rate="0.7516" branch-rate="0">
18
  <methods/>
19
  <lines>
20
  <line number="1" hits="1"/>
@@ -34,151 +34,155 @@
34
  <line number="16" hits="0"/>
35
  <line number="18" hits="1"/>
36
  <line number="20" hits="1"/>
37
- <line number="25" hits="1"/>
38
  <line number="26" hits="1"/>
 
39
  <line number="28" hits="1"/>
40
- <line number="29" hits="0"/>
41
- <line number="30" hits="1"/>
42
- <line number="32" hits="1"/>
43
- <line number="33" hits="0"/>
44
  <line number="35" hits="1"/>
45
  <line number="36" hits="1"/>
46
- <line number="37" hits="1"/>
47
  <line number="39" hits="1"/>
48
- <line number="40" hits="0"/>
49
  <line number="42" hits="1"/>
50
- <line number="43" hits="0"/>
51
- <line number="44" hits="0"/>
52
- <line number="47" hits="1"/>
53
- <line number="48" hits="1"/>
54
  <line number="50" hits="1"/>
55
  <line number="51" hits="1"/>
56
  <line number="53" hits="1"/>
57
  <line number="54" hits="1"/>
58
- <line number="55" hits="1"/>
59
- <line number="56" hits="0"/>
60
- <line number="57" hits="0"/>
61
- <line number="58" hits="0"/>
62
  <line number="60" hits="0"/>
63
  <line number="61" hits="0"/>
64
- <line number="63" hits="1"/>
65
  <line number="64" hits="0"/>
66
- <line number="65" hits="0"/>
67
- <line number="66" hits="0"/>
68
  <line number="67" hits="0"/>
69
  <line number="68" hits="0"/>
70
  <line number="69" hits="0"/>
71
- <line number="72" hits="1"/>
72
- <line number="73" hits="0"/>
 
73
  <line number="75" hits="1"/>
74
- <line number="77" hits="1"/>
75
- <line number="79" hits="1"/>
76
- <line number="83" hits="1"/>
77
- <line number="84" hits="1"/>
78
- <line number="85" hits="1"/>
79
  <line number="86" hits="1"/>
80
- <line number="87" hits="0"/>
 
81
  <line number="89" hits="1"/>
82
  <line number="90" hits="1"/>
83
- <line number="91" hits="1"/>
84
  <line number="92" hits="1"/>
 
85
  <line number="94" hits="1"/>
86
- <line number="98" hits="1"/>
87
- <line number="99" hits="0"/>
88
- <line number="100" hits="1"/>
89
  <line number="101" hits="1"/>
90
  <line number="102" hits="1"/>
91
- <line number="103" hits="0"/>
92
  <line number="104" hits="1"/>
93
  <line number="105" hits="1"/>
94
- <line number="106" hits="0"/>
95
- <line number="107" hits="0"/>
96
- <line number="109" hits="1"/>
97
- <line number="113" hits="1"/>
98
- <line number="121" hits="1"/>
99
- <line number="122" hits="1"/>
100
- <line number="123" hits="1"/>
101
- <line number="124" hits="0"/>
 
102
  <line number="126" hits="1"/>
103
  <line number="127" hits="0"/>
104
  <line number="129" hits="1"/>
105
- <line number="130" hits="1"/>
106
- <line number="131" hits="1"/>
107
  <line number="133" hits="1"/>
108
  <line number="134" hits="1"/>
109
- <line number="135" hits="1"/>
110
- <line number="136" hits="1"/>
111
  <line number="137" hits="1"/>
112
  <line number="138" hits="1"/>
113
- <line number="139" hits="1"/>
114
  <line number="140" hits="1"/>
115
  <line number="141" hits="1"/>
116
- <line number="143" hits="0"/>
 
 
117
  <line number="145" hits="1"/>
118
  <line number="146" hits="1"/>
119
  <line number="147" hits="1"/>
120
- <line number="149" hits="1"/>
121
- <line number="150" hits="1"/>
122
- <line number="151" hits="1"/>
123
  <line number="152" hits="1"/>
 
124
  <line number="154" hits="1"/>
125
- <line number="167" hits="1"/>
126
- <line number="168" hits="1"/>
127
- <line number="169" hits="1"/>
128
- <line number="171" hits="1"/>
129
- <line number="173" hits="1"/>
130
  <line number="174" hits="1"/>
131
  <line number="175" hits="1"/>
132
- <line number="177" hits="1"/>
133
  <line number="178" hits="1"/>
134
- <line number="179" hits="1"/>
135
  <line number="180" hits="1"/>
136
  <line number="181" hits="1"/>
137
  <line number="182" hits="1"/>
138
- <line number="183" hits="1"/>
139
  <line number="184" hits="1"/>
140
  <line number="185" hits="1"/>
141
  <line number="186" hits="1"/>
142
  <line number="187" hits="1"/>
143
  <line number="188" hits="1"/>
144
  <line number="189" hits="1"/>
145
- <line number="190" hits="0"/>
146
- <line number="191" hits="0"/>
 
147
  <line number="193" hits="1"/>
148
  <line number="194" hits="1"/>
149
  <line number="195" hits="1"/>
150
  <line number="196" hits="1"/>
151
  <line number="197" hits="1"/>
152
- <line number="198" hits="0"/>
153
- <line number="199" hits="1"/>
154
- <line number="200" hits="0"/>
155
  <line number="201" hits="1"/>
156
  <line number="202" hits="1"/>
157
  <line number="203" hits="1"/>
158
  <line number="204" hits="1"/>
159
- <line number="205" hits="1"/>
160
  <line number="206" hits="1"/>
161
- <line number="207" hits="1"/>
162
  <line number="208" hits="1"/>
163
  <line number="209" hits="1"/>
164
  <line number="210" hits="1"/>
165
- <line number="211" hits="0"/>
166
- <line number="212" hits="0"/>
167
- <line number="213" hits="0"/>
168
- <line number="214" hits="0"/>
169
  <line number="215" hits="1"/>
170
  <line number="216" hits="1"/>
171
  <line number="217" hits="1"/>
172
- <line number="218" hits="1"/>
173
  <line number="219" hits="0"/>
174
  <line number="220" hits="0"/>
175
- <line number="221" hits="1"/>
 
 
176
  <line number="224" hits="1"/>
 
 
 
 
 
177
  </lines>
178
  </class>
179
  </classes>
180
  </package>
181
- <package name="magiceye_solve.test" line-rate="0.9854" branch-rate="0" complexity="0">
182
  <classes>
183
  <class name="__init__.py" filename="magiceye_solve/test/__init__.py" complexity="0" line-rate="1" branch-rate="0">
184
  <methods/>
@@ -331,6 +335,116 @@
331
  <line number="149" hits="0"/>
332
  </lines>
333
  </class>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
  </classes>
335
  </package>
336
  </packages>
 
1
  <?xml version="1.0" ?>
2
+ <coverage version="7.8.2" timestamp="1748027079655" lines-valid="404" lines-covered="370" line-rate="0.9158" branches-covered="0" branches-valid="0" branch-rate="0" complexity="0">
3
  <!-- Generated by coverage.py: https://coverage.readthedocs.io/en/7.8.2 -->
4
  <!-- Based on https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd -->
5
  <sources>
6
  <source>/home/runner/work/magiceye-solver/magiceye-solver</source>
7
  </sources>
8
  <packages>
9
+ <package name="magiceye_solve" line-rate="0.8086" branch-rate="0" complexity="0">
10
  <classes>
11
  <class name="__init__.py" filename="magiceye_solve/__init__.py" complexity="0" line-rate="1" branch-rate="0">
12
  <methods/>
 
14
  <line number="1" hits="1"/>
15
  </lines>
16
  </class>
17
+ <class name="solver.py" filename="magiceye_solve/solver.py" complexity="0" line-rate="0.8075" branch-rate="0">
18
  <methods/>
19
  <lines>
20
  <line number="1" hits="1"/>
 
34
  <line number="16" hits="0"/>
35
  <line number="18" hits="1"/>
36
  <line number="20" hits="1"/>
 
37
  <line number="26" hits="1"/>
38
+ <line number="27" hits="1"/>
39
  <line number="28" hits="1"/>
40
+ <line number="29" hits="1"/>
41
+ <line number="31" hits="1"/>
42
+ <line number="32" hits="0"/>
43
+ <line number="33" hits="1"/>
44
  <line number="35" hits="1"/>
45
  <line number="36" hits="1"/>
46
+ <line number="38" hits="1"/>
47
  <line number="39" hits="1"/>
48
+ <line number="40" hits="1"/>
49
  <line number="42" hits="1"/>
50
+ <line number="43" hits="1"/>
51
+ <line number="45" hits="1"/>
52
+ <line number="46" hits="0"/>
53
+ <line number="47" hits="0"/>
54
  <line number="50" hits="1"/>
55
  <line number="51" hits="1"/>
56
  <line number="53" hits="1"/>
57
  <line number="54" hits="1"/>
58
+ <line number="56" hits="1"/>
59
+ <line number="57" hits="1"/>
60
+ <line number="58" hits="1"/>
61
+ <line number="59" hits="0"/>
62
  <line number="60" hits="0"/>
63
  <line number="61" hits="0"/>
64
+ <line number="63" hits="0"/>
65
  <line number="64" hits="0"/>
66
+ <line number="66" hits="1"/>
 
67
  <line number="67" hits="0"/>
68
  <line number="68" hits="0"/>
69
  <line number="69" hits="0"/>
70
+ <line number="70" hits="0"/>
71
+ <line number="71" hits="0"/>
72
+ <line number="72" hits="0"/>
73
  <line number="75" hits="1"/>
74
+ <line number="76" hits="1"/>
75
+ <line number="78" hits="1"/>
76
+ <line number="80" hits="1"/>
77
+ <line number="82" hits="1"/>
 
78
  <line number="86" hits="1"/>
79
+ <line number="87" hits="1"/>
80
+ <line number="88" hits="1"/>
81
  <line number="89" hits="1"/>
82
  <line number="90" hits="1"/>
 
83
  <line number="92" hits="1"/>
84
+ <line number="93" hits="1"/>
85
  <line number="94" hits="1"/>
86
+ <line number="95" hits="1"/>
87
+ <line number="97" hits="1"/>
 
88
  <line number="101" hits="1"/>
89
  <line number="102" hits="1"/>
90
+ <line number="103" hits="1"/>
91
  <line number="104" hits="1"/>
92
  <line number="105" hits="1"/>
93
+ <line number="106" hits="1"/>
94
+ <line number="107" hits="1"/>
95
+ <line number="108" hits="1"/>
96
+ <line number="109" hits="0"/>
97
+ <line number="110" hits="0"/>
98
+ <line number="112" hits="1"/>
99
+ <line number="116" hits="1"/>
100
+ <line number="124" hits="1"/>
101
+ <line number="125" hits="1"/>
102
  <line number="126" hits="1"/>
103
  <line number="127" hits="0"/>
104
  <line number="129" hits="1"/>
105
+ <line number="130" hits="0"/>
106
+ <line number="132" hits="1"/>
107
  <line number="133" hits="1"/>
108
  <line number="134" hits="1"/>
 
 
109
  <line number="137" hits="1"/>
110
  <line number="138" hits="1"/>
 
111
  <line number="140" hits="1"/>
112
  <line number="141" hits="1"/>
113
+ <line number="142" hits="1"/>
114
+ <line number="143" hits="1"/>
115
+ <line number="144" hits="1"/>
116
  <line number="145" hits="1"/>
117
  <line number="146" hits="1"/>
118
  <line number="147" hits="1"/>
119
+ <line number="148" hits="1"/>
120
+ <line number="150" hits="0"/>
 
121
  <line number="152" hits="1"/>
122
+ <line number="153" hits="1"/>
123
  <line number="154" hits="1"/>
124
+ <line number="156" hits="1"/>
125
+ <line number="157" hits="1"/>
126
+ <line number="158" hits="1"/>
127
+ <line number="159" hits="1"/>
128
+ <line number="161" hits="1"/>
129
  <line number="174" hits="1"/>
130
  <line number="175" hits="1"/>
131
+ <line number="176" hits="1"/>
132
  <line number="178" hits="1"/>
 
133
  <line number="180" hits="1"/>
134
  <line number="181" hits="1"/>
135
  <line number="182" hits="1"/>
 
136
  <line number="184" hits="1"/>
137
  <line number="185" hits="1"/>
138
  <line number="186" hits="1"/>
139
  <line number="187" hits="1"/>
140
  <line number="188" hits="1"/>
141
  <line number="189" hits="1"/>
142
+ <line number="190" hits="1"/>
143
+ <line number="191" hits="1"/>
144
+ <line number="192" hits="1"/>
145
  <line number="193" hits="1"/>
146
  <line number="194" hits="1"/>
147
  <line number="195" hits="1"/>
148
  <line number="196" hits="1"/>
149
  <line number="197" hits="1"/>
150
+ <line number="198" hits="1"/>
151
+ <line number="200" hits="1"/>
 
152
  <line number="201" hits="1"/>
153
  <line number="202" hits="1"/>
154
  <line number="203" hits="1"/>
155
  <line number="204" hits="1"/>
156
+ <line number="205" hits="0"/>
157
  <line number="206" hits="1"/>
158
+ <line number="207" hits="0"/>
159
  <line number="208" hits="1"/>
160
  <line number="209" hits="1"/>
161
  <line number="210" hits="1"/>
162
+ <line number="211" hits="1"/>
163
+ <line number="212" hits="1"/>
164
+ <line number="213" hits="1"/>
165
+ <line number="214" hits="1"/>
166
  <line number="215" hits="1"/>
167
  <line number="216" hits="1"/>
168
  <line number="217" hits="1"/>
169
+ <line number="218" hits="0"/>
170
  <line number="219" hits="0"/>
171
  <line number="220" hits="0"/>
172
+ <line number="221" hits="0"/>
173
+ <line number="222" hits="1"/>
174
+ <line number="223" hits="1"/>
175
  <line number="224" hits="1"/>
176
+ <line number="225" hits="1"/>
177
+ <line number="226" hits="0"/>
178
+ <line number="227" hits="0"/>
179
+ <line number="228" hits="1"/>
180
+ <line number="231" hits="1"/>
181
  </lines>
182
  </class>
183
  </classes>
184
  </package>
185
+ <package name="magiceye_solve.test" line-rate="0.9876" branch-rate="0" complexity="0">
186
  <classes>
187
  <class name="__init__.py" filename="magiceye_solve/test/__init__.py" complexity="0" line-rate="1" branch-rate="0">
188
  <methods/>
 
335
  <line number="149" hits="0"/>
336
  </lines>
337
  </class>
338
+ <class name="test_solver_unit.py" filename="magiceye_solve/test/test_solver_unit.py" complexity="0" line-rate="0.9905" branch-rate="0">
339
+ <methods/>
340
+ <lines>
341
+ <line number="1" hits="1"/>
342
+ <line number="2" hits="1"/>
343
+ <line number="3" hits="1"/>
344
+ <line number="5" hits="1"/>
345
+ <line number="7" hits="1"/>
346
+ <line number="8" hits="1"/>
347
+ <line number="10" hits="1"/>
348
+ <line number="11" hits="1"/>
349
+ <line number="12" hits="1"/>
350
+ <line number="14" hits="1"/>
351
+ <line number="15" hits="1"/>
352
+ <line number="16" hits="1"/>
353
+ <line number="18" hits="1"/>
354
+ <line number="19" hits="1"/>
355
+ <line number="20" hits="1"/>
356
+ <line number="21" hits="1"/>
357
+ <line number="23" hits="1"/>
358
+ <line number="24" hits="1"/>
359
+ <line number="25" hits="1"/>
360
+ <line number="26" hits="1"/>
361
+ <line number="28" hits="1"/>
362
+ <line number="29" hits="1"/>
363
+ <line number="30" hits="1"/>
364
+ <line number="31" hits="1"/>
365
+ <line number="32" hits="1"/>
366
+ <line number="34" hits="1"/>
367
+ <line number="36" hits="1"/>
368
+ <line number="37" hits="1"/>
369
+ <line number="38" hits="1"/>
370
+ <line number="40" hits="1"/>
371
+ <line number="41" hits="1"/>
372
+ <line number="42" hits="1"/>
373
+ <line number="43" hits="1"/>
374
+ <line number="44" hits="1"/>
375
+ <line number="45" hits="1"/>
376
+ <line number="47" hits="1"/>
377
+ <line number="48" hits="1"/>
378
+ <line number="49" hits="1"/>
379
+ <line number="50" hits="1"/>
380
+ <line number="52" hits="1"/>
381
+ <line number="53" hits="1"/>
382
+ <line number="54" hits="1"/>
383
+ <line number="55" hits="1"/>
384
+ <line number="56" hits="1"/>
385
+ <line number="57" hits="1"/>
386
+ <line number="58" hits="1"/>
387
+ <line number="60" hits="1"/>
388
+ <line number="62" hits="1"/>
389
+ <line number="63" hits="1"/>
390
+ <line number="64" hits="1"/>
391
+ <line number="65" hits="1"/>
392
+ <line number="66" hits="1"/>
393
+ <line number="67" hits="1"/>
394
+ <line number="69" hits="1"/>
395
+ <line number="70" hits="1"/>
396
+ <line number="71" hits="1"/>
397
+ <line number="72" hits="1"/>
398
+ <line number="73" hits="1"/>
399
+ <line number="74" hits="1"/>
400
+ <line number="76" hits="1"/>
401
+ <line number="78" hits="1"/>
402
+ <line number="80" hits="1"/>
403
+ <line number="81" hits="1"/>
404
+ <line number="82" hits="1"/>
405
+ <line number="83" hits="1"/>
406
+ <line number="84" hits="1"/>
407
+ <line number="85" hits="1"/>
408
+ <line number="86" hits="1"/>
409
+ <line number="87" hits="1"/>
410
+ <line number="88" hits="1"/>
411
+ <line number="89" hits="1"/>
412
+ <line number="90" hits="1"/>
413
+ <line number="92" hits="1"/>
414
+ <line number="94" hits="1"/>
415
+ <line number="95" hits="1"/>
416
+ <line number="96" hits="1"/>
417
+ <line number="98" hits="1"/>
418
+ <line number="99" hits="1"/>
419
+ <line number="100" hits="1"/>
420
+ <line number="102" hits="1"/>
421
+ <line number="103" hits="1"/>
422
+ <line number="104" hits="1"/>
423
+ <line number="105" hits="1"/>
424
+ <line number="106" hits="1"/>
425
+ <line number="108" hits="1"/>
426
+ <line number="109" hits="1"/>
427
+ <line number="110" hits="1"/>
428
+ <line number="111" hits="1"/>
429
+ <line number="112" hits="1"/>
430
+ <line number="114" hits="1"/>
431
+ <line number="115" hits="1"/>
432
+ <line number="116" hits="1"/>
433
+ <line number="117" hits="1"/>
434
+ <line number="118" hits="1"/>
435
+ <line number="120" hits="1"/>
436
+ <line number="122" hits="1"/>
437
+ <line number="123" hits="1"/>
438
+ <line number="124" hits="1"/>
439
+ <line number="125" hits="1"/>
440
+ <line number="126" hits="1"/>
441
+ <line number="127" hits="1"/>
442
+ <line number="128" hits="1"/>
443
+ <line number="129" hits="1"/>
444
+ <line number="131" hits="1"/>
445
+ <line number="132" hits="0"/>
446
+ </lines>
447
+ </class>
448
  </classes>
449
  </package>
450
  </packages>
magiceye_solve/solver.py CHANGED
@@ -22,6 +22,9 @@ def offset(img: np.ndarray) -> Tuple[int, np.ndarray, np.ndarray, np.ndarray]:
22
  Calculates the offset that defines the stereoscopic effect.
23
  Now selects the offset corresponding to the highest peak in the autocorrelation curve.
24
  """
 
 
 
25
  img = img - img.mean()
26
  ac: np.ndarray = fftconvolve(img, np.flipud(np.fliplr(img)), mode='same')
27
  # check ac shape
@@ -130,6 +133,10 @@ class InteractiveSolver:
130
  self.shape: Tuple[int, ...] = image.shape
131
  self.c: int = 1
132
 
 
 
 
 
133
  if len(self.shape) >= 3 and self.shape[2] in [3, 4]:
134
  self.m, self.n, self.c = self.shape[0], self.shape[1], self.shape[2]
135
  self.color_image = True
 
22
  Calculates the offset that defines the stereoscopic effect.
23
  Now selects the offset corresponding to the highest peak in the autocorrelation curve.
24
  """
25
+ # Handle empty or 1D arrays gracefully
26
+ if img.size == 0 or img.ndim < 2:
27
+ return 0, np.array([]), np.array([]), np.array([])
28
  img = img - img.mean()
29
  ac: np.ndarray = fftconvolve(img, np.flipud(np.fliplr(img)), mode='same')
30
  # check ac shape
 
133
  self.shape: Tuple[int, ...] = image.shape
134
  self.c: int = 1
135
 
136
+ # Additional check for >3 dimensions or unsupported 3D
137
+ if image.ndim != 2 and not (image.ndim == 3 and image.shape[2] in [1, 3, 4]):
138
+ raise ValueError(f"Unsupported image shape: {self.shape}")
139
+
140
  if len(self.shape) >= 3 and self.shape[2] in [3, 4]:
141
  self.m, self.n, self.c = self.shape[0], self.shape[1], self.shape[2]
142
  self.color_image = True
magiceye_solve/test/test_solver_unit.py ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import unittest
2
+ import numpy as np
3
+ import sys
4
+
5
+ from magiceye_solve import solver
6
+
7
+ class TestSolverFunctions(unittest.TestCase):
8
+ def test_offset_empty_and_1d(self):
9
+ # Empty array
10
+ arr = np.array([])
11
+ result = solver.offset(arr)
12
+ self.assertEqual(result[0], 0)
13
+ # 1D array
14
+ arr1d = np.zeros(10)
15
+ result = solver.offset(arr1d)
16
+ self.assertEqual(result[0], 0)
17
+
18
+ def test_offset_all_zeros(self):
19
+ arr = np.zeros((20, 30))
20
+ result = solver.offset(arr)
21
+ self.assertEqual(result[0], 30)
22
+
23
+ def test_offset_no_peaks(self):
24
+ arr = np.ones((20, 30))
25
+ result = solver.offset(arr)
26
+ self.assertEqual(result[0], 30)
27
+
28
+ def test_offset_width_less_than_10(self):
29
+ arr = np.random.rand(10, 5)
30
+ result = solver.offset(arr)
31
+ self.assertGreaterEqual(result[0], 1)
32
+ self.assertLessEqual(result[0], 5)
33
+
34
+ def test_offset_exception_handling(self):
35
+ # Should trigger ValueError in np.where
36
+ arr = np.full((20, 30), np.nan)
37
+ result = solver.offset(arr)
38
+ self.assertEqual(result[0], 30)
39
+
40
+ def test_shift_pic_gap_zero_and_negative(self):
41
+ arr = np.random.rand(10, 10)
42
+ out = solver.shift_pic(arr, 0)
43
+ np.testing.assert_array_equal(out, arr)
44
+ out = solver.shift_pic(arr, -5)
45
+ np.testing.assert_array_equal(out, arr)
46
+
47
+ def test_shift_pic_gap_greater_than_width(self):
48
+ arr = np.random.rand(10, 10)
49
+ out = solver.shift_pic(arr, 15)
50
+ self.assertEqual(out.shape[1], 0)
51
+
52
+ def test_shift_pic_one_row_one_col(self):
53
+ arr = np.random.rand(1, 10)
54
+ out = solver.shift_pic(arr, 3)
55
+ self.assertEqual(out.shape[0], 1)
56
+ arr = np.random.rand(10, 1)
57
+ out = solver.shift_pic(arr, 1)
58
+ self.assertEqual(out.shape[1], 0)
59
+
60
+ def test_post_process_skimage_unavailable(self):
61
+ # Simulate skimage unavailable
62
+ orig = solver._SKIMAGE_AVAILABLE
63
+ solver._SKIMAGE_AVAILABLE = False
64
+ arr = np.random.rand(10, 10)
65
+ out = solver.post_process(arr)
66
+ np.testing.assert_array_equal(out, arr)
67
+ solver._SKIMAGE_AVAILABLE = orig
68
+
69
+ def test_post_process_empty_and_all_same(self):
70
+ arr = np.array([])
71
+ out = solver.post_process(arr)
72
+ np.testing.assert_array_equal(out, arr)
73
+ arr = np.ones((10, 10))
74
+ out = solver.post_process(arr)
75
+ # Should return something, but all-same triggers early return
76
+ self.assertTrue(np.allclose(out, out[0, 0]))
77
+
78
+ def test_post_process_exception(self):
79
+ # Pass a malformed array to trigger exception
80
+ class BadArray(np.ndarray):
81
+ def __new__(cls):
82
+ return np.ndarray.__new__(cls, shape=(10, 10))
83
+ def __array_function__(self, *args, **kwargs):
84
+ raise Exception("fail")
85
+ arr = BadArray()
86
+ try:
87
+ out = solver.post_process(arr)
88
+ np.testing.assert_array_equal(out, arr)
89
+ except Exception:
90
+ pass # Acceptable if exception propagates
91
+
92
+ def test_interactive_solver_bad_shape(self):
93
+ # 3D with 2 channels (unsupported)
94
+ arr = np.random.rand(10, 10, 2)
95
+ with self.assertRaises(ValueError):
96
+ solver.InteractiveSolver(arr)
97
+ # 4D array
98
+ arr = np.random.rand(10, 10, 3, 2)
99
+ with self.assertRaises(ValueError):
100
+ solver.InteractiveSolver(arr)
101
+
102
+ def test_interactive_solver_std_zero(self):
103
+ arr = np.zeros((10, 10))
104
+ s = solver.InteractiveSolver(arr)
105
+ self.assertEqual(s.default_offset, s.n)
106
+ self.assertEqual(s.autocorrelation_curve.size, 0)
107
+
108
+ def test_solve_with_offset_negative(self):
109
+ arr = np.random.rand(10, 10, 3)
110
+ s = solver.InteractiveSolver(arr)
111
+ out = s.solve_with_offset(-5)
112
+ self.assertEqual(out.shape, (s.m, s.n * s.c))
113
+
114
+ def test_solve_with_offset_std_zero(self):
115
+ arr = np.ones((10, 10, 3))
116
+ s = solver.InteractiveSolver(arr)
117
+ out = s.solve_with_offset(5, channel_mode='average')
118
+ self.assertEqual(out.shape, (s.m, s.n - 5))
119
+
120
+ def test_solve_with_offset_exception(self):
121
+ # Patch ndimage.prewitt to raise
122
+ import scipy.ndimage
123
+ orig_prewitt = scipy.ndimage.prewitt
124
+ scipy.ndimage.prewitt = lambda x: (_ for _ in ()).throw(Exception("fail"))
125
+ arr = np.random.rand(10, 10, 3)
126
+ s = solver.InteractiveSolver(arr)
127
+ out = s.solve_with_offset(5, channel_mode='average')
128
+ self.assertEqual(out.shape, (s.m, s.n - 5))
129
+ scipy.ndimage.prewitt = orig_prewitt
130
+
131
+ if __name__ == "__main__":
132
+ unittest.main()