From ceee58a6bc2412b94ab641d949a78d4c81274acf Mon Sep 17 00:00:00 2001 From: Akumatic Date: Fri, 15 May 2020 12:19:47 +0200 Subject: [PATCH] Current state --- .gitignore | 129 +++++++++++++++++++ LICENSE | 21 ++++ README.md | 47 +++++++ example/test.png | Bin 0 -> 23755 bytes example/test_eval.png | Bin 0 -> 135302 bytes main.py | 69 +++++++++++ scan/__init__.py | 0 scan/contours.py | 117 ++++++++++++++++++ scan/eval.py | 207 +++++++++++++++++++++++++++++++ scan/image.py | 143 +++++++++++++++++++++ scan/io.py | 280 ++++++++++++++++++++++++++++++++++++++++++ scan/utils.py | 65 ++++++++++ 12 files changed, 1078 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 example/test.png create mode 100644 example/test_eval.png create mode 100644 main.py create mode 100644 scan/__init__.py create mode 100644 scan/contours.py create mode 100644 scan/eval.py create mode 100644 scan/image.py create mode 100644 scan/io.py create mode 100644 scan/utils.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b6e4761 --- /dev/null +++ b/.gitignore @@ -0,0 +1,129 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4a69b2b --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019-2020 Akumatic + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..7df4958 --- /dev/null +++ b/README.md @@ -0,0 +1,47 @@ +# ExamScan +A Python 3 script to evaluate single and multiple choice exam sheets. + +## Example + +Input image: + +![Example Image](/example/test.png) + +Processed image: + +![Processed Image](/example/test_eval.png) + +## Usage + +`main.py (-u URL | -f FILE) -n NUM [-h] [-i IOUT] [-d DOUT] [-c COMP] [-p]` + +#### Flags: +##### Required, but mutually exclusive flags: +| Short | Long | Description | +| -- | -- | -- | +| -u URL | --url URL | URL to the image or pdf to be evaluated | +| -f FILE | --file FILE | path to the image or pdf to be evaluated | + +##### Required flags: +| Short | Long | Description | +| -- | -- | -- | +| -n NUM | --num NUM | number of answers per question | + +##### Optional flags: +| Short | Long | Description | +| -- | -- | -- | +|-h | --help | shows help message and exits | +| -i IOUT | --iout IOUT | path for the output picture to be stored. | +| -d DOUT | --dout DOUT | path for the output data to be stored. | +| -c COMP | --compare COMP | compares the calculated result to a given result | +| -p | --plot | plots every single step | + +## Requirements: +| Module | Pip | Git | +| -- | -- | -- | +| OpenCV | [opencv-python](https://pypi.org/project/opencv-python/) | [skvark/opencv-python](https://github.com/skvark/opencv-python) | +| NumPy | [numpy](https://pypi.org/project/numpy/) | [numpy/numpy](https://github.com/numpy/numpy) | +| Matplotlib | [matplotlib](https://pypi.org/project/matplotlib/) | [matplotlib/matplotlib](https://github.com/matplotlib/matplotlib) | +| imutils | [imutils](https://pypi.org/project/imutils/) | [jrosebr1/imutils](https://github.com/jrosebr1/imutils) | +| python-magic | [python-magic](https://pypi.org/project/python-magic/) | [ahupp/python-magic](https://github.com/ahupp/python-magic) | +| pdf2image | [pdf2image](https://pypi.org/project/pdf2image/) | [Belval/pdf2image](https://github.com/Belval/pdf2image) | \ No newline at end of file diff --git a/example/test.png b/example/test.png new file mode 100644 index 0000000000000000000000000000000000000000..9e4e14ca337ed07c1b06a19b8f2ab698ec6f2f14 GIT binary patch literal 23755 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
  • ; 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

    SY^pB28WBZnH_yCKP$9@>8&$Vh65Qb+X1AiU{X zwIjCO5AAOc}pQQC{_zofVm{UJ4t~ z5k=hQBC0=A!!73Q$c`(44Z(Af_XmJe;ttg}Ck?cEYScKd+=j^=m;S@Pjko(lVz8Le?% z!j8naPWMPWb*F;6jGwO@zq+k`(ja<~+nU`t`C^fgF-V_yaW8c zoDqDu_1U)rlj>4YM+S`zw2fLZ=$`DFwO_6*HR+Fx3sN(4`%%GhhJ7h=;}2__K3>U8 zmvtl01omGn^#BKmx@1Qoy5+~L(;>Ik~L<6q{# zK5|7ozTCGzzj^8lNia0kx;v0L^B!ot?WZQZ9_X3DZGz9;k=?kz&$3iFmvGjO#CIJ{ z$f;nL%2zJ`yz|;~91NY))SiEK?T?$M>xjZ5QrBBPt@GMGRvgv7n}=rRRkf{2F*{PUCg<{y z7g;}vyfmr3?P=1KKVo(a$d|UA{pAVCigs|un3~ILd-_)VZNxmQ`cXQn@U3sRvQ9|R z!LQ!$s5tce4ww6p`u2=Q#N0)r^S zn99g&R3MEl6L@3A_`#R6o2M-k;m>Z{ zIzE2sxI!*$Dd_l-xY@hPf1Q5dyc|#4`@oQv#%=ef_gZ5**NNcEH#~6d&G%PIcA0lh zsk7oMJLiu)oYOc`V)-}r+|zWX4{3j`(Hzr*Pj9MM?sQgM?Dpec$6}1!m92T+fED{* zJSDNLWJ7B>wf4SCU%o?%!IR%THF)R=(xT65C_V#qHJmI8r_a6y@ZII)dQWZ+f%n{0Zp4q6<|>C&vLm?Wwzq3d+VF@uICs*34^0sVN+iok&PLIhj2BgB zZMS_t9v!y{JWGLv@<6A3NpiJ|ytQWKg19RWG7eF*{ovVjLCQlQ+-*00#~h@*0TTXB z4Q>fOrWma8p9(CqXwF!(yrF>P(gh{2fmyy+R9q?BUyx}Kfnezck;m0MZ*8%fKUvBf zV4*xI_XQ|**lc-;{`xH9iAMLxnDaBizvXa5-%oNr2eemw@u3tS1INk(8+ow6xx6_h za|(bPc{ClHSI*@MFwdI?uI6raKz{afx4Mzb)D4}(+>aE$xP`6G6GRDx4kZQA(ir+w&2NJAEiFpwSMoR(90%;B= zkYxk{HQotoc3DllSb@Z$urb+$B{Z`8I$CRyMGQcd4nH>JZ2zAO+mQ7FX00PWJ?s>vMlFr zF*o4I9B2$juKcfpL_usHr%Q^gRw6cy;+ZA%vZ1kgumF_@OL5O1$wCV%jNP9s-V*mgE07*qoM6N<$ Ef@dD86aWAK literal 0 HcmV?d00001 diff --git a/example/test_eval.png b/example/test_eval.png new file mode 100644 index 0000000000000000000000000000000000000000..c8bb0354378824a69aeca970b7a59fb7589247a3 GIT binary patch literal 135302 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�m?Phc5+0JRSVH&`&+lZb9ku=KmG+yOSR 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 XUnxz$J)0p1|Fbl=GE3Tg@aO*n{k7(D literal 0 HcmV?d00001 diff --git a/main.py b/main.py new file mode 100644 index 0000000..6980c1c --- /dev/null +++ b/main.py @@ -0,0 +1,69 @@ +#!/usr/bin/python +# SPDX-License-Identifier: MIT +# Copyright (c) 2019 Akumatic + +from scan import image, contours, io, eval + +if __name__ == "__main__": + # BGR Colors + BGR_R = (0, 0, 255) + BGR_G = (0, 255, 0) + BGR_B = (255, 0, 0) + + # Parse cli args + args = io.parse_args() + + # Reads File + img = io.read_image(path=args.file, url=args.url) + + # image copies for processing + img_orig = img.copy() + img_blur = image.blur(img) + img_thres = image.threshold(img_blur) + img_edges = image.edge_detection(img_thres.copy()) + + # gather contour data and find boxes. draws boxes + cnts = contours.find_contours(img_edges) + cnts_box = contours.find_boxes(cnts) + img_boxes = image.draw_contours(img_orig.copy(), cnts_box, BGR_G) + + # retrieve the center of all contours and + # filters entries too close to each other. + center_boxes = contours.find_center(cnts_box) + avg_radius = int(sum([contours.dist_center_topleft(center[2], + center[:2]) for center in center_boxes]) / len(center_boxes)) + contours.filter_centers(center_boxes, avg_radius) + + # Sort Box data + #center_boxes = sorted(center_boxes, key=lambda x: (x[1], x[0])) + + # draw center of given contours into original picture + img_center_boxes = image.draw_circles(img_orig.copy(), center_boxes, BGR_G) + + img_rad = image.draw_circles(img_thres.copy(), center_boxes, BGR_G, + radius=int(avg_radius)) + + center_eval = [(center[0], center[1], image.ratio_black(img_thres, center[:2], + avg_radius)) for center in center_boxes] + + img_eval = image.eval_image(img_orig.copy(), center_eval, avg_radius) + + evaluation = eval.evaluate(center_eval, args.num, avg_radius) + print(evaluation) + + if args.comp is not None: + result = eval.compare(evaluation, io.load_results(args.comp)) + print(result) + + # write result to file if optional flag is given + if args.iout is not None: + io.write_image(img_eval, args.iout) + print(f"Stored image to {args.iout}") + + if args.dout is not None: + io.store_results(evaluation, args.dout) + print(f"Stored data to {args.dout}") + + if args.plot: + io.plot(img_orig, img_blur, img_thres, img_edges, + img_boxes, img_center_boxes, img_rad, img_eval) \ No newline at end of file diff --git a/scan/__init__.py b/scan/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/scan/contours.py b/scan/contours.py new file mode 100644 index 0000000..55894f5 --- /dev/null +++ b/scan/contours.py @@ -0,0 +1,117 @@ +# SPDX-License-Identifier: MIT +# Copyright (c) 2019 Akumatic + +import cv2, imutils, numpy +from . import utils + +###################### +# Contour operations # +###################### + +def find_contours ( + image: numpy.ndarray, + mode: int = cv2.RETR_LIST, + method: int = cv2.CHAIN_APPROX_SIMPLE + ) -> list: + """ Find all contours of the filtered image + + Args: + image (ndarray): + the filtered image + + Returns: + A list containing contour data + """ + cnts = cv2.findContours(image.copy(), mode, method) + return imutils.grab_contours(cnts) + +def find_boxes ( + contours: list, + thres_area: int = 500, + pad_ratio: float = 0.05 + ) -> (list, list): + """ Find contours that resemble a box + + Args: + contours (list): + a list containing contour data + + Returns: + A list containing the box contours + """ + boxes = list() + for c in contours: + area = cv2.contourArea(c) + perimeter = cv2.arcLength(c, True) + shape_factor = utils.circularity(area, perimeter) + + if 0.7 < shape_factor < 0.85 and area >= thres_area: + boxes.append(c) + + return boxes + +def find_center ( + contours: list + ) -> list: + """ Find the center coordinates of all given contours. + + Args: + contours (list): + A list containing contour data + + Returns: + A list containing the center coordinates and the contour as tuples + (x, y, contour). + """ + centers = [] + for contour in contours: + m = cv2.moments(contour) + try: + x = int(m["m10"] / m["m00"]) + y = int(m["m01"] / m["m00"]) + centers.append((x, y, contour)) + except ZeroDivisionError: + pass + return centers + +def filter_centers ( + coords: list, + radius: int + ): + """ Removes all but one entry in circles given by coordinates and radius + + Args: + coords (list): + a list containing tuples of coordinates (x, y) + radius (float): + the radius around a center where no other center should be + """ + a = 0 + + while a < len(coords): + b = a + 1 + while b < len(coords): + if utils.distance(coords[a][:2], coords[b][:2]) <= radius: + del coords[b] + else: + b += 1 + a += 1 + +def dist_center_topleft ( + contour: numpy.ndarray, + center: tuple + ) -> float: + """ Calculates the distance from the center of a given contour to it's + top left corner + + Args: + contour (ndarray): + The contour data + center (tuple): + A tuple containing the center coordinates (x, y) + + Returns: + A float with the distance from center to the top left corner as value + """ + x, y, _, _ = cv2.boundingRect(contour) + return utils.distance((x, y), center[:2]) diff --git a/scan/eval.py b/scan/eval.py new file mode 100644 index 0000000..9380ca7 --- /dev/null +++ b/scan/eval.py @@ -0,0 +1,207 @@ +# SPDX-License-Identifier: MIT +# Copyright (c) 2019 Akumatic + +from . import utils + +def evaluate ( + centers: list, + n: int, + radius: int + ) -> list: + """ Evaluates the given data + + Args: + centers (list): + contains tuples with coordinates and the black-pixel-ratio + (x, y, ratio) + n (int): + the number of possible answers per question + radius (int): + the radius of the circle around a box + + Returns: + A list containings lists with evaluated answers + """ + start_of_file, _ = utils.find_closest_element(centers, (0,0)) + + # detect distance to next field on the right side + point_1 = start_of_file + point_r = find_next(centers, point_1, 2* radius, "r") + dist_r = utils.distance(point_1[:2], point_r[:2]) + + # detect distance to next field below + point_d = find_next(centers, point_1, 2 * radius, "d") + dist_d = utils.distance(point_1[:2], point_d[:2]) + + # detect distance to the next set of answers + for i in range(n - 2): + point_r = find_next(centers, point_r, dist_r, "r") + + point_3 = find_next(centers, point_r, 3 * radius, "r", max_dist=dist_r) + if point_3 is not None: + dist_set = utils.distance(start_of_file, point_3) + else: + dist_set = None + + answers = eval_set(start_of_file, centers, n, dist_r, dist_d) + if dist_set is not None: + cur = find_next(centers, start_of_file, dist_set, "r", max_dist = radius) + while cur is not None: + answers += eval_set(cur, centers, n, dist_r, dist_d) + cur = find_next(centers, cur, dist_set, "r", max_dist = radius) + + return answers + +def eval_set ( + start: tuple, + centers: list, + n: int, + dist_r: int, + dist_d: int + ) -> list: + """ Evaluates a set of answers + + Args: + start (tuple): + Containing the coordinates of the top left answer of a set + centers (list): + contains tuples with coordinates and the black-pixel-ratio + (x, y, ratio) + n (int): + the number of possible answers per question + dist_r (int): + the horizontal distance between two columns of answers + dist_d (int): + the vertical distance between two rows of answers + + Returns: + A list containing all evaluated data + """ + result = [] + result.append(eval_row(start, centers, n, dist_r)) + + cur = start + while cur != None: + cur = find_next(centers, cur, dist_d, "d", max_dist=dist_r / 2) + if cur is not None: + result.append(eval_row(cur, centers, n, dist_r)) + return result + +def eval_row ( + start: tuple, + centers: int, + n: int, + dist_r: int + ) -> list: + """ Evaluates a row of a set of answers with length n + + Args: + start (tuple): + Containing the coordinates of the top left answer of a set + centers (list): + contains tuples with coordinates and the black-pixel-ratio + (x, y, ratio) + n (int): + the number of possible answers per question + dist_r (int): + the horizontal distance between two columns of answers + + Returns: + A list containing all evaluated answer data from a given row + """ + result = [] + result.append(rate_black_pixel_ratio(start[2])) + + cur = start + for i in range(n - 1): + cur = find_next(centers, cur, dist_r, "r") + result.append(rate_black_pixel_ratio(cur[2])) + + return result + +def rate_black_pixel_ratio ( + ratio: int, + thres_1: int = 20, + thres_2: int = 50 + ) -> int: + """ Evaluates the given black-pixel-ratio and returns the detected marking. + + Args: + ratio (int): + the ratio of black pixels compared to total pixels + thres_1 (int): + threshold for checked boxes + thres_2 (int): + threshold for corrected boxes + + Returns: + -1 if a box is filled + 1 if a box is checked + 0 if a box is empty + """ + if ratio >= thres_2: + return -1 # corrected box + if ratio >= thres_1: + return 1 # checked + else: # empty box + return 0 + +def find_next ( + centers: list, + cur: tuple, + dist: int, + dir: str, + max_dist: int = None + ): + """ Finds the next point in a given direction with a given distance + + Args: + centers (list): + contains tuples with coordinates and the black-pixel-ratio + (x, y, ratio) + cur (tuple): + the current point + dist (int): + approximated distance to the next point + dir (str): + direction of the distance. can be either "r" or "d" + max_dist (int): + max distance to be allowe + + Returns: + returns the next found point. + if max_dist is given, returns None if distance > max_dist + """ + if dir == "r": + e, d = utils.find_closest_element(centers, (cur[0] + dist, cur[1])) + + elif dir == "d": + e, d = utils.find_closest_element(centers, (cur[0], cur[1] + dist)) + + if max_dist is None or d <= max_dist: + return e + return None + +def compare( + data_a: list, + data_b: list + ): + """ Compares two sets of evaluated data with the same + + Args: + data_a (list): + data set A + data_b (list): + data set B + + Returns: + A String "Correct/Total Answers" and the percentage + """ + # asserts the number of questions is the same + assert len(data_a) == len(data_b) + cnt = 0 + for i in range(len(data_a)): + if data_a[i] == data_b[i]: + cnt += 1 + + return (f"{cnt}/{len(data_a)}", cnt / len(data_a) * 100) \ No newline at end of file diff --git a/scan/image.py b/scan/image.py new file mode 100644 index 0000000..171373c --- /dev/null +++ b/scan/image.py @@ -0,0 +1,143 @@ +# SPDX-License-Identifier: MIT +# Copyright (c) 2019 Akumatic + +import cv2, imutils, numpy + +def threshold ( + image: numpy.ndarray, + threshold: int = 200, + ) -> numpy.ndarray: + """ Converts given image to black and white + + Args: + image (ndarray): + the image to be processed + threshold (int): + the threshold at which every pixel value should be set to 255 + """ + _, img_binary = cv2.threshold(image, threshold, 255, cv2.THRESH_BINARY) + return img_binary + +def blur ( + image: numpy.ndarray, + kernel_size: int = 7 + ) -> numpy.ndarray: + """ Filters the given picture by converting it to gray scale and + applying gaussian blur filter + + Args: + image (ndarray): + the image to be processed + kernel_size (int): + the size of the kernel for the gaussian blur + + Returns: + A ndarray containing the filtered image + """ + gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) + return cv2.GaussianBlur(gray, (kernel_size, kernel_size), 0) + +def edge_detection ( + image: numpy.ndarray + ) -> numpy.ndarray: + """ Applies canny edge detection on a given image + + Args: + image (ndarray): + the image to be processed + + Returns: + A ndarray containing the image after canny detection + """ + return imutils.auto_canny(image) + +def draw_circles ( + image: numpy.ndarray, + centers: list, + color: tuple, + radius: int = 3, + thickness: int = 2 + ) -> numpy.ndarray: + """ Draws the centers of given contures with the given color, + (size, thickness) + + Args: + image (ndarray): + the image to be modified + centers (list): + a list containing the center coordinates as tuples (x, y) + color (tuple): + the color coded in BGR color scale + radius (int): + size of the circles to be drawn + thickness (int): + thickness of the borders of all circles to be drawn + """ + for center in centers: + cv2.circle(img=image, center=center[:2], radius=radius, + color=color,thickness=thickness) + + return image + +def draw_contours ( + image: numpy.ndarray, + contours: list, + color: tuple + ) -> numpy.ndarray: + """ Draws the given contours in the given picture with the given color. + Args: + image (ndarray): + the image to be modified + contours (list): + A list containing contour data + color (tuple): + the color coded in BGR color scale + + Returns: + the modified image as a multidimensional array + """ + img = cv2.drawContours(image, contours, -1, color, 3) + return img + + +def ratio_black ( + image: numpy.ndarray, + center: tuple, + radius: int, + ) -> int: + """ Calculates the ratio of black pixels in a given square area. + + Args: + image (ndarray): + the image + center (tuple): + + radius (int): + + + Returns: + The ratio of black pixels to all pixels + """ + cnt_black = 0 + cnt_pixel = 0 + for x in range(center[0] - radius, center[0] + radius): + for y in range(center[1] - radius, center[1] + radius): + if image[y, x] == 0: + cnt_black += 1 + cnt_pixel += 1 + + return int(cnt_black / cnt_pixel * 100) + +def eval_image ( + image: numpy.ndarray, + data: list, + radius: int + ) -> numpy.ndarray: + + checked = [d[:2] for d in data if d[2] >= 20] + corrected = [d[:2] for d in data if d[2] >= 50] + + draw_circles(image, checked, (0,255,0), radius, thickness=4) + draw_circles(image, corrected, (255,0,0), radius, thickness=4) + + return image diff --git a/scan/io.py b/scan/io.py new file mode 100644 index 0000000..1ba5c1f --- /dev/null +++ b/scan/io.py @@ -0,0 +1,280 @@ +# SPDX-License-Identifier: MIT +# Copyright (c) 2019 Akumatic + +import cv2, argparse, numpy + +############## +# Exceptions # +############## + +class UnsupportedFileType(Exception): + pass + +class UnsupportedNumberOfPages(Exception): + pass + +class MissingArg(Exception): + pass + +#################### +# Argument Parsing # +#################### + +def parse_args ( + ) -> argparse.Namespace: + """ Parser for cli arguments. + + Returns: + A Namespace containing all parsed data + """ + # The parser itself + parser = argparse.ArgumentParser(add_help=False) + parser.description = "Evaluates single choice sheets" + + # Groups for ordering arguments in help command + grp_req_excl = parser.add_argument_group("required arguments, mutually exclusive") + grp_req = parser.add_argument_group("required arguments") + grp_opt = parser.add_argument_group("optional arguments") + + ######################### + ##### Required Args ##### + ######################### + + # Input path - either an url or a path to a local file + io_grp = grp_req_excl.add_mutually_exclusive_group(required=True) + io_grp.add_argument("-u", "--url", dest="url", + help="URL to the image or pdf to be evaluated.") + io_grp.add_argument("-f", "--file", dest="file", + help="path to the image or pdf to be evaluated.") + + # required arg for number of answers each question + grp_req.add_argument("-n", "--num", dest="num", required=True, + type=_arg_int_pos, help="number of answers per question") + + ######################### + ##### Optional Args ##### + ######################### + + # help message. Added manually so it is shown under optional + grp_opt.add_argument("-h", "--help", action="help", help="show this help message and exit") + + # path to store the result picture to + grp_opt.add_argument("-i", "--iout", dest="iout", + help="path for the output picture to be stored.") + + # path to store the result list to + grp_opt.add_argument("-d", "--dout", dest="dout", + help="path for the output data to be stored.") + + # path to compare results generated by the program with data stored in a file + grp_opt.add_argument("-c", "--compare", dest="comp", + help="compares the calculated result to a given result") + + # plotting all steps + grp_opt.add_argument("-p", "--plot", dest="plot", action="store_true", + help="plots every single step") + + return parser.parse_args() + +def _arg_int_pos ( + value: str + ) -> int: + """ Trying to parse the input argument into a positive value. + + Args: + value (str): + The value to be checked + + Returns: + The checked and casted value + + Raises: + ArgumentTypeError: + Raises an ArgumentTypeError if + - value is not a number + - value is not positive + """ + try: + int_value = int(value) + except ValueError: + raise argparse.ArgumentTypeError(f"{value} is not a number.") + + if int_value <= 0: + raise argparse.ArgumentTypeError(f"{value} is not a positive integer.") + return int_value + +################# +# File Handling # +################# + +def read_image ( + path: str = None, + url: str = None + ) -> numpy.ndarray: + """ Opens the image if file extension is supported. + + Supported file extensions are .jpg, .jpeg, .png and .pdf + + Args: + path (String): Path to local file + url (String): URL to file + + Returns: + A ndarray containing the image data + """ + # Neither URL nor path provied + if url is None and path is None: + raise MissingArg + + # Get data and mime type + if url: + import requests + try: + response = requests.get(url, stream=True).raw + ext = response.headers["Content-Type"].split("; ")[0] + except requests.exceptions.ConnectionError as e: + raise e + + else: # path + import magic + try: + with open(path, "rb") as f: + file = f.read() + ext = magic.from_buffer(file, mime=True).split("; ")[0] + except FileNotFoundError as e: + raise e + + # If file is image or pdf, parse to cv2 + if ext in ("image/png", "image/jpeg"): + if url: + data = numpy.asarray(bytearray(response.read())) + else: + data = numpy.asarray(bytearray(file)) + + return cv2.imdecode(data, cv2.IMREAD_COLOR) + + elif ext in ("application/pdf"): + import pdf2image + if url: + images = pdf2image.convert_from_bytes(response.read()) + else: + images = pdf2image.convert_from_bytes(file) + + # only pdf with one page are supported + if len(images) != 1: + raise UnsupportedNumberOfPages + + data = numpy.asarray(images[0]) + return cv2.cvtColor(data, cv2.COLOR_RGB2BGR) + + else: + raise UnsupportedFileType(ext) + +def write_image ( + image: numpy.ndarray, + path: str, + ): + """ Stores an image to the given path. + If the directory does not exist, it tries to create it. + + Args: + image (ndarray): + the image to be stored + path (String): Path for file to be stored + """ + import os + path_abs = os.path.abspath(path) + path_dir = os.path.dirname(path_abs) + + if not os.path.exists(path_dir): + os.makedirs(path_dir) + + cv2.imwrite(path_abs, image) + +def load_results ( + path: str + ): + import json + with open(path, "r") as f: + return json.loads(f.read()) + +def store_results ( + data: list, + path: str + ): + import json + with open(path, "w+") as f: + f.write(json.dumps(data)) + +############ +# Plotting # +############ + +def plot ( + orig: numpy.ndarray, + blur: numpy.ndarray, + thres: numpy.ndarray, + edges: numpy.ndarray, + boxes: numpy.ndarray, + center_boxes: numpy.ndarray, + checked: numpy.ndarray, + end: numpy.ndarray, + ): + """ Plots up to 8 given data. + All Subplot consists of the following: + + - plt.subplot => subplot identifier + - plt.xticks, plt.yticks => hide axis labels + - plt.title => Title to be shown + - plt.imshow => shows given image in the subplot + - since opencv image is in BGR, it needs to convert to RGB first + """ + import matplotlib.pyplot as plt + from imutils import opencv2matplotlib + idx = 240 + + # Subplot y = 1, x = 1 + plt.subplot(idx + 1) + plt.xticks([]), plt.yticks([]) + plt.title("Original") + plt.imshow(opencv2matplotlib(orig)) + + # Subplot y = 1, x = 2 + plt.subplot(idx + 2) + plt.xticks([]), plt.yticks([]) + plt.title("Blur") + plt.imshow(opencv2matplotlib(blur)) + + # Subplot y = 1, x = 3 + plt.subplot(idx + 3) + plt.xticks([]), plt.yticks([]) + plt.title("Threshold") + plt.imshow(opencv2matplotlib(thres)) + + plt.subplot(idx + 4) + plt.xticks([]), plt.yticks([]) + plt.title("Canny Edge") + plt.imshow(opencv2matplotlib(edges)) + + plt.subplot(idx + 5) + plt.xticks([]), plt.yticks([]) + plt.title("Boxes") + plt.imshow(opencv2matplotlib(boxes)) + + plt.subplot(idx + 6) + plt.xticks([]), plt.yticks([]) + plt.title("Center of Boxes") + plt.imshow(opencv2matplotlib(center_boxes)) + + + plt.subplot(idx + 7) + plt.xticks([]), plt.yticks([]) + plt.title("Areas to be checked") + plt.imshow(opencv2matplotlib(checked)) + + plt.subplot(idx + 8) + plt.xticks([]), plt.yticks([]) + plt.title("Found corrected and checked boxes") + plt.imshow(opencv2matplotlib(end)) + + plt.show() \ No newline at end of file diff --git a/scan/utils.py b/scan/utils.py new file mode 100644 index 0000000..da236b1 --- /dev/null +++ b/scan/utils.py @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: MIT +# Copyright (c) 2019 Akumatic + +import math + +def distance ( + p: tuple, + q: tuple + ) -> float: + """ Calculates direct distance between two points given as tuples. + + Args: + p (tuple): + A tuple containing x and y coordinates + q (tuple): + A tuple containing x and y coordinates + + Returns: + A float with the distance between given points as value + """ + return math.sqrt((p[0] - q[0])**2 + (p[1] - q[1])**2) + +def find_closest_element ( + points: list, + point: tuple + ) -> tuple: + """ Finds the closes element to a given point + + Args: + points (list): + A list containing tuples of coordinates (x, y) + point (tuple): + The (x, y) coordinates of the given point + + Returns: + the tuple of the closest point and the distance + between the given and closest point + """ + start, min_dist = None, None + for p in points: + dist = distance(p[:2], point) + if min_dist is None or dist < min_dist: + start, min_dist = p, dist + + return start, min_dist + + +def circularity ( + area: float, + perimeter: float + ) -> float: + """ Calculates the circularity shape factor with given area and perimeter. + + Args: + area (float): + area of a shape + perimeter (float): + length of the perimeter of a shape + + Returns: + A float with the circularity shape factor as value + """ + if perimeter == 0: + return 0.0 + return (4 * math.pi * area) / (perimeter ** 2) \ No newline at end of file