Skip to content

Commit f3d38f5

Browse files
authored
Split finding and matching features to separate stages (#111)
1 parent 156a284 commit f3d38f5

File tree

6 files changed

+212
-115
lines changed

6 files changed

+212
-115
lines changed

src/vstarstack/library/objects/features.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
import numpy as np
1717

1818
import imutils
19+
import imutils.contours
20+
1921
from skimage import measure
2022
import matplotlib.pyplot as plt
2123

@@ -33,9 +35,11 @@ def get_subimage(image, num_splits):
3335
subimage = image[base_y:next_y, base_x:next_x]
3436
yield subimage, base_x, base_y
3537

36-
def _find_keypoints_orb(image, base_x, base_y, detector):
38+
def _find_keypoints_orb(image : np.ndarray, base_x : int, base_y : int, detector):
3739
"""Find keypoints with ORB detector"""
3840
cpts = []
41+
if image.dtype != np.uint8:
42+
image = (image * 255 / np.amax(image)).astype("uint8")
3943
points = detector.detect(image, mask=None)
4044
for point in points:
4145
pdesc = {
@@ -46,9 +50,9 @@ def _find_keypoints_orb(image, base_x, base_y, detector):
4650
cpts.append(pdesc)
4751
return cpts
4852

49-
def find_keypoints_orb(image, num_splits):
53+
def find_keypoints_orb(image, num_splits, param):
5054
points = []
51-
orb = cv2.ORB_create()
55+
orb = cv2.ORB_create(patchSize=param["patchSize"])
5256
for subimage, bx, by in get_subimage(image, num_splits):
5357
points += _find_keypoints_orb(subimage, bx, by, orb)
5458
return points
@@ -108,7 +112,8 @@ def find_keypoints_brightness(image, num_splits, params):
108112
return points
109113

110114
def describe_keypoints(image : np.ndarray,
111-
keypoints : list) -> list:
115+
keypoints : list,
116+
param : dict) -> list:
112117
"""
113118
Build keypoints and calculate their descriptors
114119
@@ -120,7 +125,7 @@ def describe_keypoints(image : np.ndarray,
120125
121126
Return: list of keypoint and list of descriptors
122127
"""
123-
orb = cv2.ORB_create()
128+
orb = cv2.ORB_create(patchSize=param["patchSize"])
124129
kps = [cv2.KeyPoint(point["x"], point["y"], point["size"]) for point in keypoints]
125130
image = np.clip(image / np.amax(image), 0, 1)*255
126131
image = image.astype('uint8')

src/vstarstack/tool/objects/config.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,20 @@
3030
"max_diameter": (int, 40),
3131
}),
3232
"features" : ("module", {
33-
"detector" : (str, "brightness"),
33+
"path" : (str, "features/"),
3434
"num_splits" : (int, 4),
35-
"max_feature_delta" : (int, 10),
36-
"features_percent" : (int, 20),
35+
"max_feature_delta" : (int, 20),
36+
"features_percent" : (int, 100),
3737
"bright_spots": {
3838
"blurSize" : (int, 21),
3939
"k_thr" : (float, 1.15),
4040
"minValue" : (float, 0.1),
4141
"minPixel" : (int, 5),
4242
"maxPixel" : (int, 20),
4343
},
44-
"orb" : {},
44+
"orb" : {
45+
"patchSize" : (int, 31),
46+
},
4547
}),
4648
}
4749

src/vstarstack/tool/objects/features.py

Lines changed: 0 additions & 103 deletions
This file was deleted.
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#
2+
# Copyright (c) 2023-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 os
16+
import json
17+
18+
import vstarstack.tool.common
19+
import vstarstack.tool.cfg
20+
import vstarstack.library.data
21+
import vstarstack.library.common
22+
from vstarstack.library.objects.features import find_keypoints_orb
23+
from vstarstack.library.objects.features import find_keypoints_brightness
24+
from vstarstack.library.objects.features import describe_keypoints
25+
26+
def build_keypoints_structure(keypoints, ds, fname, name):
27+
record = {
28+
"fname" : fname,
29+
"name" : name,
30+
"points" : [],
31+
}
32+
33+
for keypoint, desc in zip(keypoints, ds):
34+
for di in desc:
35+
if di != 0:
36+
break
37+
else:
38+
continue
39+
record["points"].append({
40+
"keypoint" : keypoint,
41+
"descriptor" : [int(item) for item in list(desc)],
42+
})
43+
44+
return record
45+
46+
def proj_find_keypoints_orb(files, num_splits, param):
47+
points = {}
48+
49+
for fname in files:
50+
name = os.path.splitext(os.path.basename(fname))[0]
51+
dataframe = vstarstack.library.data.DataFrame.load(fname)
52+
gray, _ = vstarstack.library.common.df_to_light(dataframe)
53+
54+
keypoints = find_keypoints_orb(gray, num_splits, param)
55+
ds = describe_keypoints(gray, keypoints, param)
56+
points[name] = build_keypoints_structure(keypoints, ds, fname, name)
57+
58+
return points
59+
60+
def proj_find_keypoints_brightness(files, num_splits, param, orb_param):
61+
points = {}
62+
63+
for fname in files:
64+
name = os.path.splitext(os.path.basename(fname))[0]
65+
dataframe = vstarstack.library.data.DataFrame.load(fname)
66+
gray, _ = vstarstack.library.common.df_to_light(dataframe)
67+
68+
keypoints = find_keypoints_brightness(gray, num_splits, param)
69+
ds = describe_keypoints(gray, keypoints, orb_param)
70+
points[name] = build_keypoints_structure(keypoints, ds, fname, name)
71+
72+
return points
73+
74+
def save_features(points, features_path):
75+
for name in points:
76+
record = points[name]
77+
with open(os.path.join(features_path, f"{name}_keypoints.json"), "w") as f:
78+
json.dump(record, f, indent=4, ensure_ascii=False)
79+
80+
def find_points_orb(project: vstarstack.tool.cfg.Project, argv: list[str]):
81+
if len(argv) >= 2:
82+
inputs = argv[0]
83+
features = argv[1]
84+
else:
85+
inputs = project.config.paths.npy_fixed
86+
features = project.config.objects.features.path
87+
88+
num_splits = project.config.objects.features.num_splits
89+
param = {
90+
"patchSize" : project.config.objects.features.orb.patchSize
91+
}
92+
93+
files = vstarstack.tool.common.listfiles(inputs, ".zip")
94+
files = [filename for _, filename in files]
95+
points = proj_find_keypoints_orb(files, num_splits, param)
96+
save_features(points, features)
97+
98+
def find_points_brightness(project: vstarstack.tool.cfg.Project, argv: list[str]):
99+
if len(argv) >= 2:
100+
inputs = argv[0]
101+
features = argv[1]
102+
else:
103+
inputs = project.config.paths.npy_fixed
104+
features = project.config.objects.features.path
105+
106+
num_splits = project.config.objects.features.num_splits
107+
108+
orb_param = {
109+
"patchSize" : project.config.objects.features.orb.patchSize
110+
}
111+
112+
param = {
113+
"blur_size" : project.config.objects.features.bright_spots.blurSize,
114+
"k_thr" : project.config.objects.features.bright_spots.k_thr,
115+
"min_value" : project.config.objects.features.bright_spots.minValue,
116+
"min_pixel" : project.config.objects.features.bright_spots.minPixel,
117+
"max_pixel" : project.config.objects.features.bright_spots.maxPixel,
118+
}
119+
120+
files = vstarstack.tool.common.listfiles(inputs, ".zip")
121+
files = [filename for _, filename in files]
122+
points = proj_find_keypoints_brightness(files, num_splits, param, orb_param)
123+
save_features(points, features)
124+
125+
commands = {
126+
"brightness": (find_points_brightness, "find keypoints with brightness detector", "npys/ features/"),
127+
"orb": (find_points_orb, "find keypoints with ORB detector", "npys/ features/")
128+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#
2+
# Copyright (c) 2023-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 math
16+
import json
17+
import numpy as np
18+
19+
import vstarstack.tool.common
20+
import vstarstack.tool.cfg
21+
import vstarstack.library.data
22+
from vstarstack.library.objects.features import build_clusters
23+
24+
def load_keypoints(files):
25+
points = {}
26+
for file in files:
27+
with open(file) as f:
28+
record = json.load(f)
29+
name = record["name"]
30+
points[name] = record
31+
return points
32+
33+
def run(project: vstarstack.tool.cfg.Project, argv: list[str]):
34+
if len(argv) >= 2:
35+
points_path = argv[0]
36+
clusters_fname = argv[1]
37+
else:
38+
points_path = project.config.paths.npy_fixed
39+
clusters_fname = project.config.cluster.path
40+
41+
max_feature_delta = project.config.objects.features.max_feature_delta
42+
features_percent = project.config.objects.features.features_percent / 100.0
43+
44+
files = vstarstack.tool.common.listfiles(points_path, ".json")
45+
files = [filename for _, filename in files]
46+
keypoints = load_keypoints(files)
47+
print("Found keypoints")
48+
49+
points = {}
50+
descs = {}
51+
52+
for name in keypoints:
53+
points[name] = [item["keypoint"] for item in keypoints[name]["points"]]
54+
descs[name] = np.array([np.array(item["descriptor"], dtype=np.uint8) for item in keypoints[name]["points"]])
55+
56+
clusters = build_clusters(points, descs,
57+
max_feature_delta,
58+
features_percent)
59+
60+
print("Builded clusters")
61+
vstarstack.tool.common.check_dir_exists(clusters_fname)
62+
with open(clusters_fname, "w") as f:
63+
json.dump(clusters, f, indent=4, ensure_ascii=False)

0 commit comments

Comments
 (0)