Last active
November 20, 2019 02:41
-
-
Save synap5e/fb32ffff0e0db8de15064cf1b8bb6dfa to your computer and use it in GitHub Desktop.
OpenCV OCR segmentation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import cv2 | |
import matplotlib.pyplot as plt | |
import numpy as np | |
def main() -> None: | |
f, (ax1, ax2) = plt.subplots(2, 1) | |
im = cv2.imread('number_plate.png') | |
# experiment with what works best here... | |
# If the background can have bright colours but never white then np.min(im, axis=2) works well | |
# If the text is a specific colour you could use HSV and do an inrange where the hue is correct and S/V match the text being foreground | |
# If it's just a black/white then bgr2gray is simplest | |
gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY) | |
# Find some threshold that works | |
# OTSU is tempting, but can make the stroke vary depending on the background so if you can get a clean thresh with a value use that | |
# invert as necessary | |
# Add an empty border so our segmenting doesn't get unmatched start/ends | |
_, thresh = cv2.threshold(gray, 100, 255, cv2.THRESH_BINARY | cv2.THRESH_BINARY_INV) | |
thresh = cv2.copyMakeBorder(thresh, 1, 1, 1, 1, borderType=cv2.BORDER_CONSTANT, value=0) | |
ax1.imshow(thresh) | |
cols = cv2.reduce(thresh, 0, cv2.REDUCE_AVG)[0] | |
ax1.plot(cols, color='r') | |
# The peak threshold (height) is set somewhere that it will still detect the horizontal stroke of e.g. L, H, etc. as the same | |
# letter but can filter noise. If the image is super noisy play with the morphology and/or blurring etc. of gray/thresh images | |
gaps = cols < 10 | |
# edges now contains the start and end of each segment, with -1=segment start and +1=segment end | |
edges = gaps[1:].astype(np.int) - gaps[:-1] | |
ax1.plot(100 + edges * 50, color='g') # transform signal to show on graph properly | |
segment_starts = np.where(edges == -1)[0] | |
segment_ends = np.where(edges == 1)[0] | |
ax1.scatter(segment_starts, [40 for _ in segment_starts], color='blue') | |
ax1.scatter(segment_ends, [40 for _ in segment_ends], color='orange') | |
assert len(segment_starts) == len(segment_ends), 'Got unmatched segment start/ends. Do any characters/segmentations touch the edges of the image?' | |
segments = [] | |
for start, end in zip(segment_starts, segment_ends): | |
segments.append(im[:, max(0, start - 1):min(end + 1, im.shape[1] - 1)]) | |
ax2.imshow(np.hstack([ | |
cv2.copyMakeBorder(s, 0, 0, 0, 50 - s.shape[1], cv2.BORDER_CONSTANT, value=(255, 0, 255)) | |
for s in segments | |
])) | |
plt.show() | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment