Coverage for src/recon3d/image_stack_to_array.py: 34%

59 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-04-02 00:06 +0000

1"""Converts a series of images to a stack array.""" 

2 

3import argparse 

4from pathlib import Path 

5from typing import NamedTuple 

6 

7import numpy as np 

8import yaml 

9 

10import recon3d.utility as ut 

11 

12 

13class Recipe(NamedTuple): 

14 """Defines the recipe to run this module directly from its Python API. 

15 

16 Attributes 

17 ---------- 

18 """ 

19 

20 image_dir: Path 

21 image_type: str 

22 out_dir: Path 

23 

24 

25def save_image_stack_to_array(image_dir: Path, file_extension: str, out_dir: Path): 

26 """ 

27 Reads all images with a specified file extension from a directory, 

28 stores them in a numpy array, and saves the array to a .npy file in the 

29 specified directory. 

30 

31 Parameters: 

32 image_dir: The directory from which to read images. 

33 file_extension: The file extension of the images to read. 

34 out_dir: The directory where the .npy file will be saved. 

35 """ 

36 try: 

37 # Assuming 'ut.read_images' is a valid function that reads images and 

38 # returns a numpy array 

39 array_data = ut.read_images(image_dir, file_extension) 

40 

41 # Correctly form the output file path 

42 out_file_path = out_dir.joinpath(f"{image_dir.name}.npy") 

43 

44 # Save the numpy array to a .npy file 

45 np.save(out_file_path, array_data) 

46 

47 print(f"Images from {image_dir} saved to {out_file_path}") 

48 

49 except Exception as e: 

50 print(f"An error occurred: {e}") 

51 

52 

53def validate_recipe(*, recipe: Recipe) -> bool: 

54 """Validate the given recipe. 

55 

56 Ensures that all values in the Recipe NamedTuple are valid. 

57 

58 Parameters 

59 ---------- 

60 recipe : Recipe 

61 The recipe to validate. 

62 

63 Raises 

64 ------ 

65 AssertionError 

66 If any of the recipe attributes are invalid. 

67 

68 Examples 

69 -------- 

70 >>> recipe = Recipe( 

71 ... input_path=Path("path/to/input"), 

72 ... input_file_type=".tif", 

73 ... output_path=Path("path/to/output") 

74 ... ) 

75 >>> validate_recipe(recipe) 

76 True 

77 """ 

78 assert isinstance(recipe.image_dir, Path), "image_dir must be a Path object" 

79 assert recipe.image_dir.is_dir(), "image_dir must be a directory" 

80 assert isinstance(recipe.image_type, str), "image_type must be a string" 

81 assert recipe.image_type in [ 

82 ".tif", 

83 ".tiff", 

84 ], "image_type must be .tif or .tiff" 

85 assert isinstance(recipe.out_dir, Path), "out_path must be a Path object" 

86 assert recipe.out_dir.is_dir(), "out_path must be a directory" 

87 

88 return True 

89 

90 

91def image_to_stack_array(*, yml_input_file: Path) -> bool: 

92 """Converts a series of images to a stack array. 

93 

94 Parameters 

95 ---------- 

96 yml_input_file : Path 

97 The path to the input .yml file containing the image paths and other 

98 parameters. 

99 

100 Returns 

101 ------- 

102 True if the conversion is successful, False otherwise. 

103 

104 Raises 

105 ------ 

106 FileNotFoundError 

107 If the input .yml file is not found. 

108 TypeError 

109 If the input file type is not supported. 

110 OSError 

111 If there is an error with the yml module. 

112 """ 

113 print(f"This is {Path(__file__).resolve()}") 

114 

115 fin = yml_input_file.resolve().expanduser() 

116 

117 print(f"Processing file: {fin}") 

118 

119 if not fin.is_file(): 

120 raise FileNotFoundError(f"File not found: {str(fin)}") 

121 

122 file_type = fin.suffix.casefold() 

123 supported_types = (".yaml", ".yml") 

124 

125 if file_type not in supported_types: 

126 raise TypeError("Only file types .yaml, and .yml are supported.") 

127 

128 db = [] 

129 

130 try: 

131 with open(file=fin, mode="r", encoding="utf-8") as stream: 

132 db = yaml.load(stream, Loader=yaml.SafeLoader) # overwrite 

133 except yaml.YAMLError as error: 

134 print(f"Error with yml module: {error}") 

135 print(f"Could not open or decode: {fin}") 

136 raise OSError from error 

137 

138 print(f"Success: database created from file: {fin}") 

139 print(db) 

140 

141 recipe = Recipe( 

142 image_dir=Path(db["image_dir"]).expanduser(), 

143 image_type=db["image_type"], 

144 out_dir=Path(db["out_dir"]).expanduser(), 

145 ) 

146 

147 validate_recipe(recipe=recipe) 

148 

149 save_image_stack_to_array( 

150 image_dir=recipe.image_dir, 

151 file_extension=recipe.image_type, 

152 out_dir=recipe.out_dir, 

153 ) 

154 

155 return True # success 

156 

157 

158def main(): 

159 """ 

160 Runs the module from the command line. 

161 

162 This function serves as the entry point for terminal-based access to the 

163 module. It uses the argparse library to parse command-line arguments. 

164 

165 Parameters 

166 ---------- 

167 None 

168 

169 Returns 

170 ------- 

171 None 

172 

173 Examples 

174 -------- 

175 To run the module, use the following command in the terminal: 

176 $ image_to_stack_array path/to/input.yml 

177 """ 

178 

179 parser = argparse.ArgumentParser() 

180 parser.add_argument("input_file", help="the .yml input file") 

181 args = parser.parse_args() 

182 input_file = args.input_file 

183 input_file = Path(input_file).expanduser() 

184 

185 image_to_stack_array(yml_input_file=input_file) 

186 

187 

188if __name__ == "__main__": 

189 main()