import cv2 import numpy as np class Helpers(object): ''' Image manipulation helper functions ''' def show(self, img, windowName='Image'): screen_res = 1280.0, 720.0 scale_width = screen_res[0] / img.shape[1] scale_height = screen_res[1] / img.shape[0] scale = min(scale_width, scale_height) window_width = int(img.shape[1] * scale) window_height = int(img.shape[0] * scale) cv2.namedWindow(windowName, cv2.WINDOW_NORMAL) cv2.resizeWindow(windowName, window_width, window_height) cv2.imshow(windowName, img) cv2.waitKey(0) cv2.destroyAllWindows() def isCv2(self): return cv2.__version__.startswith('2.') def thresholdify(self, img): img = cv2.adaptiveThreshold(img.astype(np.uint8), 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 3) return 255 - img def Canny(self, image): edges = cv2.Canny(image, 100, 200) self.show(edges) return edges def dilate(self, image, kernel): cv2.dilate(image, kernel) return image def largestContour(self, image): if self.isCv2(): contours, h = cv2.findContours( image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) else: _, contours, h = cv2.findContours( image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) return max(contours, key=cv2.contourArea) def largest4SideContour(self, image): contours, h = cv2.findContours( image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) contours = sorted(contours, key=cv2.contourArea, reverse=True) for cnt in contours[:min(5,len(contours))]: #im = image.copy() #cv2.drawContours(im, cnt, -1, (255,255,255), 5) #self.show(im,'contour') if len(self.approx(cnt)) == 4: return cnt return None def make_it_square(self, image, side_length=306): return cv2.resize(image, (side_length, side_length)) def area(self, image): return float(image.shape[0] * image.shape[1]) def cut_out_sudoku_puzzle(self, image, contour): x, y, w, h = cv2.boundingRect(contour) image = image[y:y + h, x:x + w] return self.make_it_square(image, min(image.shape)) def binarized(self, image): for i in xrange(image.shape[0]): for j in xrange(image.shape[1]): image[i][j] = 255 * int(image[i][j] != 255) return image def approx(self, cnt): peri = cv2.arcLength(cnt, True) app = cv2.approxPolyDP(cnt, 0.01 * peri, True) return app def get_rectangle_corners(self, cnt): pts = cnt.reshape(4, 2) rect = np.zeros((4, 2), dtype="float32") # the top-left point has the smallest sum whereas the # bottom-right has the largest sum s = pts.sum(axis=1) rect[0] = pts[np.argmin(s)] rect[2] = pts[np.argmax(s)] # compute the difference between the points -- the top-right # will have the minumum difference and the bottom-left will # have the maximum difference diff = np.diff(pts, axis=1) rect[1] = pts[np.argmin(diff)] rect[3] = pts[np.argmax(diff)] return rect def warp_perspective(self, rect, grid): (tl, tr, br, bl) = rect widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2)) widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2)) # ...and now for the height of our new image heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2)) heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2)) # take the maximum of the width and height values to reach # our final dimensions maxWidth = max(int(widthA), int(widthB)) maxHeight = max(int(heightA), int(heightB)) # construct our destination points which will be used to # map the screen to a top-down, "birds eye" view dst = np.array([ [0, 0], [maxWidth - 1, 0], [maxWidth - 1, maxHeight - 1], [0, maxHeight - 1]], dtype="float32") # calculate the perspective transform matrix and warp # the perspective to grab the screen M = cv2.getPerspectiveTransform(rect, dst) warp = cv2.warpPerspective(grid, M, (maxWidth, maxHeight)) return self.make_it_square(warp) def getTopLine(self, image): for i, row in enumerate(image): if np.any(row): return i return None def getBottomLine(self, image): for i in xrange(image.shape[0] - 1, -1, -1): if np.any(image[i]): return i return None def getLeftLine(self, image): for i in xrange(image.shape[1]): if np.any(image[:, i]): return i return None def getRightLine(self, image): for i in xrange(image.shape[1] - 1, -1, -1): if np.any(image[:, i]): return i return None def rowShift(self, image, start, end, length): shifted = np.zeros(image.shape) if start + length < 0: length = -start elif end + length >= image.shape[0]: length = image.shape[0] - 1 - end for row in xrange(start, end + 1): shifted[row + length] = image[row] return shifted def colShift(self, image, start, end, length): shifted = np.zeros(image.shape) if start + length < 0: length = -start elif end + length >= image.shape[1]: length = image.shape[1] - 1 - end for col in xrange(start, end + 1): shifted[:, col + length] = image[:, col] return shifted