diff --git a/.gitignore b/.gitignore index 7b1094c..8a1c345 100644 --- a/.gitignore +++ b/.gitignore @@ -1,262 +1,229 @@ - -# Created by https://www.gitignore.io/api/python,pycharm,eclipse,linux,osx,windows - -### Python ### -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -*.egg-info/ -.installed.cfg -*.egg - -# 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/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*,cover -.hypothesis/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# IPython Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# dotenv -.env - -# virtualenv -venv/ -ENV/ - -# Spyder project settings -.spyderproject - -# Rope project settings -.ropeproject - - -### PyCharm ### -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff: -.idea/workspace.xml -.idea/tasks.xml -.idea/dictionaries -.idea/vcs.xml -.idea/jsLibraryMappings.xml - -# Sensitive or high-churn files: -.idea/dataSources.ids -.idea/dataSources.xml -.idea/dataSources.local.xml -.idea/sqlDataSources.xml -.idea/dynamic.xml -.idea/uiDesigner.xml - -# Gradle: -.idea/gradle.xml -.idea/libraries - -# Mongo Explorer plugin: -.idea/mongoSettings.xml - -## File-based project format: -*.iws - -## Plugin-specific files: - -# IntelliJ -/out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -### PyCharm Patch ### -# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 - -# *.iml -# modules.xml - - -### Eclipse ### - -.metadata -bin/ -tmp/ -*.tmp -*.bak -*.swp -*~.nib -local.properties -.settings/ -.loadpath -.recommenders - -# Eclipse Core -.project - -# External tool builders -.externalToolBuilders/ - -# Locally stored "Eclipse launch configurations" -*.launch - -# PyDev specific (Python IDE for Eclipse) -*.pydevproject - -# CDT-specific (C/C++ Development Tooling) -.cproject - -# JDT-specific (Eclipse Java Development Tools) -.classpath - -# Java annotation processor (APT) -.factorypath - -# PDT-specific (PHP Development Tools) -.buildpath - -# sbteclipse plugin -.target - -# Tern plugin -.tern-project - -# TeXlipse plugin -.texlipse - -# STS (Spring Tool Suite) -.springBeans - -# Code Recommenders -.recommenders/ - - -### Linux ### -*~ - -# temporary files which can be created if a process still has a handle open of a deleted file -.fuse_hidden* - -# KDE directory preferences -.directory - -# Linux trash folder which might appear on any partition or disk -.Trash-* - - -### OSX ### -*.DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - - -### Windows ### -# Windows image file caches -Thumbs.db -ehthumbs.db - -# Folder config file -Desktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msm -*.msp - -# Windows shortcuts -*.lnk + +# Created by https://www.gitignore.io/api/osx,linux,python,windows,pycharm+all + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### OSX ### +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### PyCharm+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries + +# Sensitive or high-churn files: +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml + +# Gradle: +.idea/**/gradle.xml +.idea/**/libraries + +# CMake +cmake-build-debug/ + +# Mongo Explorer plugin: +.idea/**/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Ruby plugin and RubyMine +/.rakeTasks + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +### PyCharm+all Patch ### +# Ignores the whole idea folder +# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 + +.idea/ + +### Python ### +# 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/ +*.egg-info/ +.installed.cfg +*.egg + +# 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/ +.coverage +.coverage.* +.cache +.pytest_cache/ +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule.* + +# 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/ + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + + +# End of https://www.gitignore.io/api/osx,linux,python,windows,pycharm+all +/pictures/ diff --git a/.idea/.name b/.idea/.name deleted file mode 100644 index c02165a..0000000 --- a/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -vanishing-point-detection \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml deleted file mode 100644 index 97626ba..0000000 --- a/.idea/encodings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 183deb0..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index dfc2113..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vanishing-point-detection.iml b/.idea/vanishing-point-detection.iml deleted file mode 100644 index 6f63a63..0000000 --- a/.idea/vanishing-point-detection.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/src/Testing.py b/src/Testing.py index c087c4b..b188ddd 100644 --- a/src/Testing.py +++ b/src/Testing.py @@ -1,20 +1,21 @@ import os -from VanishingPoint import * +import cv2 -i = 0 -for subdir, dirs, files in os.walk('/pictures/input'): +from src.vanishing_point import hough_transform, find_intersections, sample_lines, find_vanishing_point + +for subdir, dirs, files in os.walk('../pictures/input'): for file in files: filepath = subdir + os.sep + file if filepath.endswith(".jpg"): - i += 1 + print(filepath) img = cv2.imread(filepath) hough_lines = hough_transform(img) if hough_lines: random_sample = sample_lines(hough_lines, 100) - intersections = find_intersections(random_sample, img) + intersections = find_intersections(random_sample) if intersections: grid_size = min(img.shape[0], img.shape[1]) // 3 vanishing_point = find_vanishing_point(img, grid_size, intersections) - filename = '/pictures/output/center' + str(i) + '.jpg' + filename = '../pictures/output/' + os.path.splitext(file)[0] + '_center' + '.jpg' cv2.imwrite(filename, img) diff --git a/src/VanishingPoint.py b/src/VanishingPoint.py deleted file mode 100644 index a318224..0000000 --- a/src/VanishingPoint.py +++ /dev/null @@ -1,128 +0,0 @@ -import random - -import cv2 -import numpy as np - - -# Perform edge detection -def hough_transform(img): - gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Convert image to grayscale - - kernel = np.ones((15, 15), np.uint8) - opening = cv2.morphologyEx(gray, cv2.MORPH_OPEN, kernel) # Open (erode, then dilate) - # cv2.imwrite('../pictures/output/opening.jpg', opening) - - edges = cv2.Canny(opening, 50, 150, apertureSize=3) # Canny edge detection - # cv2.imwrite('../pictures/output/canny.jpg', edges) - - lines = cv2.HoughLines(edges, 1, np.pi / 180, 200) # Hough line detection - hough_lines = [] - # Lines are represented by rho, theta; convert to endpoint notation - if lines is not None: - for line in lines: - for rho, theta in line: - a = np.cos(theta) - b = np.sin(theta) - x0 = a * rho - y0 = b * rho - x1 = int(x0 + 1000 * (-b)) - y1 = int(y0 + 1000 * (a)) - x2 = int(x0 - 1000 * (-b)) - y2 = int(y0 - 1000 * (a)) - - cv2.line(img, (x1, y1), (x2, y2), (0, 0, 255), 2) - hough_lines.append(((x1, y1), (x2, y2))) - - # cv2.imwrite('../pictures/output/hough.jpg', img) - return hough_lines - - -# Random sampling of lines -def sample_lines(lines, size): - if size > len(lines): - size = len(lines) - return random.sample(lines, size) - - -def det(a, b): - return a[0] * b[1] - a[1] * b[0] - - -# Find intersection point of two lines (not segments!) -def line_intersection(line1, line2): - x_diff = (line1[0][0] - line1[1][0], line2[0][0] - line2[1][0]) - y_diff = (line1[0][1] - line1[1][1], line2[0][1] - line2[1][1]) - - div = det(x_diff, y_diff) - if div == 0: - return None # Lines don't cross - - d = (det(*line1), det(*line2)) - x = det(d, x_diff) / div - y = det(d, y_diff) / div - - return x, y - - -# Find intersections between multiple lines (not line segments!) -def find_intersections(lines, img): - intersections = [] - for i in range(len(lines)): - line1 = lines[i] - for j in range(i + 1, len(lines)): - line2 = lines[j] - - if not line1 == line2: - intersection = line_intersection(line1, line2) - if intersection: # If lines cross, then add - # Don't include intersections that happen off-image - # Seems to cost more time than it saves - # if not (intersection[0] < 0 or intersection[0] > img.shape[1] or - # intersection[1] < 0 or intersection[1] > img.shape[0]): - # print 'adding', intersection[0],intersection[1],img.shape[1],img.shape[0] - intersections.append(intersection) - - return intersections - - -# Given intersections, find the grid where most intersections occur and treat as vanishing point -def find_vanishing_point(img, grid_size, intersections): - # Image dimensions - image_height = img.shape[0] - image_width = img.shape[1] - - # Grid dimensions - grid_rows = (image_height // grid_size) + 1 - grid_columns = (image_width // grid_size) + 1 - - # Current cell with most intersection points - max_intersections = 0 - best_cell = (0.0, 0.0) - - for i in range(grid_rows): - for j in range(grid_columns): - cell_left = i * grid_size - cell_right = (i + 1) * grid_size - cell_bottom = j * grid_size - cell_top = (j + 1) * grid_size - cv2.rectangle(img, (cell_left, cell_bottom), (cell_right, cell_top), (0, 0, 255), 10) - - current_intersections = 0 # Number of intersections in the current cell - for x, y in intersections: - if cell_left < x < cell_right and cell_bottom < y < cell_top: - current_intersections += 1 - - # Current cell has more intersections that previous cell (better) - if current_intersections > max_intersections: - max_intersections = current_intersections - best_cell = ((cell_left + cell_right) / 2, (cell_bottom + cell_top) / 2) - print(best_cell, "this best cell") - if best_cell[0] != None and best_cell[1] != None: - rx1 = int(best_cell[0] - grid_size / 2) - ry1 = int(best_cell[1] - grid_size / 2) - rx2 = int(best_cell[0] + grid_size / 2) - ry2 = int(best_cell[1] + grid_size / 2) - cv2.rectangle(img, (rx1, ry1), (rx2, ry2), (0, 255, 0), 10) - # cv2.imwrite('../pictures/output/center.jpg', img) - - return best_cell diff --git a/src/__init__.py b/src/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/vanishing_point.py b/src/vanishing_point.py new file mode 100644 index 0000000..ef41f43 --- /dev/null +++ b/src/vanishing_point.py @@ -0,0 +1,120 @@ +import itertools +import random +from itertools import starmap + +import cv2 +import numpy as np + + +# Perform edge detection +def hough_transform(img): + gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Convert image to grayscale + kernel = np.ones((15, 15), np.uint8) + + opening = cv2.morphologyEx(gray, cv2.MORPH_OPEN, kernel) # Open (erode, then dilate) + edges = cv2.Canny(opening, 50, 150, apertureSize=3) # Canny edge detection + lines = cv2.HoughLines(edges, 1, np.pi / 180, 200) # Hough line detection + + hough_lines = [] + # Lines are represented by rho, theta; converted to endpoint notation + if lines is not None: + for line in lines: + hough_lines.extend(list(starmap(endpoints, line))) + + return hough_lines + + +def endpoints(rho, theta): + a = np.cos(theta) + b = np.sin(theta) + x_0 = a * rho + y_0 = b * rho + x_1 = int(x_0 + 1000 * (-b)) + y_1 = int(y_0 + 1000 * (a)) + x_2 = int(x_0 - 1000 * (-b)) + y_2 = int(y_0 - 1000 * (a)) + + return ((x_1, y_1), (x_2, y_2)) + + +# Random sampling of lines +def sample_lines(lines, size): + if size > len(lines): + size = len(lines) + return random.sample(lines, size) + + +def det(a, b): + return a[0] * b[1] - a[1] * b[0] + + +# Find intersection point of two lines (not segments!) +def line_intersection(line1, line2): + x_diff = (line1[0][0] - line1[1][0], line2[0][0] - line2[1][0]) + y_diff = (line1[0][1] - line1[1][1], line2[0][1] - line2[1][1]) + + div = det(x_diff, y_diff) + if div == 0: + return None # Lines don't cross + + d = (det(*line1), det(*line2)) + x = det(d, x_diff) / div + y = det(d, y_diff) / div + + return x, y + + +# Find intersections between multiple lines (not line segments!) +def find_intersections(lines): + intersections = [] + for i, line_1 in enumerate(lines): + for line_2 in lines[i + 1:]: + if not line_1 == line_2: + intersection = line_intersection(line_1, line_2) + if intersection: # If lines cross, then add + intersections.append(intersection) + + return intersections + + +# Given intersections, find the grid where most intersections occur and treat as vanishing point +def find_vanishing_point(img, grid_size, intersections): + # Image dimensions + image_height = img.shape[0] + image_width = img.shape[1] + + # Grid dimensions + grid_rows = (image_height // grid_size) + 1 + grid_columns = (image_width // grid_size) + 1 + + # Current cell with most intersection points + max_intersections = 0 + best_cell = (0.0, 0.0) + + for i, j in itertools.product(range(grid_rows), range(grid_columns)): + cell_left = i * grid_size + cell_right = (i + 1) * grid_size + cell_bottom = j * grid_size + cell_top = (j + 1) * grid_size + cv2.rectangle(img, (cell_left, cell_bottom), (cell_right, cell_top), (0, 0, 255), 10) + + current_intersections = 0 # Number of intersections in the current cell + for x, y in intersections: + if cell_left < x < cell_right and cell_bottom < y < cell_top: + current_intersections += 1 + + # Current cell has more intersections that previous cell (better) + if current_intersections > max_intersections: + max_intersections = current_intersections + best_cell = ((cell_left + cell_right) / 2, (cell_bottom + cell_top) / 2) + print("Best Cell:", best_cell) + + if best_cell[0] != None and best_cell[1] != None: + rx1 = int(best_cell[0] - grid_size / 2) + ry1 = int(best_cell[1] - grid_size / 2) + rx2 = int(best_cell[0] + grid_size / 2) + ry2 = int(best_cell[1] + grid_size / 2) + cv2.rectangle(img, (rx1, ry1), (rx2, ry2), (0, 255, 0), 10) + cv2.imwrite('/pictures/output/center.jpg', img) + + return best_cell