Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions src/vstarstack/library/objects/features.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import numpy as np

import imutils
import imutils.contours

from skimage import measure
import matplotlib.pyplot as plt

Expand All @@ -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 = {
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand All @@ -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')
Expand Down
10 changes: 6 additions & 4 deletions src/vstarstack/tool/objects/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,20 @@
"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),
"minValue" : (float, 0.1),
"minPixel" : (int, 5),
"maxPixel" : (int, 20),
},
"orb" : {},
"orb" : {
"patchSize" : (int, 31),
},
}),
}

Expand Down
103 changes: 0 additions & 103 deletions src/vstarstack/tool/objects/features.py

This file was deleted.

128 changes: 128 additions & 0 deletions src/vstarstack/tool/objects/find_features.py
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.
#

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/")
}
63 changes: 63 additions & 0 deletions src/vstarstack/tool/objects/match_features.py
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.
#

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)
Loading