Commit 32c236d6 authored by Julia Garriga Ferrer's avatar Julia Garriga Ferrer
Browse files

Merge branch 'silx_backend' into 'master'

Silx backend

See merge request julia.garriga/darfix!44
parents bbda9007 2605d313
Pipeline #22165 passed with stage
in 3 minutes and 17 seconds
......@@ -34,12 +34,15 @@ import numpy
from enum import Enum
from silx.image import sift
class Method(Enum):
"""
Methods available to compute the matching.
"""
feature_matching = "feature matching"
orb_feature_matching = "orb feature matching"
sift_feature_matching = "sift feature matching"
euclidean_distance = "euclidean distance"
@staticmethod
......@@ -57,24 +60,37 @@ class ComponentsMatching():
def __init__(self, components):
self._create_descriptors(components)
self.components = components
def _create_descriptors(self, components_list):
def _create_descriptors(self):
"""
Function that detects and computes the keypoints and descriptors for
the components.
"""
self.orb = cv2.ORB_create()
self.components = []
orb = cv2.ORB_create()
descripted_components = []
for array in components_list:
for array in self.components:
components = []
for image in array:
cv2.normalize(image, image, 0, 255, cv2.NORM_MINMAX)
image = image.astype(numpy.uint8)
kp, des = self.orb.detectAndCompute(image, None)
kp, des = orb.detectAndCompute(image, None)
components.append(Component(image, kp, des))
self.components.append(components)
descripted_components.append(components)
return descripted_components
def _create_sift_keypoints(self):
keypoints = []
for array in self.components:
sift_ocl = sift.SiftPlan(template=array[0], devicetype="CPU")
components = [sift_ocl(image) for image in array]
keypoints.append(components)
return keypoints
def euclidean_distance(self, X, Y):
"""
......@@ -85,7 +101,7 @@ class ComponentsMatching():
dst = numpy.linalg.norm(X - Y) # their euclidean distances
return dst
def match_components(self, id1=None, id2=None, method=Method.feature_matching):
def match_components(self, id1=None, id2=None, method=Method.orb_feature_matching):
"""
Match components. Given the components x1,...,xn of dataset 1 and the
components y1,...,ym of dataset 2, this function computes the pairs
......@@ -108,12 +124,14 @@ class ComponentsMatching():
matches = {}
final_matches = {}
if method == Method.feature_matching:
if method == Method.orb_feature_matching:
self.descriptors = self._create_descriptors()
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
# Match components with id1 and id2
for i, component1 in enumerate(self.components[id1]):
for i, component1 in enumerate(self.descriptors[id1]):
if component1.descriptor is not None:
for j, component2 in enumerate(self.components[id2]):
for j, component2 in enumerate(self.descriptors[id2]):
if component2.descriptor is not None:
# Match descriptors
matches[(i, j)] = bf.match(component1.descriptor,
......@@ -125,10 +143,26 @@ class ComponentsMatching():
if x not in final_matches.keys() and y not in final_matches.values():
final_matches[x] = y
elif method == Method.sift_feature_matching:
keypoints = self._create_sift_keypoints()
mp = sift.MatchPlan()
# Match components with id1 and id2
for i, kp1 in enumerate(keypoints[id1]):
for j, kp2 in enumerate(keypoints[id2]):
# Match descriptors
matches[(i, j)] = mp.match(kp1, kp2)
# Add matches sorted by number of matches found.
for x, y in sorted(matches, key=lambda match: matches[match].shape[0],
reverse=True):
# Only add match if neither x nor y are already in the list.
if x not in final_matches.keys() and y not in final_matches.values():
final_matches[x] = y
elif method == Method.euclidean_distance:
for i, X in enumerate(self.components[id1]):
for j, Y in enumerate(self.components[id2]):
matches[(i, j)] = self.euclidean_distance(X.image, Y.image)
matches[(i, j)] = self.euclidean_distance(X, Y)
# Add matches sorted by distance.
for x, y in sorted(matches, key=lambda match: matches[match]):
# Only add match if neither x nor y are already in the list.
......@@ -157,32 +191,32 @@ class ComponentsMatching():
id1 = 0
id2 = 1
stack = []
for i, component in enumerate(self.components[id1]):
for i, img1 in enumerate(self.components[id1]):
if i in final_matches:
j = final_matches[i]
component2 = self.components[id2][j]
img2 = self.components[id2][j]
# Show link between features
if displayMatches:
# Check that all values are of type `cv2.DMatch`
assert all((isinstance(match, cv2.DMatch) for match in values)
for values in matches.values()), \
"Dictionary `matches` has to contain values of type `cv2.DMatch`"
img = cv2.drawMatches(component.image,
self.components[id1][i].keypoints,
component2.image,
self.components[id2][j].keypoints,
img = cv2.drawMatches(self.descriptors[id1][i].image,
self.descriptors[id1][i].keypoints,
self.descriptors[id2][j].image,
self.descriptors[id2][j].keypoints,
matches[(i, j)], None, flags=2)
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
else:
shape1, shape2 = component.image.shape, component2.image.shape
shape1, shape2 = img1.shape, img2.shape
img = numpy.zeros((max(shape1[0], shape2[0]), shape1[1] + shape2[1]))
img[:shape1[0], :shape1[1]] = component.image
img[:shape2[0], shape1[1]:] = component2.image
img[:shape1[0], :shape1[1]] = img1
img[:shape2[0], shape1[1]:] = img2
else:
shape1 = component.image.shape
shape2 = self.components[id2][0].image.shape
shape1 = img1.shape
shape2 = self.components[id2][0].shape
img = numpy.zeros((max(shape1[0], shape2[0]), shape1[1] + shape2[1]))
img[:shape1[0], :shape1[1]] = component.image
img[:shape1[0], :shape1[1]] = img1
stack.append(img)
return stack
......
......@@ -29,7 +29,7 @@ __license__ = "MIT"
__date__ = "29/11/2019"
import numpy
import cv2
import silx.math
from enum import Enum
......@@ -145,7 +145,7 @@ def hot_pixel_removal(data, ksize=3):
elif frame.dtype == numpy.float:
frame = frame.astype(numpy.float32)
corrected_frame = numpy.array(frame)
median = cv2.medianBlur(frame, ksize)
median = silx.math.medfilt(frame, ksize)
hot_pixels = numpy.subtract(frame, median, dtype=numpy.int64)
threshold = numpy.std(hot_pixels)
corrected_frame[hot_pixels > threshold] = median[hot_pixels > threshold]
......
......@@ -65,11 +65,11 @@ class TestComponentsMatching(unittest.TestCase):
def test_match_components(self):
final_matches, matches = self.componentsMatching.match_components(method=Method.feature_matching)
final_matches, matches = self.componentsMatching.match_components(method=Method.orb_feature_matching)
self.assertEqual(final_matches[0], 1)
def test_draw_matches0(self):
final_matches, matches = self.componentsMatching.match_components(method=Method.feature_matching)
final_matches, matches = self.componentsMatching.match_components(method=Method.orb_feature_matching)
stack = self.componentsMatching.draw_matches(final_matches, matches, displayMatches=True)
self.assertEqual(stack[0].shape, (512, 1024))
stack = self.componentsMatching.draw_matches(final_matches, matches, displayMatches=False)
......
......@@ -172,12 +172,12 @@ class LinkComponentsWidget(qt.QWidget):
def _comboBoxChanged(self, text):
method = Method(text)
if method == Method.euclidean_distance:
self._checkbox.setEnabled(False)
self._displayMatches = False
else:
if method == Method.orb_feature_matching:
self._checkbox.setEnabled(True)
self._displayMatches = self._checkbox.checkState()
else:
self._checkbox.setEnabled(False)
self._displayMatches = False
def _linkComponents(self):
"""
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment