diff --git a/src/vstarstack/library/objects/features.py b/src/vstarstack/library/objects/features.py
index a5366dac..ebde642b 100644
--- a/src/vstarstack/library/objects/features.py
+++ b/src/vstarstack/library/objects/features.py
@@ -16,6 +16,8 @@
import numpy as np
import imutils
+import imutils.contours
+
from skimage import measure
import matplotlib.pyplot as plt
@@ -33,9 +35,11 @@ def get_subimage(image, num_splits):
subimage = image[base_y:next_y, base_x:next_x]
yield subimage, base_x, base_y
-def _find_keypoints_orb(image, base_x, base_y, detector):
+def _find_keypoints_orb(image : np.ndarray, base_x : int, base_y : int, detector):
"""Find keypoints with ORB detector"""
cpts = []
+ if image.dtype != np.uint8:
+ image = (image * 255 / np.amax(image)).astype("uint8")
points = detector.detect(image, mask=None)
for point in points:
pdesc = {
@@ -46,9 +50,9 @@ def _find_keypoints_orb(image, base_x, base_y, detector):
cpts.append(pdesc)
return cpts
-def find_keypoints_orb(image, num_splits):
+def find_keypoints_orb(image, num_splits, param):
points = []
- orb = cv2.ORB_create()
+ orb = cv2.ORB_create(patchSize=param["patchSize"])
for subimage, bx, by in get_subimage(image, num_splits):
points += _find_keypoints_orb(subimage, bx, by, orb)
return points
@@ -108,7 +112,8 @@ def find_keypoints_brightness(image, num_splits, params):
return points
def describe_keypoints(image : np.ndarray,
- keypoints : list) -> list:
+ keypoints : list,
+ param : dict) -> list:
"""
Build keypoints and calculate their descriptors
@@ -120,7 +125,7 @@ def describe_keypoints(image : np.ndarray,
Return: list of keypoint and list of descriptors
"""
- orb = cv2.ORB_create()
+ orb = cv2.ORB_create(patchSize=param["patchSize"])
kps = [cv2.KeyPoint(point["x"], point["y"], point["size"]) for point in keypoints]
image = np.clip(image / np.amax(image), 0, 1)*255
image = image.astype('uint8')
diff --git a/src/vstarstack/tool/objects/config.py b/src/vstarstack/tool/objects/config.py
index ba5105d3..0ac7e221 100644
--- a/src/vstarstack/tool/objects/config.py
+++ b/src/vstarstack/tool/objects/config.py
@@ -30,10 +30,10 @@
"max_diameter": (int, 40),
}),
"features" : ("module", {
- "detector" : (str, "brightness"),
+ "path" : (str, "features/"),
"num_splits" : (int, 4),
- "max_feature_delta" : (int, 10),
- "features_percent" : (int, 20),
+ "max_feature_delta" : (int, 20),
+ "features_percent" : (int, 100),
"bright_spots": {
"blurSize" : (int, 21),
"k_thr" : (float, 1.15),
@@ -41,7 +41,9 @@
"minPixel" : (int, 5),
"maxPixel" : (int, 20),
},
- "orb" : {},
+ "orb" : {
+ "patchSize" : (int, 31),
+ },
}),
}
diff --git a/src/vstarstack/tool/objects/features.py b/src/vstarstack/tool/objects/features.py
deleted file mode 100644
index 46ce4ca3..00000000
--- a/src/vstarstack/tool/objects/features.py
+++ /dev/null
@@ -1,103 +0,0 @@
-#
-# Copyright (c) 2023 Vladislav Tsendrovskii
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, version 3 of the License.
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-# See the GNU General Public License for more details.
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-#
-
-import os
-import json
-import numpy as np
-
-import vstarstack.tool.common
-import vstarstack.tool.cfg
-import vstarstack.library.data
-from vstarstack.library.objects.features import find_keypoints_orb
-from vstarstack.library.objects.features import find_keypoints_brightness
-from vstarstack.library.objects.features import describe_keypoints
-from vstarstack.library.objects.features import build_clusters
-
-
-def find_keypoints(files, num_splits, detector_type, param):
- points = {}
- descs = {}
- fnames = {}
-
- for fname in files:
- name = os.path.splitext(os.path.basename(fname))[0]
- #print(name)
- fnames[name] = fname
- dataframe = vstarstack.library.data.DataFrame.load(fname)
- for channel in dataframe.get_channels():
- image, opts = dataframe.get_channel(channel)
- if not opts["brightness"]:
- continue
- if channel not in points:
- points[channel] = {}
- descs[channel] = {}
-
- if detector_type == "orb":
- keypoints = find_keypoints_orb(image, num_splits)
- elif detector_type == "brightness":
- keypoints = find_keypoints_brightness(image, num_splits, param)
- else:
- raise Exception(f"Invalid detector {detector_type}")
-
- ds = describe_keypoints(image, keypoints)
- points[channel][name] = keypoints
- descs[channel][name] = ds
-
- return points, descs, fnames
-
-def run(project: vstarstack.tool.cfg.Project, argv: list[str]):
- if len(argv) >= 2:
- inputs = argv[0]
- clusters_fname = argv[1]
- else:
- inputs = project.config.paths.npy_fixed
- clusters_fname = project.config.cluster.path
-
- detector_type = project.config.objects.features.detector
- num_splits = project.config.objects.features.num_splits
- max_feature_delta = project.config.objects.features.max_feature_delta
- features_percent = project.config.objects.features.features_percent / 100
-
- if detector_type == "orb":
- param = None
- print("Using ORB detector")
- elif detector_type == "brightness":
- param = {
- "blur_size" : project.config.objects.features.bright_spots.blurSize,
- "k_thr" : project.config.objects.features.bright_spots.k_thr,
- "min_value" : project.config.objects.features.bright_spots.minValue,
- "min_pixel" : project.config.objects.features.bright_spots.minPixel,
- "max_pixel" : project.config.objects.features.bright_spots.maxPixel,
- }
- print("Using brightness detector")
-
- files = vstarstack.tool.common.listfiles(inputs, ".zip")
- files = [filename for _, filename in files]
- points, descs, _ = find_keypoints(files, num_splits, detector_type, param)
- print("Found keypoints")
-
- total_clusters = []
- for channel in points:
- print(f"\tBuild clusters for {channel} channel")
- crd_clusters = build_clusters(points[channel],
- descs[channel],
- max_feature_delta,
- features_percent)
-
- total_clusters += crd_clusters
-
- print("Builded clusters")
- vstarstack.tool.common.check_dir_exists(clusters_fname)
- with open(clusters_fname, "w") as f:
- json.dump(total_clusters, f, indent=4, ensure_ascii=False)
diff --git a/src/vstarstack/tool/objects/find_features.py b/src/vstarstack/tool/objects/find_features.py
new file mode 100644
index 00000000..faeb783a
--- /dev/null
+++ b/src/vstarstack/tool/objects/find_features.py
@@ -0,0 +1,128 @@
+#
+# Copyright (c) 2023-2024 Vladislav Tsendrovskii
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, version 3 of the License.
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU General Public License for more details.
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+
+import os
+import json
+
+import vstarstack.tool.common
+import vstarstack.tool.cfg
+import vstarstack.library.data
+import vstarstack.library.common
+from vstarstack.library.objects.features import find_keypoints_orb
+from vstarstack.library.objects.features import find_keypoints_brightness
+from vstarstack.library.objects.features import describe_keypoints
+
+def build_keypoints_structure(keypoints, ds, fname, name):
+ record = {
+ "fname" : fname,
+ "name" : name,
+ "points" : [],
+ }
+
+ for keypoint, desc in zip(keypoints, ds):
+ for di in desc:
+ if di != 0:
+ break
+ else:
+ continue
+ record["points"].append({
+ "keypoint" : keypoint,
+ "descriptor" : [int(item) for item in list(desc)],
+ })
+
+ return record
+
+def proj_find_keypoints_orb(files, num_splits, param):
+ points = {}
+
+ for fname in files:
+ name = os.path.splitext(os.path.basename(fname))[0]
+ dataframe = vstarstack.library.data.DataFrame.load(fname)
+ gray, _ = vstarstack.library.common.df_to_light(dataframe)
+
+ keypoints = find_keypoints_orb(gray, num_splits, param)
+ ds = describe_keypoints(gray, keypoints, param)
+ points[name] = build_keypoints_structure(keypoints, ds, fname, name)
+
+ return points
+
+def proj_find_keypoints_brightness(files, num_splits, param, orb_param):
+ points = {}
+
+ for fname in files:
+ name = os.path.splitext(os.path.basename(fname))[0]
+ dataframe = vstarstack.library.data.DataFrame.load(fname)
+ gray, _ = vstarstack.library.common.df_to_light(dataframe)
+
+ keypoints = find_keypoints_brightness(gray, num_splits, param)
+ ds = describe_keypoints(gray, keypoints, orb_param)
+ points[name] = build_keypoints_structure(keypoints, ds, fname, name)
+
+ return points
+
+def save_features(points, features_path):
+ for name in points:
+ record = points[name]
+ with open(os.path.join(features_path, f"{name}_keypoints.json"), "w") as f:
+ json.dump(record, f, indent=4, ensure_ascii=False)
+
+def find_points_orb(project: vstarstack.tool.cfg.Project, argv: list[str]):
+ if len(argv) >= 2:
+ inputs = argv[0]
+ features = argv[1]
+ else:
+ inputs = project.config.paths.npy_fixed
+ features = project.config.objects.features.path
+
+ num_splits = project.config.objects.features.num_splits
+ param = {
+ "patchSize" : project.config.objects.features.orb.patchSize
+ }
+
+ files = vstarstack.tool.common.listfiles(inputs, ".zip")
+ files = [filename for _, filename in files]
+ points = proj_find_keypoints_orb(files, num_splits, param)
+ save_features(points, features)
+
+def find_points_brightness(project: vstarstack.tool.cfg.Project, argv: list[str]):
+ if len(argv) >= 2:
+ inputs = argv[0]
+ features = argv[1]
+ else:
+ inputs = project.config.paths.npy_fixed
+ features = project.config.objects.features.path
+
+ num_splits = project.config.objects.features.num_splits
+
+ orb_param = {
+ "patchSize" : project.config.objects.features.orb.patchSize
+ }
+
+ param = {
+ "blur_size" : project.config.objects.features.bright_spots.blurSize,
+ "k_thr" : project.config.objects.features.bright_spots.k_thr,
+ "min_value" : project.config.objects.features.bright_spots.minValue,
+ "min_pixel" : project.config.objects.features.bright_spots.minPixel,
+ "max_pixel" : project.config.objects.features.bright_spots.maxPixel,
+ }
+
+ files = vstarstack.tool.common.listfiles(inputs, ".zip")
+ files = [filename for _, filename in files]
+ points = proj_find_keypoints_brightness(files, num_splits, param, orb_param)
+ save_features(points, features)
+
+commands = {
+ "brightness": (find_points_brightness, "find keypoints with brightness detector", "npys/ features/"),
+ "orb": (find_points_orb, "find keypoints with ORB detector", "npys/ features/")
+}
diff --git a/src/vstarstack/tool/objects/match_features.py b/src/vstarstack/tool/objects/match_features.py
new file mode 100644
index 00000000..c4a7154d
--- /dev/null
+++ b/src/vstarstack/tool/objects/match_features.py
@@ -0,0 +1,63 @@
+#
+# Copyright (c) 2023-2024 Vladislav Tsendrovskii
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, version 3 of the License.
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU General Public License for more details.
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+
+import math
+import json
+import numpy as np
+
+import vstarstack.tool.common
+import vstarstack.tool.cfg
+import vstarstack.library.data
+from vstarstack.library.objects.features import build_clusters
+
+def load_keypoints(files):
+ points = {}
+ for file in files:
+ with open(file) as f:
+ record = json.load(f)
+ name = record["name"]
+ points[name] = record
+ return points
+
+def run(project: vstarstack.tool.cfg.Project, argv: list[str]):
+ if len(argv) >= 2:
+ points_path = argv[0]
+ clusters_fname = argv[1]
+ else:
+ points_path = project.config.paths.npy_fixed
+ clusters_fname = project.config.cluster.path
+
+ max_feature_delta = project.config.objects.features.max_feature_delta
+ features_percent = project.config.objects.features.features_percent / 100.0
+
+ files = vstarstack.tool.common.listfiles(points_path, ".json")
+ files = [filename for _, filename in files]
+ keypoints = load_keypoints(files)
+ print("Found keypoints")
+
+ points = {}
+ descs = {}
+
+ for name in keypoints:
+ points[name] = [item["keypoint"] for item in keypoints[name]["points"]]
+ descs[name] = np.array([np.array(item["descriptor"], dtype=np.uint8) for item in keypoints[name]["points"]])
+
+ clusters = build_clusters(points, descs,
+ max_feature_delta,
+ features_percent)
+
+ print("Builded clusters")
+ vstarstack.tool.common.check_dir_exists(clusters_fname)
+ with open(clusters_fname, "w") as f:
+ json.dump(clusters, f, indent=4, ensure_ascii=False)
diff --git a/src/vstarstack/tool/objects/objects.py b/src/vstarstack/tool/objects/objects.py
index 623ae1c0..71f8aba6 100644
--- a/src/vstarstack/tool/objects/objects.py
+++ b/src/vstarstack/tool/objects/objects.py
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2022 Vladislav Tsendrovskii
+# Copyright (c) 2022-2024 Vladislav Tsendrovskii
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -17,7 +17,8 @@
import vstarstack.tool.objects.cut
import vstarstack.tool.objects.config
-import vstarstack.tool.objects.features
+import vstarstack.tool.objects.find_features
+import vstarstack.tool.objects.match_features
def _enable_objects(project : vstarstack.tool.cfg.Project, _argv: list[str]):
project.config.enable_module("objects")
@@ -26,6 +27,7 @@ def _enable_objects(project : vstarstack.tool.cfg.Project, _argv: list[str]):
commands = {
"config": (_enable_objects, "configure compact_objects pipeline"),
"detect": ("vstarstack.tool.objects.detect", "detect compact objects"),
- "features": (vstarstack.tool.objects.features.run, "detect and match image features"),
+ "find-features": (vstarstack.tool.objects.find_features.commands, "detect image features"),
+ "match-features": (vstarstack.tool.objects.match_features.run, "match image features"),
"cut": (vstarstack.tool.objects.cut.run, "cut compact objects"),
}