Skip to content

Commit

Permalink
#120 :: Delete redundant code
Browse files Browse the repository at this point in the history
  • Loading branch information
JackBuck committed Mar 22, 2017
1 parent df3663a commit 8b57498
Show file tree
Hide file tree
Showing 2 changed files with 11 additions and 201 deletions.
165 changes: 11 additions & 154 deletions roboplot/dottodot/number_recognition.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,11 +146,21 @@ def _mask_using_contours(self, contours):
def _rotate_centre_spot_to_bottom_right(self):
self.rotated_image = None
if self.centre_spot is not None:
current_angle = _estimate_degrees_from_number_centre_to_spot(self._img, self.centre_spot)
current_angle = self._estimate_degrees_from_number_centre_to_spot()
desired_angle = -30
self._img = _rotate_image(desired_angle - current_angle, self._img)
self.rotated_image = self._img.copy()

def _estimate_degrees_from_number_centre_to_spot(self):
total_intensity = np.sum(255 - self._img)
centroid_y = np.sum(
np.arange(self._img.shape[0]).reshape(-1, 1) * (255 - self._img)) / total_intensity
centroid_x = np.sum(
np.arange(self._img.shape[1]).reshape(1, -1) * (255 - self._img)) / total_intensity

return np.rad2deg(np.arctan2(-(self.centre_spot.pt[0] - centroid_y),
self.centre_spot.pt[1] - centroid_x))

def _recognise_number_text(self):
img = Image.fromarray(self._img)

Expand Down Expand Up @@ -183,36 +193,6 @@ def read_image(file_path: str) -> np.ndarray:
raise TypeError("Could not open image file: {}".format(file_path))


def recognise_rotated_number(img) -> Number:
"""
Extract an integer from a potentially rotated image containing text such as '23.'.
Note that the image must contain a sequence of digits followed by a period '.'.
The location of the period is used to correctly orient the image before performing text recognition.
Args:
img (np.ndarray): the image
Returns:
Number: the number detected in the image
"""

img = _clean_image(img)
spot = _extract_spot_closest_to_centre_from_clean_image(img)

if spot is not None:
current_angle = _estimate_degrees_from_number_centre_to_spot(img, spot)
desired_angle = -30
rotated_image = _rotate_image(desired_angle - current_angle, img)
numeric_value = _recognise_number_in_clean_image(rotated_image)
spot_location = spot.pt
else:
numeric_value = None
spot_location = None

return Number(numeric_value, dot_location_yx=spot_location)


def _rotate_image(degrees, img):
rows, cols = img.shape
rotation_matrix = cv2.getRotationMatrix2D(center=(cols / 2, rows / 2), angle=degrees, scale=1)
Expand All @@ -226,22 +206,6 @@ def _rotate_image(degrees, img):
return rotated_image


def _estimate_degrees_from_number_centre_to_spot(img, spot_keypoint):
spot_x, spot_y = spot_keypoint.pt
spot_size = spot_keypoint.size
neighbourhood_of_spot, spot_local_position = _crop_about(img, centre=(spot_y, spot_x),
new_side_length=20 * spot_size)

total_intensity = np.sum(255 - neighbourhood_of_spot)
centroid_y = np.sum(
np.arange(neighbourhood_of_spot.shape[0]).reshape(-1, 1) * (255 - neighbourhood_of_spot)) / total_intensity
centroid_x = np.sum(
np.arange(neighbourhood_of_spot.shape[1]).reshape(1, -1) * (255 - neighbourhood_of_spot)) / total_intensity

return np.rad2deg(np.arctan2(-(spot_local_position[0] - centroid_y),
spot_local_position[1] - centroid_x))


def _crop_about(img, centre, new_side_length):
new_side_length = 2 * int(new_side_length / 2)

Expand All @@ -255,113 +219,6 @@ def _crop_about(img, centre, new_side_length):
return cropped_img, new_centre


def recognise_number(img: np.ndarray) -> Number:
"""
Extract an integer from an (correctly oriented) image containing text such as '23.'.
Args:
img (np.ndarray): a image
Returns:
Number: the number detected in the image
"""
img = _clean_image(img)
numeric_value = _recognise_number_in_clean_image(img)
spot = _extract_spot_closest_to_centre_from_clean_image(img)
spot_location = spot.pt if spot is not None else None
return Number(numeric_value, dot_location_yx=spot_location)


def _recognise_number_in_clean_image(img) -> int:
recognised_text = _recognise_number_text(img)
return _text_to_number(recognised_text)


def _clean_image(img):
img = cv2.medianBlur(img, ksize=3)
img = cv2.adaptiveThreshold(img, maxValue=255, adaptiveMethod=cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
thresholdType=cv2.THRESH_BINARY, blockSize=11, C=2)
return img


def _recognise_number_text(img: np.ndarray) -> str:
img = Image.fromarray(img)

# psm 8 => single word;
# digits => use the digits config file supplied with the software
return pytesseract.image_to_string(img, config='-psm 8, digits')


def _text_to_number(recognised_text: str) -> int:
# Forcing a terminating period helps us to filter out bad results
match = re.match(r'(\d+)\.$', recognised_text)
if match is None:
return None
else:
return int(match.group(1))


def _extract_spot_closest_to_centre_from_clean_image(img):
possible_spots = _extract_spots_from_clean_image(img)
if len(possible_spots) == 0:
return None
else:
image_centre = np.array(img.shape) / 2
return min(possible_spots, key=lambda s: np.linalg.norm(s.pt - image_centre))


def _extract_spots_from_clean_image(img):
# Dilate and Erode to 'clean' the spot (note that this harms the number itself, so we only do it to extract spots
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
img = cv2.dilate(img, kernel, iterations=1)
img = cv2.erode(img, kernel, iterations=1)

# Perform a simple blob detect
params = cv2.SimpleBlobDetector_Params()
params.filterByArea = True
params.minArea = 20 # The dot in 20pt font has area of about 30
params.filterByCircularity = True
params.minCircularity = 0.7
params.filterByConvexity = True
params.minConvexity = 0.8
params.filterByInertia = True
params.minInertiaRatio = 0.6
detector = cv2.SimpleBlobDetector_create(params)
keypoints = detector.detect(img)
return keypoints


def _extract_contours_close_to(img, target_point, maximum_pixels_between_contours):
img_inverted = 255 - img
_, contours, _ = cv2.findContours(img_inverted, mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_SIMPLE)

def dist_between_contours(cnt1, cnt2):
return min([min(np.linalg.norm(cnt1 - pt, axis=2)) for pt in cnt2])

# contours = [cv2.convexHull(c, returnPoints=True) for c in contours]

spot_location_as_contour = np.reshape(target_point, (-1, 1, 2))
central_contours = [spot_location_as_contour]

still_adding_contours = True
while still_adding_contours:
still_adding_contours = False

for i in reversed(range(len(contours))):
dist_from_central_contours = min([dist_between_contours(contours[i], c) for c in central_contours])
if dist_from_central_contours <= maximum_pixels_between_contours:
central_contours.append(contours.pop(i))
still_adding_contours = True

return central_contours[1:]


def _mask_with_contours(img, contours):
mask = np.zeros(img.shape, np.uint8)
cv2.drawContours(mask, contours, contourIdx=-1, color=255, thickness=-1)
img[np.where(mask == 0)] = 255


def draw_image_with_keypoints(img, keypoints, window_title="Image with keypoints"):
"""An apparently unused method which is actually quite useful when debugging!"""

Expand Down
47 changes: 0 additions & 47 deletions scripts/recognise_central_number.py

This file was deleted.

0 comments on commit 8b57498

Please sign in to comment.