-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathanalysis.py
172 lines (145 loc) · 6.59 KB
/
analysis.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# -*- coding: utf-8 -*-
from random import Random
from typing import Self
from numpy import zeros
from src.data_structures.aggregate_manager import AggregateManager
from src.data_structures.field import FieldNullError
from src.data_structures.field_manager import FieldManager
from src.utilities.config import Config
from src.utilities.geometry import orthogonalise_matrix, euler_angles
from src.data_structures.map_manager import MapManager
from src.data_structures.parameter_groups import ScanParams
from src.data_structures.phase import Phase
from src.utilities.utils import tuple_degrees
class Analysis:
def __init__(
self,
data_ref: str,
width: int,
height: int,
phases: dict[int, Phase],
phase_id_values: list[list[int]],
euler_angle_values: list[list[tuple[float, float, float] | None]],
pattern_quality_values: list[list[float]],
index_quality_values: list[list[float]],
config: Config,
reduction_factor: int = 0,
pixel_size: float = None,
local_unindexed_id: int = None,
):
if pixel_size is None:
pixel_size = config.data.pixel_size
self.params = ScanParams(data_ref, width, height, phases, pixel_size, reduction_factor)
self.config = config
self._random_source = Random(config.analysis.random_seed)
self.local_unindexed_id = local_unindexed_id
self.field = FieldManager(
self.params,
phase_id_values,
euler_angle_values,
pattern_quality_values,
index_quality_values,
self.config,
self._random_source,
)
self._map = None
self._cluster_aggregate = None
@property
def map(self) -> MapManager:
if self._map is None:
self._map = MapManager(self.field)
return self._map
@property
def cluster_aggregate(self) -> AggregateManager:
if self._cluster_aggregate is None:
self._cluster_aggregate = AggregateManager(self.field, self.field.orientation_cluster_id)
return self._cluster_aggregate
@property
def cluster_count(self) -> int:
return self.field._cluster_count
def _reduce_resolution(self) -> Self:
if self.params.width % 2 != 0 or self.params.height % 2 != 0:
raise ArithmeticError("Can only reduce resolution of scan with even width and height.")
data_ref = self.params.data_ref
width = self.params.width // 2
height = self.params.height // 2
phases = self.params.phases
reduction_factor = self.params.reduction_factor + 1
pixel_size = self.params.pixel_size * 2
config = self.config
local_unindexed_id = self.local_unindexed_id
phase_id_values: list[list[int | None]] = list()
euler_angle_values: list[list[tuple[float, float, float] | None]] = list()
index_quality_values: list[list[float]] = list()
pattern_quality_values: list[list[float]] = list()
for y in range(height):
phase_id_values.append(list())
euler_angle_values.append(list())
index_quality_values.append(list())
pattern_quality_values.append(list())
for x in range(width):
kernel = [(0, 0), (1, 0), (0, 1), (1, 1)]
kernel_phases = set()
for dx, dy in kernel:
try:
phase = self.field._phase_id.get_value_at(2 * x + dx, 2 * y + dy)
except FieldNullError:
continue
kernel_phases.add(phase)
if len(kernel_phases) != 1:
phase_id = None
euler_angle_aggregate = None
index_quality_aggregate = 0.0
pattern_quality_aggregate = 0.0
else:
phase_id = kernel_phases.pop()
count = 0
orientation_matrix_total = zeros((3, 3))
index_quality_total = 0.0
pattern_quality_total = 0.0
for dx, dy in kernel:
try:
self.field._phase_id.get_value_at(2 * x + dx, 2 * y + dy)
orientation_matrix = self.field.orientation_matrix.get_value_at(2 * x + dx, 2 * y + dy)
index_quality = self.field.index_quality.get_value_at(2 * x + dx, 2 * y + dy)
pattern_quality = self.field.pattern_quality.get_value_at(2 * x + dx, 2 * y + dy)
except FieldNullError:
continue
orientation_matrix_total += orientation_matrix
index_quality_total += index_quality
pattern_quality_total += pattern_quality
count += 1
try:
orientation_matrix_aggregate = orthogonalise_matrix(orientation_matrix_total / count, self.config.resolution.scaling_tolerance)
euler_angle_aggregate = tuple_degrees(euler_angles(orientation_matrix_aggregate, self.config.data.euler_axis_set))
index_quality_aggregate = index_quality_total / count
pattern_quality_aggregate = pattern_quality_total / len(kernel)
except ArithmeticError:
phase_id = None
euler_angle_aggregate = None
index_quality_aggregate = 0.0
pattern_quality_aggregate = 0.0
phase_id_values[y].append(phase_id)
euler_angle_values[y].append(euler_angle_aggregate)
index_quality_values[y].append(index_quality_aggregate)
pattern_quality_values[y].append(pattern_quality_aggregate)
analysis = Analysis(
data_ref=data_ref,
width=width,
height=height,
phases=phases,
phase_id_values=phase_id_values,
euler_angle_values=euler_angle_values,
pattern_quality_values=pattern_quality_values,
index_quality_values=index_quality_values,
config=config,
reduction_factor=reduction_factor,
pixel_size=pixel_size,
local_unindexed_id=local_unindexed_id,
)
return analysis
def reduce_resolution(self, reduction_factor: int) -> Self:
if reduction_factor <= 0:
return self
else:
return self._reduce_resolution().reduce_resolution(reduction_factor - 1)