Skip to content

Commit

Permalink
add RDP algorithm to smooth functions
Browse files Browse the repository at this point in the history
  • Loading branch information
liupeng89 committed Aug 22, 2018
1 parent bc63285 commit 0b2433e
Show file tree
Hide file tree
Showing 12 changed files with 401 additions and 182 deletions.
8 changes: 8 additions & 0 deletions .idea/markdown-exported-files.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

72 changes: 72 additions & 0 deletions .idea/markdown-navigator.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions .idea/markdown-navigator/profiles_settings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

61 changes: 61 additions & 0 deletions algorithms/RDP.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"""
The Ramer-Douglas-Peucker algorithm roughly ported from the pseudo-code provided
by http://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
"""
from math import sqrt


def distance(a, b):
return sqrt((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2)


def point_line_distance(point, start, end):
"""
Calcluate the distance between point to line.
point = (px, py) p
start = (ax, ay) a
end = (bx, by) b
1. b - a = (bx - ax, by - ay)
p - a = (px - ax, py - ay)
2. (b - a) * (c - a) = | (b-a) * (c-a)|
3. diatance p to line (a, b)
= |(b-a) * (c-a)| / sqrt((bx -ax)^2 + (by-ay)^2)
:param point:
:param start:
:param end:
:return:
"""
if start == end:
return distance(point, start)
else:
n = abs((end[0] - start[0]) * (start[1] - point[1]) - (start[0] - point[0]) * (end[1] - start[1]))

d = sqrt((end[0] - start[0]) ** 2 + (end[1] - start[1]) ** 2)
return n / d


def rdp(points, epsilon):
"""
Reduces a series of points to a simplified version that loses detail, but maintains the general shape of the series.
:param points:
:param epsilon:
:return:
"""
dmax = 0.0
index = 0
for i in range(1, len(points)-1):
d = point_line_distance(points[i], points[0], points[-1])
if d > dmax:
index = i
dmax = d
if dmax > epsilon:
results = rdp(points[:index+1], epsilon)[:-1] + rdp(points[index:], epsilon)
else:
results = [points[0], points[-1]]
return results
Empty file added algorithms/__init__.py
Empty file.
21 changes: 17 additions & 4 deletions calligraphySmoothingTool/smoothmanuallymainwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,20 @@

# Form implementation generated from reading ui file 'smoothmanuallymainwindow.ui'
#
# Created by: PyQt5 UI code generator 5.10.1
# Created by: PyQt5 UI code generator 5.9.2
#
# WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(1030, 681)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
self.verticalLayoutWidget.setGeometry(QtCore.QRect(20, 10, 181, 321))
self.verticalLayoutWidget.setGeometry(QtCore.QRect(20, 10, 200, 321))
self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
Expand All @@ -30,6 +29,17 @@ def setupUi(self, MainWindow):
self.contour_btn = QtWidgets.QPushButton(self.verticalLayoutWidget)
self.contour_btn.setObjectName("contour_btn")
self.verticalLayout.addWidget(self.contour_btn)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.label_2 = QtWidgets.QLabel(self.verticalLayoutWidget)
self.label_2.setObjectName("label_2")
self.horizontalLayout_2.addWidget(self.label_2)
self.epsilon_ledit_2 = QtWidgets.QLineEdit(self.verticalLayoutWidget)
self.epsilon_ledit_2.setObjectName("epsilon_ledit_2")
self.horizontalLayout_2.addWidget(self.epsilon_ledit_2)
self.verticalLayout.addLayout(self.horizontalLayout_2)
self.verticalLayout_2 = QtWidgets.QVBoxLayout()
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.label = QtWidgets.QLabel(self.verticalLayoutWidget)
Expand All @@ -38,7 +48,8 @@ def setupUi(self, MainWindow):
self.maxerror_ledit = QtWidgets.QLineEdit(self.verticalLayoutWidget)
self.maxerror_ledit.setObjectName("maxerror_ledit")
self.horizontalLayout.addWidget(self.maxerror_ledit)
self.verticalLayout.addLayout(self.horizontalLayout)
self.verticalLayout_2.addLayout(self.horizontalLayout)
self.verticalLayout.addLayout(self.verticalLayout_2)
self.smooth_btn = QtWidgets.QPushButton(self.verticalLayoutWidget)
self.smooth_btn.setObjectName("smooth_btn")
self.verticalLayout.addWidget(self.smooth_btn)
Expand Down Expand Up @@ -80,6 +91,8 @@ def retranslateUi(self, MainWindow):
self.open_btn.setText(_translate("MainWindow", "Open"))
self.clear_btn.setText(_translate("MainWindow", "Clear"))
self.contour_btn.setText(_translate("MainWindow", "Contour"))
self.label_2.setText(_translate("MainWindow", "RDP epsilon:"))
self.epsilon_ledit_2.setPlaceholderText(_translate("MainWindow", "0"))
self.label.setText(_translate("MainWindow", "Max Error:"))
self.maxerror_ledit.setPlaceholderText(_translate("MainWindow", "0"))
self.smooth_btn.setText(_translate("MainWindow", "Smooth"))
Expand Down
32 changes: 27 additions & 5 deletions calligraphySmoothingTool/smoothmanuallymainwindow.ui
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<rect>
<x>20</x>
<y>10</y>
<width>181</width>
<width>200</width>
<height>321</height>
</rect>
</property>
Expand All @@ -46,23 +46,45 @@
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Max Error:</string>
<string>RDP epsilon:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="maxerror_ledit">
<widget class="QLineEdit" name="epsilon_ledit_2">
<property name="placeholderText">
<string>0</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Max Error:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="maxerror_ledit">
<property name="placeholderText">
<string>0</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="smooth_btn">
<property name="text">
Expand Down
7 changes: 5 additions & 2 deletions calligraphySmoothingToolGUI.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,12 +217,15 @@ def autoSmoothBtn(self):
"""
print("Auto-smooth button clicked")

# RDP eplison
rdp_eplison = float(self.epsilon_ledit_2.text())
# max Error
max_error = int(self.maxerror_ledit.text())
max_error = float(self.maxerror_ledit.text())

img_gray = self.image_gray.copy()

img_smoothed = autoSmoothContoursOfCharacter(img_gray, bitmap_threshold=127, dist_threshold=30, max_error=max_error)
img_smoothed = autoSmoothContoursOfCharacter(img_gray, bitmap_threshold=127, eplison=rdp_eplison, \
max_error=max_error)
qimg = QImage(img_smoothed.data, img_smoothed.shape[1], img_smoothed.shape[0], img_smoothed.shape[1], \
QImage.Format_Indexed8)

Expand Down
29 changes: 29 additions & 0 deletions test/contour_smooth_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import sys
import math
import cv2
import os
import numpy as np
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

from calligraphySmoothingTool.smoothmanuallymainwindow import Ui_MainWindow

from utils.Functions import getContourOfImage, removeBreakPointsOfContour, \
sortPointsOnContourOfImage, fitCurve, draw_cubic_bezier
from utils.contours_smoothed_algorithm import autoSmoothContoursOfCharacter

path = "../test_images/src_resize.png"

img = cv2.imread(path, 0)

img_smoothed = autoSmoothContoursOfCharacter(img)



cv2.imshow("img", img)
cv2.imshow("img_smoothed", img_smoothed)

cv2.waitKey(0)
cv2.destroyAllWindows()

25 changes: 25 additions & 0 deletions test/pypotrce_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from __future__ import division, print_function
from svgpathtools import Path, Line, QuadraticBezier, CubicBezier, Arc

seg1 = CubicBezier(300+100j, 100+100j, 200+200j, 200+300j)
seg2 = Line(200+300j, 250+350j)
path = Path(seg1, seg2)

from svgpathtools import parse_path
path_alt = parse_path('M 300 100 C 100 100 200 200 200 300 L 250 350')

print(path)
print(path_alt)

print(path == path_alt)

print(path.d())

path.append(CubicBezier(250+350j, 275+350j, 250+225j, 200+100j))
print(path)

path[0] = Line(200+100j, 200+300j)
print(path)

print('path is contious?', path.iscontinuous())
print('path is closed?', path.isclosed())
Loading

0 comments on commit 0b2433e

Please sign in to comment.