Skip to content

Commit 17e6bd7

Browse files
committed
dev(meta):new class to read .pid file and some code cleanup
1 parent 821df7c commit 17e6bd7

17 files changed

+1114
-214
lines changed

src/ZooProcess_lib/Converter.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99

1010
from .ImageJLike import convert_16bit_image_to_8bit_min_max
1111
from .Legacy import averaged_median_mean
12-
from .img_tools import rotate90c, Lut, minAndMax, saveimage, load_tiff_image_and_info
12+
from .img_tools import rotate90c, minAndMax, saveimage, load_tiff_image_and_info
13+
from .LegacyMeta import LutFile
1314

1415
CV2_VERTICAL_FLIP_CODE = 0
1516
CV2_NO_TIFF_COMPRESSION = 1
@@ -20,7 +21,7 @@ class Converter(object):
2021
In charge of images 16->8 bits conversion
2122
"""
2223

23-
def __init__(self, lut: Lut):
24+
def __init__(self, lut: LutFile):
2425
self.lut = lut
2526

2627
def do_file_to_file(self, from_file: Path, to_file: Path):

src/ZooProcess_lib/Extractor.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,10 @@ def extract_all_to_images(
6868

6969
def _add_border_and_legend(self, image: np.ndarray, longline: float):
7070
height, width = image.shape
71-
# Resized image is plain one...
72-
# ...+ 20% border and footer in height
73-
final_height = round(height * 1.4 + self.FOOTER)
74-
# ...and +20% border with enough space for line in width
75-
final_width = round(max(width * 1.4, longline + 2 * self.X1))
71+
final_height, final_width = self.get_final_dimensions(height, width, longline)
7672
# draw a new frame big enough and paste
7773
resized = np.full((final_height, final_width), 255, dtype=np.uint8)
78-
y_offset = (final_height - self.FOOTER - height) // 2
74+
y_offset = (final_height - Extractor.FOOTER - height) // 2
7975
x_offset = (final_width - width) // 2
8076
resized[y_offset : y_offset + height, x_offset : x_offset + width] = image
8177
# Paint the scale
@@ -114,6 +110,15 @@ def _add_border_and_legend(self, image: np.ndarray, longline: float):
114110
# print(f"height: {height}, width: {width} => {final_height}x{final_width} px xoffset: {x_offset} yoffset: {y_offset}")
115111
return resized
116112

113+
@staticmethod
114+
def get_final_dimensions(height: int, width: int, longline: float):
115+
# Resized image is plain one...
116+
# ...+ 20% border and footer in height
117+
final_height = round(height * 1.4 + Extractor.FOOTER)
118+
# ...and +20% border with enough space for line in width
119+
final_width = round(max(width * 1.4, longline + 2 * Extractor.X1))
120+
return final_height, final_width
121+
117122
def extract_image_at_ROI(
118123
self, image: np.ndarray, a_roi: ROI, erasing_background: bool = False
119124
) -> np.ndarray:

src/ZooProcess_lib/LegacyConfig.py

Lines changed: 0 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,9 @@
1-
# A .lut file contains processing directives for a whole project
21
import dataclasses
32
from configparser import ConfigParser
43

54
from pathlib import Path
65

76

8-
class Lut:
9-
def __init__(self):
10-
self.min: int = 0
11-
self.max: int = 65536
12-
self.gamma: float = 1
13-
self.sens: str = "before"
14-
# Appeared in a later version
15-
self.adjust: str = "no"
16-
self.odrange: float = 1.8
17-
# Appeared in a later version
18-
self.ratio: float = 1.15
19-
# Appeared in a later version
20-
self.sizelimit: int = 800 # Unused?
21-
self.overlap: float = 0.07
22-
# Appeared in a later version
23-
self.medianchoice: str = "no"
24-
self.medianvalue: int = 1
25-
# Appeared in a later version
26-
self.resolutionreduct: int = 1200
27-
28-
@staticmethod
29-
def read(path: Path) -> "Lut":
30-
ret = Lut()
31-
with open(path, "r") as f:
32-
lines = f.readlines()
33-
for a_var_name, a_line in zip(ret.__dict__.keys(), lines):
34-
a_var = getattr(ret, a_var_name)
35-
# Below for clarity and extensibility, as we could use directly the class, class(a_line)
36-
if isinstance(a_var, int):
37-
setattr(ret, a_var_name, int(a_line))
38-
elif isinstance(a_var, float):
39-
setattr(ret, a_var_name, float(a_line))
40-
elif isinstance(a_var, str):
41-
setattr(ret, a_var_name, a_line.strip())
42-
# From legacy macro, there is a typo "od_g_range" so the code doesn't do what it should I guess
43-
# if ret.odrange >= 3:
44-
# ret.odgrange = 1.15
45-
return ret
46-
47-
487
# Full dump of a config file, for future use:
498
# _Used_:
509
# background_process= last
@@ -114,135 +73,3 @@ def read(cls, path: Path) -> "ZooscanConfig":
11473
raise
11574
args.append(value)
11675
return ZooscanConfig(*args)
117-
118-
119-
@dataclasses.dataclass(frozen=False)
120-
class ProjectMeta:
121-
"""
122-
Class to read and store metadata from a metadata.txt file.
123-
All fields are explicitly defined with proper type annotations.
124-
"""
125-
126-
# String fields
127-
SampleId: str = "" # e.g. apero2023_tha_bioness_sup2000_017_st66_d_n1
128-
Scanop: str = "" # e.g. adelaide_perruchon
129-
Ship: str = "" # e.g. thalassa
130-
Scientificprog: str = "" # e.g. apero
131-
Date: str = "" # e.g. 20230704-0503
132-
CTDref: str = "" # e.g. apero_bio_ctd_017
133-
Otherref: str = "" # e.g. apero_bio_uvp6_017u
134-
Nettype: str = "" # e.g. bioness
135-
Observation: str = "" # e.g. no
136-
SubMethod: str = "" # e.g. motoda
137-
Sample_comment: str = "" # e.g. vol_zooprocess_saisi
138-
barcode: str = "" # e.g. ape000000147
139-
FracId: str = "" # e.g. d2_4_sur_4
140-
141-
# Integer fields
142-
StationId: int = -1 # e.g. 66
143-
Townb: int = -1 # e.g. 1
144-
Towtype: int = -1 # e.g. 1
145-
Netmesh: int = -1 # e.g. 2000
146-
Netsurf: int = -1 # e.g. 1
147-
Vol: int = -1 # e.g. 357
148-
Fracmin: int = -1 # e.g. 2000
149-
Fracsup: int = -1 # e.g. 999999
150-
Fracnb: int = -1 # e.g. 4
151-
Code: int = -1 # e.g. 1
152-
CellPart: int = -1 # e.g. 1
153-
Replicates: int = -1 # e.g. 1
154-
VolIni: int = -1 # e.g. 1
155-
VolPrec: int = -1 # e.g. 1
156-
vol_qc: int = -1 # e.g. 1
157-
depth_qc: int = -1 # e.g. 1
158-
sample_qc: int = -1 # e.g. 1111
159-
160-
# Float fields
161-
Depth: float = -1.0 # e.g. 99999
162-
Zmax: float = -1.0 # e.g. 1008
163-
Zmin: float = -1.0 # e.g. 820
164-
net_duration: float = -1.0 # e.g. 20
165-
ship_speed_knots: float = -1.0 # e.g. 2
166-
cable_length: float = -1.0 # e.g. 9999
167-
cable_angle: float = -1.0 # e.g. 99999
168-
cable_speed: float = -1.0 # e.g. 0
169-
nb_jar: float = -1.0 # e.g. 1
170-
Latitude: float = -1.0 # e.g. 51.4322000
171-
Longitude: float = -1.0 # e.g. 18.3108000
172-
latitude_end: float = -1.0 # e.g. 51.4421000
173-
longitude_end: float = -1.0 # e.g. 18.3417000
174-
175-
@classmethod
176-
def read(cls, path: Path) -> "ProjectMeta":
177-
"""
178-
Read a metadata.txt file and return a ProjectMeta instance with fields
179-
populated from the file.
180-
"""
181-
meta = ProjectMeta()
182-
with open(path, "r") as strm:
183-
for line in strm:
184-
if "=" in line:
185-
key, value = line.split("=", 1)
186-
key = key.strip()
187-
value = value.strip()
188-
189-
if hasattr(meta, key):
190-
field_type = type(getattr(meta, key))
191-
try:
192-
if field_type == int:
193-
setattr(meta, key, int(value))
194-
elif field_type == float:
195-
setattr(meta, key, float(value))
196-
else:
197-
setattr(meta, key, value)
198-
except ValueError:
199-
# If conversion fails, keep as string
200-
setattr(meta, key, value)
201-
else:
202-
# For any fields not explicitly defined, add them as strings
203-
setattr(meta, key, value)
204-
205-
return meta
206-
207-
def _relevant_attributes(self):
208-
"""
209-
Generator that yields relevant attributes for writing to a metadata file.
210-
Yields tuples of (attr_name, attr_value) for non-default attributes.
211-
"""
212-
for attr_name in dir(self):
213-
# Skip special attributes, methods, and class variables
214-
if (
215-
attr_name.startswith("__")
216-
or callable(getattr(self, attr_name))
217-
or attr_name == "__annotations__"
218-
):
219-
continue
220-
221-
# Get the attribute value
222-
attr_value = getattr(self, attr_name)
223-
224-
# Skip default values for primitive types
225-
if attr_value == "" or attr_value == -1 or attr_value == -1.0:
226-
continue
227-
228-
yield attr_name, attr_value
229-
230-
def to_dict(self) -> dict:
231-
"""
232-
Convert the ProjectMeta instance to a dictionary using the _relevant_attributes generator.
233-
Returns a dictionary with attribute names as keys and their values as values.
234-
"""
235-
return {
236-
attr_name: attr_value
237-
for attr_name, attr_value in self._relevant_attributes()
238-
}
239-
240-
def write(self, path: Path) -> None:
241-
"""
242-
Write the ProjectMeta instance to a metadata.txt file in the same format
243-
as it is read.
244-
"""
245-
with open(path, "w") as strm:
246-
# Write all relevant attributes to the file
247-
for attr_name, attr_value in self._relevant_attributes():
248-
strm.write(f"{attr_name}= {attr_value}\n")

0 commit comments

Comments
 (0)