Skip to content

Commit 3d935d6

Browse files
authored
Image wave refactor (#99)
Big refactoring of fine shift code
1 parent 7176496 commit 3d935d6

40 files changed

+2676
-1650
lines changed

.vscode/c_cpp_properties.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
{
44
"name": "Linux",
55
"includePath": [
6-
"${workspaceFolder}/**"
6+
"${workspaceFolder}/**",
7+
"${workspaceFolder}/src/vstarstack/library/fine_movement/libimagedeform/include",
8+
"/usr/include/python3.11"
79
],
810
"defines": [],
911
"compilerPath": "/usr/bin/gcc",
10-
"cStandard": "gnu17",
11-
"cppStandard": "gnu++17",
12+
"cStandard": "c17",
13+
"cppStandard": "c++17",
1214
"intelliSenseMode": "linux-gcc-x64"
1315
}
1416
],

.vscode/settings.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@
44
"random": "c",
55
"math.h": "c",
66
"unistd.h": "c",
7-
"sphere.h": "c"
7+
"sphere.h": "c",
8+
"image_grid.h": "c",
9+
"image_deform.h": "c",
10+
"image_deform_gc.h": "c",
11+
"stdlib.h": "c",
12+
"structmember.h": "c",
13+
"imagedeform_gc.h": "c"
814
},
915
"editor.tabSize": 4,
1016
"editor.insertSpaces": true,

setup.py

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,26 @@
3535
np.get_include(),
3636
])
3737

38-
image_wave = Extension(name="vstarstack.library.fine_shift.image_wave",
39-
sources=["src/vstarstack/library/fine_shift/image_wave.c",
40-
"src/vstarstack/library/fine_shift/image_wave_interpolation.c",
41-
"src/vstarstack/library/fine_shift/image_wave_targets.c",
42-
"src/vstarstack/library/fine_shift/image_wave_correlation.c",
43-
"src/vstarstack/library/fine_shift/image_wave_module.c",
44-
"src/vstarstack/library/fine_shift/image_wave_image.c",
45-
], include_dirs=[np.get_include()])
38+
libimagedeform_root = "src/vstarstack/library/fine_movement/libimagedeform"
39+
libimagedeform_headers = [libimagedeform_root + "/include"]
40+
libimagedeform_sources = [libimagedeform_root + "/src/interpolation.c",
41+
libimagedeform_root + "/src/image_grid.c",
42+
libimagedeform_root + "/src/image_deform.c",
43+
libimagedeform_root + "/src/image_deform_gc.c",
44+
libimagedeform_root + "/src/image_deform_lc.c",
45+
]
46+
47+
imagedeform_root = "src/vstarstack/library/fine_movement/module"
48+
imagedeform_sources = [imagedeform_root + "/imagegrid.c",
49+
imagedeform_root + "/imagedeform.c",
50+
imagedeform_root + "/imagedeform_gc.c",
51+
imagedeform_root + "/imagedeform_lc.c",
52+
imagedeform_root + "/imagedeform_module.c",
53+
]
54+
55+
image_deform = Extension(name="vstarstack.library.fine_movement.module",
56+
sources=imagedeform_sources+libimagedeform_sources,
57+
include_dirs=[np.get_include()]+libimagedeform_headers)
4658

4759
root = os.path.join(os.path.abspath(os.path.dirname(__file__)), "src")
4860
result = [os.path.join(dp, f) for dp, dn, filenames in os.walk(root)
@@ -63,7 +75,8 @@
6375
packages=packages,
6476
ext_modules = [projection,
6577
movements,
66-
image_wave],
78+
image_deform,
79+
],
6780
install_requires = [
6881
'numpy',
6982
'astropy',
File renamed without changes.
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
#
2+
# Copyright (c) 2022-2024 Vladislav Tsendrovskii
3+
#
4+
# This program is free software: you can redistribute it and/or modify
5+
# it under the terms of the GNU General Public License as published by
6+
# the Free Software Foundation, version 3 of the License.
7+
# This program is distributed in the hope that it will be useful,
8+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10+
# See the GNU General Public License for more details.
11+
# You should have received a copy of the GNU General Public License
12+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
13+
#
14+
15+
import numpy as np
16+
import scipy
17+
18+
from vstarstack.library.fine_movement.module import ImageGrid
19+
from vstarstack.library.fine_movement.module import ImageDeform
20+
from vstarstack.library.fine_movement.module import ImageDeformLC
21+
from vstarstack.library.fine_movement.module import ImageDeformGC
22+
23+
from vstarstack.library.data import DataFrame
24+
25+
def _cluster_average(cluster):
26+
xs = [cluster[name]["x"] for name in cluster]
27+
ys = [cluster[name]["y"] for name in cluster]
28+
av = {
29+
"x" : sum(xs) / len(xs),
30+
"y" : sum(ys) / len(ys),
31+
}
32+
return av
33+
34+
class Aligner:
35+
"""Alignment applier"""
36+
37+
def __init__(self, image_w : int, image_h : int, shift_array : np.ndarray):
38+
self.image_w = image_w
39+
self.image_h = image_h
40+
self.grid_h = shift_array.shape[0]
41+
self.grid_w = shift_array.shape[1]
42+
self.deform = ImageDeform(self.image_w, self.image_h, self.grid_w, self.grid_h)
43+
self.deform.fill(shift_array=shift_array)
44+
45+
def apply_alignment(self, dataframe : DataFrame, subpixels : int):
46+
"""Apply alignment descriptor to file"""
47+
for channel in dataframe.get_channels():
48+
image, opts = dataframe.get_channel(channel)
49+
if opts["encoded"]:
50+
continue
51+
w = image.shape[1]
52+
h = image.shape[0]
53+
grid = ImageGrid(w, h)
54+
grid.fill(image.astype('double'))
55+
fixed_grid = self.deform.apply_image(image=grid, subpixels=subpixels)
56+
fixed = fixed_grid.content()
57+
fixed[np.where(np.isnan(fixed))] = 0
58+
dataframe.replace_channel(fixed, channel)
59+
return dataframe
60+
61+
def serialize(self):
62+
"""Serialize image deform"""
63+
nitems = self.grid_h * self.grid_w * 2
64+
shift_array = np.reshape(self.deform.content(), (nitems,))
65+
return {
66+
"grid_w" : self.grid_w,
67+
"grid_h" : self.grid_h,
68+
"image_w" : self.image_w,
69+
"image_h" : self.image_h,
70+
"array" : list(shift_array),
71+
}
72+
73+
@staticmethod
74+
def deserialize(description : dict):
75+
"""Deserialize image deform"""
76+
grid_h = description["grid_h"]
77+
grid_w = description["grid_w"]
78+
shift_array = np.reshape(description["array"], (grid_h, grid_w, 2))
79+
return Aligner(description["image_w"], description["image_h"], shift_array)
80+
81+
class ClusterAlignerBuilder:
82+
def __init__(self, image_w, image_h, grid_w, grid_h, spk, num_steps, min_points, dh):
83+
self.image_w = image_w
84+
self.image_h = image_h
85+
self.grid_w = grid_w
86+
self.grid_h = grid_h
87+
self.spk = spk
88+
self.num_steps = num_steps
89+
self.min_points = min_points
90+
self.dh = dh
91+
self.correlator = ImageDeformGC(self.image_w, self.image_h,
92+
self.grid_w, self.grid_h,
93+
self.spk)
94+
95+
def find_alignment(self, name : str, clusters : list) -> Aligner:
96+
"""Find alignment of image `name` using clusters"""
97+
expected_points = []
98+
actual_points = []
99+
for cluster in clusters:
100+
if name not in cluster:
101+
continue
102+
average = _cluster_average(cluster)
103+
104+
# we need reverse transformation
105+
x = average["x"]
106+
y = average["y"]
107+
expected_points.append((x, y))
108+
x = cluster[name]["x"]
109+
y = cluster[name]["y"]
110+
actual_points.append((x, y))
111+
112+
print(f"\tusing {len(expected_points)} points")
113+
if len(expected_points) < self.min_points:
114+
print("\tskip - too low points")
115+
return None
116+
117+
deform = self.correlator.find(points=actual_points,
118+
expected_points=expected_points,
119+
dh=self.dh,
120+
Nsteps=self.num_steps)
121+
shift_array = deform.content()
122+
return Aligner(self.image_w, self.image_h, shift_array)
123+
124+
def find_all_alignments(self, clusters : list) -> dict:
125+
"""Build alignment descriptor using clusters"""
126+
names = []
127+
for cluster in clusters:
128+
names += cluster.keys()
129+
names = set(names)
130+
deforms = {}
131+
for name in names:
132+
deform = self.find_alignment(name, clusters)
133+
if deform is not None:
134+
deforms[name] = deform
135+
return deforms
136+
137+
class CorrelationAlignedBuilder:
138+
139+
def __init__(self,
140+
image_w : int,
141+
image_h : int,
142+
pixels : int,
143+
radius : int,
144+
maximal_shift : float,
145+
subpixels : int):
146+
self.image_w = image_w
147+
self.image_h = image_h
148+
self.pixels = pixels
149+
self.correlator = ImageDeformLC(self.image_w, self.image_h, self.pixels)
150+
151+
self.radius = radius
152+
self.max_shift = maximal_shift
153+
self.subpixels = subpixels
154+
155+
def find_alignment(self, image : np.ndarray,
156+
image_ref : np.ndarray,
157+
pre_align : Aligner | None,
158+
pre_align_ref : Aligner | None,
159+
smooth : int | None) -> Aligner:
160+
"""Build alignment descriptor of image using correlations"""
161+
if image.shape != image_ref.shape:
162+
return None
163+
w = image.shape[1]
164+
h = image.shape[0]
165+
166+
grid = ImageGrid(w, h)
167+
grid.fill(image)
168+
grid_ref = ImageGrid(w, h)
169+
grid_ref.fill(image_ref)
170+
171+
deform_img = pre_align.deform if pre_align is not None else None
172+
deform_ref = pre_align_ref.deform if pre_align_ref is not None else None
173+
deform = self.correlator.find(grid, deform_img,
174+
grid_ref, deform_ref,
175+
self.radius,
176+
self.max_shift,
177+
self.subpixels)
178+
print(f"smooth = {smooth}")
179+
if smooth is not None:
180+
data = deform.content()
181+
data = scipy.ndimage.gaussian_filter(data, sigma=smooth, axes=(0,1))
182+
deform.fill(data)
183+
shift_array = deform.content()
184+
return Aligner(self.image_w, self.image_h, shift_array)
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
cmake_minimum_required(VERSION 3.14)
2+
3+
project(imagedeform)
4+
5+
# Library sources
6+
add_library(imagedeform STATIC src/interpolation.c
7+
src/image_grid.c
8+
src/image_deform.c
9+
src/image_deform_gc.c
10+
src/image_deform_lc.c)
11+
12+
set(HEADERS "include/interpolation.h;"
13+
"include/image_grid.h;"
14+
"include/image_deform.h;"
15+
"include/image_deform_gc.h;"
16+
"include/image_deform_lc.h")
17+
18+
target_include_directories(imagedeform PUBLIC include)
19+
set_target_properties(imagedeform PROPERTIES PUBLIC_HEADER "${HEADERS}")
20+
21+
# Fail on warnings
22+
if(MSVC)
23+
target_compile_options(imagedeform PRIVATE /W4 /WX)
24+
else()
25+
target_compile_options(imagedeform PRIVATE -Wall -Wextra -Wpedantic -Werror)
26+
endif()
27+
28+
install(TARGETS imagedeform
29+
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/libimagedeform)
30+
31+
# Tests
32+
if (BUILD_TESTS)
33+
set(INSTALL_GTEST OFF)
34+
include(FetchContent)
35+
FetchContent_Declare(
36+
googletest
37+
URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
38+
)
39+
40+
# For Windows: Prevent overriding the parent project's compiler/linker settings
41+
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
42+
FetchContent_MakeAvailable(googletest)
43+
44+
enable_testing()
45+
add_executable(
46+
deform_test
47+
tests/deform_test.cc
48+
tests/deform_test_gc.cc
49+
)
50+
51+
target_link_libraries(
52+
deform_test
53+
GTest::gtest_main
54+
imagedeform
55+
)
56+
57+
include(GoogleTest)
58+
gtest_discover_tests(deform_test)
59+
endif()

0 commit comments

Comments
 (0)