diff --git a/.idea/other.xml b/.idea/other.xml
new file mode 100644
index 0000000..a708ec7
--- /dev/null
+++ b/.idea/other.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/calligraphyCroppingTool/__init__.py b/calligraphyCroppingTool/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/calligraphyCroppingTool/charactersegmentationmainwindow.py b/calligraphyCroppingTool/charactersegmentationmainwindow.py
new file mode 100644
index 0000000..1cea6c4
--- /dev/null
+++ b/calligraphyCroppingTool/charactersegmentationmainwindow.py
@@ -0,0 +1,124 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'charactersegmentationmainwindow.ui'
+#
+# Created by: PyQt5 UI code generator 5.10.1
+#
+# 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(1128, 676)
+ self.centralwidget = QtWidgets.QWidget(MainWindow)
+ self.centralwidget.setObjectName("centralwidget")
+ self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
+ self.verticalLayoutWidget.setGeometry(QtCore.QRect(20, 10, 201, 333))
+ self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
+ self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
+ self.verticalLayout.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.open_btn = QtWidgets.QPushButton(self.verticalLayoutWidget)
+ self.open_btn.setObjectName("open_btn")
+ self.verticalLayout.addWidget(self.open_btn)
+ self.verticalLayout_3 = QtWidgets.QVBoxLayout()
+ self.verticalLayout_3.setObjectName("verticalLayout_3")
+ self.grayscale_btn = QtWidgets.QPushButton(self.verticalLayoutWidget)
+ self.grayscale_btn.setObjectName("grayscale_btn")
+ self.verticalLayout_3.addWidget(self.grayscale_btn)
+ self.convert_btn = QtWidgets.QPushButton(self.verticalLayoutWidget)
+ self.convert_btn.setObjectName("convert_btn")
+ self.verticalLayout_3.addWidget(self.convert_btn)
+ self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_3.setObjectName("horizontalLayout_3")
+ self.binary_threshold_slider = QtWidgets.QSlider(self.verticalLayoutWidget)
+ self.binary_threshold_slider.setMaximum(255)
+ self.binary_threshold_slider.setProperty("value", 127)
+ self.binary_threshold_slider.setOrientation(QtCore.Qt.Horizontal)
+ self.binary_threshold_slider.setObjectName("binary_threshold_slider")
+ self.horizontalLayout_3.addWidget(self.binary_threshold_slider)
+ self.threshold_label = QtWidgets.QLabel(self.verticalLayoutWidget)
+ self.threshold_label.setObjectName("threshold_label")
+ self.horizontalLayout_3.addWidget(self.threshold_label)
+ self.verticalLayout_3.addLayout(self.horizontalLayout_3)
+ self.verticalLayout.addLayout(self.verticalLayout_3)
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ self.label_3 = QtWidgets.QLabel(self.verticalLayoutWidget)
+ self.label_3.setObjectName("label_3")
+ self.horizontalLayout_2.addWidget(self.label_3)
+ self.thre_width_ledit = QtWidgets.QLineEdit(self.verticalLayoutWidget)
+ self.thre_width_ledit.setObjectName("thre_width_ledit")
+ self.horizontalLayout_2.addWidget(self.thre_width_ledit)
+ self.verticalLayout.addLayout(self.horizontalLayout_2)
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.label = QtWidgets.QLabel(self.verticalLayoutWidget)
+ self.label.setObjectName("label")
+ self.horizontalLayout.addWidget(self.label)
+ self.thre_dist_ledit = QtWidgets.QLineEdit(self.verticalLayoutWidget)
+ self.thre_dist_ledit.setObjectName("thre_dist_ledit")
+ self.horizontalLayout.addWidget(self.thre_dist_ledit)
+ self.verticalLayout.addLayout(self.horizontalLayout)
+ self.segmentation_btn = QtWidgets.QPushButton(self.verticalLayoutWidget)
+ self.segmentation_btn.setObjectName("segmentation_btn")
+ self.verticalLayout.addWidget(self.segmentation_btn)
+ self.extract_btn = QtWidgets.QPushButton(self.verticalLayoutWidget)
+ self.extract_btn.setObjectName("extract_btn")
+ self.verticalLayout.addWidget(self.extract_btn)
+ self.exit_btn = QtWidgets.QPushButton(self.verticalLayoutWidget)
+ self.exit_btn.setObjectName("exit_btn")
+ self.verticalLayout.addWidget(self.exit_btn)
+ self.scrollArea = QtWidgets.QScrollArea(self.centralwidget)
+ self.scrollArea.setGeometry(QtCore.QRect(240, 10, 871, 611))
+ self.scrollArea.setWidgetResizable(True)
+ self.scrollArea.setObjectName("scrollArea")
+ self.scrollAreaWidgetContents = QtWidgets.QWidget()
+ self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 869, 609))
+ self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
+ self.image_gview = QtWidgets.QGraphicsView(self.scrollAreaWidgetContents)
+ self.image_gview.setGeometry(QtCore.QRect(0, 0, 871, 611))
+ self.image_gview.setObjectName("image_gview")
+ self.scrollArea.setWidget(self.scrollAreaWidgetContents)
+ self.verticalLayoutWidget_2 = QtWidgets.QWidget(self.centralwidget)
+ self.verticalLayoutWidget_2.setGeometry(QtCore.QRect(20, 350, 201, 271))
+ self.verticalLayoutWidget_2.setObjectName("verticalLayoutWidget_2")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_2)
+ self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.label_2 = QtWidgets.QLabel(self.verticalLayoutWidget_2)
+ self.label_2.setObjectName("label_2")
+ self.verticalLayout_2.addWidget(self.label_2)
+ self.characters_list = QtWidgets.QListView(self.verticalLayoutWidget_2)
+ self.characters_list.setObjectName("characters_list")
+ self.verticalLayout_2.addWidget(self.characters_list)
+ MainWindow.setCentralWidget(self.centralwidget)
+ self.menubar = QtWidgets.QMenuBar(MainWindow)
+ self.menubar.setGeometry(QtCore.QRect(0, 0, 1128, 22))
+ self.menubar.setObjectName("menubar")
+ MainWindow.setMenuBar(self.menubar)
+ self.statusbar = QtWidgets.QStatusBar(MainWindow)
+ self.statusbar.setObjectName("statusbar")
+ MainWindow.setStatusBar(self.statusbar)
+
+ self.retranslateUi(MainWindow)
+ QtCore.QMetaObject.connectSlotsByName(MainWindow)
+
+ def retranslateUi(self, MainWindow):
+ _translate = QtCore.QCoreApplication.translate
+ MainWindow.setWindowTitle(_translate("MainWindow", "Chinese Calligraphy Segmentation Tool"))
+ self.open_btn.setText(_translate("MainWindow", "Open"))
+ self.grayscale_btn.setText(_translate("MainWindow", "Grayscale"))
+ self.convert_btn.setText(_translate("MainWindow", "Convert"))
+ self.threshold_label.setText(_translate("MainWindow", "127"))
+ self.label_3.setText(_translate("MainWindow", "Threshold of \n"
+"Width:"))
+ self.label.setText(_translate("MainWindow", "Threshold of \n"
+"Distance:"))
+ self.segmentation_btn.setText(_translate("MainWindow", "Segmentation"))
+ self.extract_btn.setText(_translate("MainWindow", "Extract"))
+ self.exit_btn.setText(_translate("MainWindow", "Exit"))
+ self.label_2.setText(_translate("MainWindow", "Characters:"))
+
diff --git a/calligraphyCroppingTool/charactersegmentationmainwindow.ui b/calligraphyCroppingTool/charactersegmentationmainwindow.ui
new file mode 100644
index 0000000..372d61f
--- /dev/null
+++ b/calligraphyCroppingTool/charactersegmentationmainwindow.ui
@@ -0,0 +1,199 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 1128
+ 676
+
+
+
+ Chinese Calligraphy Segmentation Tool
+
+
+
+
+
+ 20
+ 10
+ 201
+ 333
+
+
+
+ -
+
+
+ Open
+
+
+
+ -
+
+
-
+
+
+ Grayscale
+
+
+
+ -
+
+
+ Convert
+
+
+
+ -
+
+
-
+
+
+ 255
+
+
+ 127
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 127
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+ Threshold of
+Width:
+
+
+
+ -
+
+
+
+
+ -
+
+
-
+
+
+ Threshold of
+Distance:
+
+
+
+ -
+
+
+
+
+ -
+
+
+ Segmentation
+
+
+
+ -
+
+
+ Extract
+
+
+
+ -
+
+
+ Exit
+
+
+
+
+
+
+
+
+ 240
+ 10
+ 871
+ 611
+
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 869
+ 609
+
+
+
+
+
+ 0
+ 0
+ 871
+ 611
+
+
+
+
+
+
+
+
+ 20
+ 350
+ 201
+ 271
+
+
+
+ -
+
+
+ Characters:
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
diff --git a/charactersegmentationGUI.py b/calligraphyCroppingToolGUI.py
similarity index 99%
rename from charactersegmentationGUI.py
rename to calligraphyCroppingToolGUI.py
index 43d42bd..9f3c2ed 100644
--- a/charactersegmentationGUI.py
+++ b/calligraphyCroppingToolGUI.py
@@ -7,7 +7,7 @@
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
-from charactersegmentationmainwindow import Ui_MainWindow
+from calligraphyCroppingTool.charactersegmentationmainwindow import Ui_MainWindow
from utils.Functions import getAllMiniBoundingBoxesOfImage, getCenterOfRectangles, combineRectangles, rgb2qimage
@@ -423,7 +423,7 @@ def setOption(self, opt):
def mousePressEvent(self, event):
"""
- Mouse press clicked!
+ Mouse press clicked!
:param event:
:return:
"""
diff --git a/calligraphyEvaluationTool/__init__.py b/calligraphyEvaluationTool/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/calligraphyEvaluationTool/calligraphycomparisonmainwindow.py b/calligraphyEvaluationTool/calligraphycomparisonmainwindow.py
new file mode 100644
index 0000000..01c61ea
--- /dev/null
+++ b/calligraphyEvaluationTool/calligraphycomparisonmainwindow.py
@@ -0,0 +1,673 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'calligraphycomparisonmainwindow.ui'
+#
+# Created by: PyQt5 UI code generator 5.10.1
+#
+# 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(1343, 782)
+ self.centralwidget = QtWidgets.QWidget(MainWindow)
+ self.centralwidget.setObjectName("centralwidget")
+ self.comparison_main_tab = QtWidgets.QTabWidget(self.centralwidget)
+ self.comparison_main_tab.setGeometry(QtCore.QRect(339, 9, 1001, 721))
+ self.comparison_main_tab.setObjectName("comparison_main_tab")
+ self.whole_comparison_tab = QtWidgets.QWidget()
+ self.whole_comparison_tab.setObjectName("whole_comparison_tab")
+ self.whole_main_tabwidget = QtWidgets.QTabWidget(self.whole_comparison_tab)
+ self.whole_main_tabwidget.setGeometry(QtCore.QRect(-1, 9, 1001, 681))
+ self.whole_main_tabwidget.setObjectName("whole_main_tabwidget")
+ self.whole_cr_tab = QtWidgets.QWidget()
+ self.whole_cr_tab.setObjectName("whole_cr_tab")
+ self.scrollArea_3 = QtWidgets.QScrollArea(self.whole_cr_tab)
+ self.scrollArea_3.setGeometry(QtCore.QRect(0, 0, 821, 651))
+ self.scrollArea_3.setWidgetResizable(True)
+ self.scrollArea_3.setObjectName("scrollArea_3")
+ self.scrollAreaWidgetContents_3 = QtWidgets.QWidget()
+ self.scrollAreaWidgetContents_3.setGeometry(QtCore.QRect(0, 0, 819, 649))
+ self.scrollAreaWidgetContents_3.setObjectName("scrollAreaWidgetContents_3")
+ self.cover_gview = QtWidgets.QGraphicsView(self.scrollAreaWidgetContents_3)
+ self.cover_gview.setGeometry(QtCore.QRect(-10, 0, 831, 651))
+ self.cover_gview.setObjectName("cover_gview")
+ self.scrollArea_3.setWidget(self.scrollAreaWidgetContents_3)
+ self.verticalLayoutWidget_2 = QtWidgets.QWidget(self.whole_cr_tab)
+ self.verticalLayoutWidget_2.setGeometry(QtCore.QRect(830, 90, 160, 186))
+ self.verticalLayoutWidget_2.setObjectName("verticalLayoutWidget_2")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_2)
+ self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.label_3 = QtWidgets.QLabel(self.verticalLayoutWidget_2)
+ self.label_3.setObjectName("label_3")
+ self.horizontalLayout.addWidget(self.label_3)
+ self.maxcr_label = QtWidgets.QLabel(self.verticalLayoutWidget_2)
+ self.maxcr_label.setObjectName("maxcr_label")
+ self.horizontalLayout.addWidget(self.maxcr_label)
+ self.verticalLayout_2.addLayout(self.horizontalLayout)
+ self.mizi_rbtn = QtWidgets.QRadioButton(self.verticalLayoutWidget_2)
+ self.mizi_rbtn.setObjectName("mizi_rbtn")
+ self.verticalLayout_2.addWidget(self.mizi_rbtn)
+ self.jingzi_rbtn = QtWidgets.QRadioButton(self.verticalLayoutWidget_2)
+ self.jingzi_rbtn.setObjectName("jingzi_rbtn")
+ self.verticalLayout_2.addWidget(self.jingzi_rbtn)
+ self.net_rbtn = QtWidgets.QRadioButton(self.verticalLayoutWidget_2)
+ self.net_rbtn.setObjectName("net_rbtn")
+ self.verticalLayout_2.addWidget(self.net_rbtn)
+ self.cr_calculate_btn = QtWidgets.QPushButton(self.verticalLayoutWidget_2)
+ self.cr_calculate_btn.setObjectName("cr_calculate_btn")
+ self.verticalLayout_2.addWidget(self.cr_calculate_btn)
+ self.whole_main_tabwidget.addTab(self.whole_cr_tab, "")
+ self.whole_goc_tab = QtWidgets.QWidget()
+ self.whole_goc_tab.setObjectName("whole_goc_tab")
+ self.scrollArea_4 = QtWidgets.QScrollArea(self.whole_goc_tab)
+ self.scrollArea_4.setGeometry(QtCore.QRect(10, 10, 401, 311))
+ self.scrollArea_4.setWidgetResizable(True)
+ self.scrollArea_4.setObjectName("scrollArea_4")
+ self.scrollAreaWidgetContents_4 = QtWidgets.QWidget()
+ self.scrollAreaWidgetContents_4.setGeometry(QtCore.QRect(0, 0, 399, 309))
+ self.scrollAreaWidgetContents_4.setObjectName("scrollAreaWidgetContents_4")
+ self.cog_template_gview = QtWidgets.QGraphicsView(self.scrollAreaWidgetContents_4)
+ self.cog_template_gview.setGeometry(QtCore.QRect(0, 0, 401, 311))
+ self.cog_template_gview.setObjectName("cog_template_gview")
+ self.scrollArea_4.setWidget(self.scrollAreaWidgetContents_4)
+ self.scrollArea_5 = QtWidgets.QScrollArea(self.whole_goc_tab)
+ self.scrollArea_5.setGeometry(QtCore.QRect(430, 10, 401, 311))
+ self.scrollArea_5.setWidgetResizable(True)
+ self.scrollArea_5.setObjectName("scrollArea_5")
+ self.scrollAreaWidgetContents_5 = QtWidgets.QWidget()
+ self.scrollAreaWidgetContents_5.setGeometry(QtCore.QRect(0, 0, 399, 309))
+ self.scrollAreaWidgetContents_5.setObjectName("scrollAreaWidgetContents_5")
+ self.cog_target_gview = QtWidgets.QGraphicsView(self.scrollAreaWidgetContents_5)
+ self.cog_target_gview.setGeometry(QtCore.QRect(0, 0, 401, 311))
+ self.cog_target_gview.setObjectName("cog_target_gview")
+ self.scrollArea_5.setWidget(self.scrollAreaWidgetContents_5)
+ self.scrollArea_6 = QtWidgets.QScrollArea(self.whole_goc_tab)
+ self.scrollArea_6.setGeometry(QtCore.QRect(10, 360, 401, 291))
+ self.scrollArea_6.setWidgetResizable(True)
+ self.scrollArea_6.setObjectName("scrollArea_6")
+ self.scrollAreaWidgetContents_6 = QtWidgets.QWidget()
+ self.scrollAreaWidgetContents_6.setGeometry(QtCore.QRect(0, 0, 399, 289))
+ self.scrollAreaWidgetContents_6.setObjectName("scrollAreaWidgetContents_6")
+ self.cog_comparison_gview = QtWidgets.QGraphicsView(self.scrollAreaWidgetContents_6)
+ self.cog_comparison_gview.setGeometry(QtCore.QRect(0, 0, 401, 291))
+ self.cog_comparison_gview.setObjectName("cog_comparison_gview")
+ self.scrollArea_6.setWidget(self.scrollAreaWidgetContents_6)
+ self.verticalLayoutWidget_3 = QtWidgets.QWidget(self.whole_goc_tab)
+ self.verticalLayoutWidget_3.setGeometry(QtCore.QRect(460, 520, 146, 96))
+ self.verticalLayoutWidget_3.setObjectName("verticalLayoutWidget_3")
+ self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_3)
+ self.verticalLayout_3.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_3.setObjectName("verticalLayout_3")
+ self.whole_cog_mizi_rbtn = QtWidgets.QRadioButton(self.verticalLayoutWidget_3)
+ self.whole_cog_mizi_rbtn.setObjectName("whole_cog_mizi_rbtn")
+ self.verticalLayout_3.addWidget(self.whole_cog_mizi_rbtn)
+ self.whole_cog_jingzi_rbtn = QtWidgets.QRadioButton(self.verticalLayoutWidget_3)
+ self.whole_cog_jingzi_rbtn.setObjectName("whole_cog_jingzi_rbtn")
+ self.verticalLayout_3.addWidget(self.whole_cog_jingzi_rbtn)
+ self.whole_cog_net_rbtn = QtWidgets.QRadioButton(self.verticalLayoutWidget_3)
+ self.whole_cog_net_rbtn.setObjectName("whole_cog_net_rbtn")
+ self.verticalLayout_3.addWidget(self.whole_cog_net_rbtn)
+ self.cog_btn = QtWidgets.QPushButton(self.verticalLayoutWidget_3)
+ self.cog_btn.setObjectName("cog_btn")
+ self.verticalLayout_3.addWidget(self.cog_btn)
+ self.horizontalLayoutWidget_2 = QtWidgets.QWidget(self.whole_goc_tab)
+ self.horizontalLayoutWidget_2.setGeometry(QtCore.QRect(110, 320, 191, 21))
+ self.horizontalLayoutWidget_2.setObjectName("horizontalLayoutWidget_2")
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget_2)
+ self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ self.label_4 = QtWidgets.QLabel(self.horizontalLayoutWidget_2)
+ self.label_4.setObjectName("label_4")
+ self.horizontalLayout_2.addWidget(self.label_4)
+ self.template_cog_label = QtWidgets.QLabel(self.horizontalLayoutWidget_2)
+ self.template_cog_label.setObjectName("template_cog_label")
+ self.horizontalLayout_2.addWidget(self.template_cog_label)
+ self.horizontalLayoutWidget_3 = QtWidgets.QWidget(self.whole_goc_tab)
+ self.horizontalLayoutWidget_3.setGeometry(QtCore.QRect(540, 320, 191, 21))
+ self.horizontalLayoutWidget_3.setObjectName("horizontalLayoutWidget_3")
+ self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget_3)
+ self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout_3.setObjectName("horizontalLayout_3")
+ self.label_5 = QtWidgets.QLabel(self.horizontalLayoutWidget_3)
+ self.label_5.setObjectName("label_5")
+ self.horizontalLayout_3.addWidget(self.label_5)
+ self.target_cog_label = QtWidgets.QLabel(self.horizontalLayoutWidget_3)
+ self.target_cog_label.setObjectName("target_cog_label")
+ self.horizontalLayout_3.addWidget(self.target_cog_label)
+ self.whole_main_tabwidget.addTab(self.whole_goc_tab, "")
+ self.whole_ch_tab = QtWidgets.QWidget()
+ self.whole_ch_tab.setObjectName("whole_ch_tab")
+ self.scrollArea_7 = QtWidgets.QScrollArea(self.whole_ch_tab)
+ self.scrollArea_7.setGeometry(QtCore.QRect(10, 10, 421, 401))
+ self.scrollArea_7.setWidgetResizable(True)
+ self.scrollArea_7.setObjectName("scrollArea_7")
+ self.scrollAreaWidgetContents_7 = QtWidgets.QWidget()
+ self.scrollAreaWidgetContents_7.setGeometry(QtCore.QRect(0, 0, 419, 399))
+ self.scrollAreaWidgetContents_7.setObjectName("scrollAreaWidgetContents_7")
+ self.template_ch_gview = QtWidgets.QGraphicsView(self.scrollAreaWidgetContents_7)
+ self.template_ch_gview.setGeometry(QtCore.QRect(0, 0, 421, 401))
+ self.template_ch_gview.setObjectName("template_ch_gview")
+ self.scrollArea_7.setWidget(self.scrollAreaWidgetContents_7)
+ self.scrollArea_8 = QtWidgets.QScrollArea(self.whole_ch_tab)
+ self.scrollArea_8.setGeometry(QtCore.QRect(470, 10, 421, 401))
+ self.scrollArea_8.setWidgetResizable(True)
+ self.scrollArea_8.setObjectName("scrollArea_8")
+ self.scrollAreaWidgetContents_8 = QtWidgets.QWidget()
+ self.scrollAreaWidgetContents_8.setGeometry(QtCore.QRect(0, 0, 419, 399))
+ self.scrollAreaWidgetContents_8.setObjectName("scrollAreaWidgetContents_8")
+ self.target_ch_gview = QtWidgets.QGraphicsView(self.scrollAreaWidgetContents_8)
+ self.target_ch_gview.setGeometry(QtCore.QRect(0, 0, 421, 401))
+ self.target_ch_gview.setObjectName("target_ch_gview")
+ self.scrollArea_8.setWidget(self.scrollAreaWidgetContents_8)
+ self.verticalLayoutWidget_4 = QtWidgets.QWidget(self.whole_ch_tab)
+ self.verticalLayoutWidget_4.setGeometry(QtCore.QRect(30, 530, 160, 96))
+ self.verticalLayoutWidget_4.setObjectName("verticalLayoutWidget_4")
+ self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_4)
+ self.verticalLayout_4.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_4.setObjectName("verticalLayout_4")
+ self.whole_ch_mizi_rbtn = QtWidgets.QRadioButton(self.verticalLayoutWidget_4)
+ self.whole_ch_mizi_rbtn.setObjectName("whole_ch_mizi_rbtn")
+ self.verticalLayout_4.addWidget(self.whole_ch_mizi_rbtn)
+ self.whole_ch_jingzi_rbtn = QtWidgets.QRadioButton(self.verticalLayoutWidget_4)
+ self.whole_ch_jingzi_rbtn.setObjectName("whole_ch_jingzi_rbtn")
+ self.verticalLayout_4.addWidget(self.whole_ch_jingzi_rbtn)
+ self.whole_ch_net_rbtn = QtWidgets.QRadioButton(self.verticalLayoutWidget_4)
+ self.whole_ch_net_rbtn.setObjectName("whole_ch_net_rbtn")
+ self.verticalLayout_4.addWidget(self.whole_ch_net_rbtn)
+ self.convexhull_btn = QtWidgets.QPushButton(self.verticalLayoutWidget_4)
+ self.convexhull_btn.setObjectName("convexhull_btn")
+ self.verticalLayout_4.addWidget(self.convexhull_btn)
+ self.formLayoutWidget = QtWidgets.QWidget(self.whole_ch_tab)
+ self.formLayoutWidget.setGeometry(QtCore.QRect(230, 440, 201, 191))
+ self.formLayoutWidget.setObjectName("formLayoutWidget")
+ self.formLayout = QtWidgets.QFormLayout(self.formLayoutWidget)
+ self.formLayout.setContentsMargins(0, 0, 0, 0)
+ self.formLayout.setObjectName("formLayout")
+ self.label_6 = QtWidgets.QLabel(self.formLayoutWidget)
+ self.label_6.setObjectName("label_6")
+ self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_6)
+ self.temp_convex_area_ratio_label = QtWidgets.QLabel(self.formLayoutWidget)
+ self.temp_convex_area_ratio_label.setObjectName("temp_convex_area_ratio_label")
+ self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.temp_convex_area_ratio_label)
+ self.label_8 = QtWidgets.QLabel(self.formLayoutWidget)
+ self.label_8.setObjectName("label_8")
+ self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_8)
+ self.temp_valid_area_ratio_label = QtWidgets.QLabel(self.formLayoutWidget)
+ self.temp_valid_area_ratio_label.setObjectName("temp_valid_area_ratio_label")
+ self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.temp_valid_area_ratio_label)
+ self.formLayoutWidget_2 = QtWidgets.QWidget(self.whole_ch_tab)
+ self.formLayoutWidget_2.setGeometry(QtCore.QRect(690, 440, 201, 191))
+ self.formLayoutWidget_2.setObjectName("formLayoutWidget_2")
+ self.formLayout_2 = QtWidgets.QFormLayout(self.formLayoutWidget_2)
+ self.formLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.formLayout_2.setObjectName("formLayout_2")
+ self.label_7 = QtWidgets.QLabel(self.formLayoutWidget_2)
+ self.label_7.setObjectName("label_7")
+ self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_7)
+ self.targ_convex_area_ratio_label_2 = QtWidgets.QLabel(self.formLayoutWidget_2)
+ self.targ_convex_area_ratio_label_2.setObjectName("targ_convex_area_ratio_label_2")
+ self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.targ_convex_area_ratio_label_2)
+ self.label_9 = QtWidgets.QLabel(self.formLayoutWidget_2)
+ self.label_9.setObjectName("label_9")
+ self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_9)
+ self.targ_valid_area_ratio_label_2 = QtWidgets.QLabel(self.formLayoutWidget_2)
+ self.targ_valid_area_ratio_label_2.setObjectName("targ_valid_area_ratio_label_2")
+ self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.targ_valid_area_ratio_label_2)
+ self.label_10 = QtWidgets.QLabel(self.whole_ch_tab)
+ self.label_10.setGeometry(QtCore.QRect(170, 420, 111, 16))
+ self.label_10.setObjectName("label_10")
+ self.label_11 = QtWidgets.QLabel(self.whole_ch_tab)
+ self.label_11.setGeometry(QtCore.QRect(620, 420, 111, 16))
+ self.label_11.setObjectName("label_11")
+ self.whole_main_tabwidget.addTab(self.whole_ch_tab, "")
+ self.whole_histogram_tab = QtWidgets.QWidget()
+ self.whole_histogram_tab.setObjectName("whole_histogram_tab")
+ self.verticalLayoutWidget_5 = QtWidgets.QWidget(self.whole_histogram_tab)
+ self.verticalLayoutWidget_5.setGeometry(QtCore.QRect(60, 570, 160, 51))
+ self.verticalLayoutWidget_5.setObjectName("verticalLayoutWidget_5")
+ self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_5)
+ self.verticalLayout_5.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_5.setObjectName("verticalLayout_5")
+ self.project_btn = QtWidgets.QPushButton(self.verticalLayoutWidget_5)
+ self.project_btn.setObjectName("project_btn")
+ self.verticalLayout_5.addWidget(self.project_btn)
+ self.label_12 = QtWidgets.QLabel(self.whole_histogram_tab)
+ self.label_12.setGeometry(QtCore.QRect(20, 140, 31, 16))
+ self.label_12.setObjectName("label_12")
+ self.label_13 = QtWidgets.QLabel(self.whole_histogram_tab)
+ self.label_13.setGeometry(QtCore.QRect(20, 380, 31, 16))
+ self.label_13.setObjectName("label_13")
+ self.label_14 = QtWidgets.QLabel(self.whole_histogram_tab)
+ self.label_14.setGeometry(QtCore.QRect(190, 540, 111, 16))
+ self.label_14.setObjectName("label_14")
+ self.label_15 = QtWidgets.QLabel(self.whole_histogram_tab)
+ self.label_15.setGeometry(QtCore.QRect(650, 540, 111, 16))
+ self.label_15.setObjectName("label_15")
+ self.formLayoutWidget_3 = QtWidgets.QWidget(self.whole_histogram_tab)
+ self.formLayoutWidget_3.setGeometry(QtCore.QRect(230, 570, 121, 51))
+ self.formLayoutWidget_3.setObjectName("formLayoutWidget_3")
+ self.formLayout_3 = QtWidgets.QFormLayout(self.formLayoutWidget_3)
+ self.formLayout_3.setContentsMargins(0, 0, 0, 0)
+ self.formLayout_3.setObjectName("formLayout_3")
+ self.label_16 = QtWidgets.QLabel(self.formLayoutWidget_3)
+ self.label_16.setObjectName("label_16")
+ self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_16)
+ self.label_20 = QtWidgets.QLabel(self.formLayoutWidget_3)
+ self.label_20.setObjectName("label_20")
+ self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_20)
+ self.temp_x_mean_label = QtWidgets.QLabel(self.formLayoutWidget_3)
+ self.temp_x_mean_label.setObjectName("temp_x_mean_label")
+ self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.temp_x_mean_label)
+ self.formLayoutWidget_4 = QtWidgets.QWidget(self.whole_histogram_tab)
+ self.formLayoutWidget_4.setGeometry(QtCore.QRect(350, 570, 121, 51))
+ self.formLayoutWidget_4.setObjectName("formLayoutWidget_4")
+ self.formLayout_4 = QtWidgets.QFormLayout(self.formLayoutWidget_4)
+ self.formLayout_4.setContentsMargins(0, 0, 0, 0)
+ self.formLayout_4.setObjectName("formLayout_4")
+ self.label_17 = QtWidgets.QLabel(self.formLayoutWidget_4)
+ self.label_17.setObjectName("label_17")
+ self.formLayout_4.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_17)
+ self.label_21 = QtWidgets.QLabel(self.formLayoutWidget_4)
+ self.label_21.setObjectName("label_21")
+ self.formLayout_4.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_21)
+ self.temp_y_mean_label = QtWidgets.QLabel(self.formLayoutWidget_4)
+ self.temp_y_mean_label.setObjectName("temp_y_mean_label")
+ self.formLayout_4.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.temp_y_mean_label)
+ self.gridLayoutWidget = QtWidgets.QWidget(self.whole_histogram_tab)
+ self.gridLayoutWidget.setGeometry(QtCore.QRect(70, 0, 391, 261))
+ self.gridLayoutWidget.setObjectName("gridLayoutWidget")
+ self.temp_x_glayout = QtWidgets.QGridLayout(self.gridLayoutWidget)
+ self.temp_x_glayout.setContentsMargins(0, 0, 0, 0)
+ self.temp_x_glayout.setObjectName("temp_x_glayout")
+ self.gridLayoutWidget_2 = QtWidgets.QWidget(self.whole_histogram_tab)
+ self.gridLayoutWidget_2.setGeometry(QtCore.QRect(510, 0, 391, 261))
+ self.gridLayoutWidget_2.setObjectName("gridLayoutWidget_2")
+ self.targ_x_glayout = QtWidgets.QGridLayout(self.gridLayoutWidget_2)
+ self.targ_x_glayout.setContentsMargins(0, 0, 0, 0)
+ self.targ_x_glayout.setObjectName("targ_x_glayout")
+ self.gridLayoutWidget_3 = QtWidgets.QWidget(self.whole_histogram_tab)
+ self.gridLayoutWidget_3.setGeometry(QtCore.QRect(70, 280, 391, 261))
+ self.gridLayoutWidget_3.setObjectName("gridLayoutWidget_3")
+ self.temp_y_glayout = QtWidgets.QGridLayout(self.gridLayoutWidget_3)
+ self.temp_y_glayout.setContentsMargins(0, 0, 0, 0)
+ self.temp_y_glayout.setObjectName("temp_y_glayout")
+ self.gridLayoutWidget_4 = QtWidgets.QWidget(self.whole_histogram_tab)
+ self.gridLayoutWidget_4.setGeometry(QtCore.QRect(510, 280, 391, 261))
+ self.gridLayoutWidget_4.setObjectName("gridLayoutWidget_4")
+ self.targ_y_glayout = QtWidgets.QGridLayout(self.gridLayoutWidget_4)
+ self.targ_y_glayout.setContentsMargins(0, 0, 0, 0)
+ self.targ_y_glayout.setObjectName("targ_y_glayout")
+ self.formLayoutWidget_5 = QtWidgets.QWidget(self.whole_histogram_tab)
+ self.formLayoutWidget_5.setGeometry(QtCore.QRect(630, 570, 121, 51))
+ self.formLayoutWidget_5.setObjectName("formLayoutWidget_5")
+ self.formLayout_5 = QtWidgets.QFormLayout(self.formLayoutWidget_5)
+ self.formLayout_5.setContentsMargins(0, 0, 0, 0)
+ self.formLayout_5.setObjectName("formLayout_5")
+ self.label_18 = QtWidgets.QLabel(self.formLayoutWidget_5)
+ self.label_18.setObjectName("label_18")
+ self.formLayout_5.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_18)
+ self.label_22 = QtWidgets.QLabel(self.formLayoutWidget_5)
+ self.label_22.setObjectName("label_22")
+ self.formLayout_5.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_22)
+ self.targ_y_mean_label = QtWidgets.QLabel(self.formLayoutWidget_5)
+ self.targ_y_mean_label.setObjectName("targ_y_mean_label")
+ self.formLayout_5.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.targ_y_mean_label)
+ self.formLayoutWidget_6 = QtWidgets.QWidget(self.whole_histogram_tab)
+ self.formLayoutWidget_6.setGeometry(QtCore.QRect(510, 570, 121, 51))
+ self.formLayoutWidget_6.setObjectName("formLayoutWidget_6")
+ self.formLayout_6 = QtWidgets.QFormLayout(self.formLayoutWidget_6)
+ self.formLayout_6.setContentsMargins(0, 0, 0, 0)
+ self.formLayout_6.setObjectName("formLayout_6")
+ self.label_19 = QtWidgets.QLabel(self.formLayoutWidget_6)
+ self.label_19.setObjectName("label_19")
+ self.formLayout_6.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_19)
+ self.label_23 = QtWidgets.QLabel(self.formLayoutWidget_6)
+ self.label_23.setObjectName("label_23")
+ self.formLayout_6.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_23)
+ self.targ_x_mean_label = QtWidgets.QLabel(self.formLayoutWidget_6)
+ self.targ_x_mean_label.setObjectName("targ_x_mean_label")
+ self.formLayout_6.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.targ_x_mean_label)
+ self.formLayoutWidget_7 = QtWidgets.QWidget(self.whole_histogram_tab)
+ self.formLayoutWidget_7.setGeometry(QtCore.QRect(800, 550, 160, 80))
+ self.formLayoutWidget_7.setObjectName("formLayoutWidget_7")
+ self.formLayout_7 = QtWidgets.QFormLayout(self.formLayoutWidget_7)
+ self.formLayout_7.setContentsMargins(0, 0, 0, 0)
+ self.formLayout_7.setObjectName("formLayout_7")
+ self.label_24 = QtWidgets.QLabel(self.formLayoutWidget_7)
+ self.label_24.setObjectName("label_24")
+ self.formLayout_7.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_24)
+ self.label_25 = QtWidgets.QLabel(self.formLayoutWidget_7)
+ self.label_25.setObjectName("label_25")
+ self.formLayout_7.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_25)
+ self.label_26 = QtWidgets.QLabel(self.formLayoutWidget_7)
+ self.label_26.setObjectName("label_26")
+ self.formLayout_7.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_26)
+ self.variance_x_label = QtWidgets.QLabel(self.formLayoutWidget_7)
+ self.variance_x_label.setObjectName("variance_x_label")
+ self.formLayout_7.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.variance_x_label)
+ self.variance_y_label = QtWidgets.QLabel(self.formLayoutWidget_7)
+ self.variance_y_label.setObjectName("variance_y_label")
+ self.formLayout_7.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.variance_y_label)
+ self.whole_main_tabwidget.addTab(self.whole_histogram_tab, "")
+ self.comparison_main_tab.addTab(self.whole_comparison_tab, "")
+ self.radical_comparison_tab = QtWidgets.QWidget()
+ self.radical_comparison_tab.setObjectName("radical_comparison_tab")
+ self.verticalLayoutWidget_6 = QtWidgets.QWidget(self.radical_comparison_tab)
+ self.verticalLayoutWidget_6.setGeometry(QtCore.QRect(10, 50, 160, 96))
+ self.verticalLayoutWidget_6.setObjectName("verticalLayoutWidget_6")
+ self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_6)
+ self.verticalLayout_6.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_6.setObjectName("verticalLayout_6")
+ self.radicals_jingzi_rbtn = QtWidgets.QRadioButton(self.verticalLayoutWidget_6)
+ self.radicals_jingzi_rbtn.setObjectName("radicals_jingzi_rbtn")
+ self.verticalLayout_6.addWidget(self.radicals_jingzi_rbtn)
+ self.radicals_mizi_rbtn = QtWidgets.QRadioButton(self.verticalLayoutWidget_6)
+ self.radicals_mizi_rbtn.setObjectName("radicals_mizi_rbtn")
+ self.verticalLayout_6.addWidget(self.radicals_mizi_rbtn)
+ self.radicals_net_rbtn = QtWidgets.QRadioButton(self.verticalLayoutWidget_6)
+ self.radicals_net_rbtn.setObjectName("radicals_net_rbtn")
+ self.verticalLayout_6.addWidget(self.radicals_net_rbtn)
+ self.stroke_layout_btn = QtWidgets.QPushButton(self.verticalLayoutWidget_6)
+ self.stroke_layout_btn.setObjectName("stroke_layout_btn")
+ self.verticalLayout_6.addWidget(self.stroke_layout_btn)
+ self.scrollArea_9 = QtWidgets.QScrollArea(self.radical_comparison_tab)
+ self.scrollArea_9.setGeometry(QtCore.QRect(180, 20, 400, 400))
+ self.scrollArea_9.setWidgetResizable(True)
+ self.scrollArea_9.setObjectName("scrollArea_9")
+ self.scrollAreaWidgetContents_9 = QtWidgets.QWidget()
+ self.scrollAreaWidgetContents_9.setGeometry(QtCore.QRect(0, 0, 398, 398))
+ self.scrollAreaWidgetContents_9.setObjectName("scrollAreaWidgetContents_9")
+ self.template_strokes_layout_gview = QtWidgets.QGraphicsView(self.scrollAreaWidgetContents_9)
+ self.template_strokes_layout_gview.setGeometry(QtCore.QRect(0, 0, 400, 400))
+ self.template_strokes_layout_gview.setObjectName("template_strokes_layout_gview")
+ self.scrollArea_9.setWidget(self.scrollAreaWidgetContents_9)
+ self.scrollArea_10 = QtWidgets.QScrollArea(self.radical_comparison_tab)
+ self.scrollArea_10.setGeometry(QtCore.QRect(590, 20, 400, 400))
+ self.scrollArea_10.setWidgetResizable(True)
+ self.scrollArea_10.setObjectName("scrollArea_10")
+ self.scrollAreaWidgetContents_10 = QtWidgets.QWidget()
+ self.scrollAreaWidgetContents_10.setGeometry(QtCore.QRect(0, 0, 398, 398))
+ self.scrollAreaWidgetContents_10.setObjectName("scrollAreaWidgetContents_10")
+ self.target_strokes_layout_gview = QtWidgets.QGraphicsView(self.scrollAreaWidgetContents_10)
+ self.target_strokes_layout_gview.setGeometry(QtCore.QRect(0, 0, 400, 400))
+ self.target_strokes_layout_gview.setObjectName("target_strokes_layout_gview")
+ self.scrollArea_10.setWidget(self.scrollAreaWidgetContents_10)
+ self.label_27 = QtWidgets.QLabel(self.radical_comparison_tab)
+ self.label_27.setGeometry(QtCore.QRect(330, 430, 101, 16))
+ self.label_27.setObjectName("label_27")
+ self.label_33 = QtWidgets.QLabel(self.radical_comparison_tab)
+ self.label_33.setGeometry(QtCore.QRect(740, 430, 101, 16))
+ self.label_33.setObjectName("label_33")
+ self.comparison_main_tab.addTab(self.radical_comparison_tab, "")
+ self.stroke_comparison_tab = QtWidgets.QWidget()
+ self.stroke_comparison_tab.setObjectName("stroke_comparison_tab")
+ self.verticalLayoutWidget_7 = QtWidgets.QWidget(self.stroke_comparison_tab)
+ self.verticalLayoutWidget_7.setGeometry(QtCore.QRect(0, 10, 131, 461))
+ self.verticalLayoutWidget_7.setObjectName("verticalLayoutWidget_7")
+ self.verticalLayout_7 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_7)
+ self.verticalLayout_7.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_7.setObjectName("verticalLayout_7")
+ self.label_28 = QtWidgets.QLabel(self.verticalLayoutWidget_7)
+ self.label_28.setObjectName("label_28")
+ self.verticalLayout_7.addWidget(self.label_28)
+ self.line = QtWidgets.QFrame(self.verticalLayoutWidget_7)
+ self.line.setLineWidth(3)
+ self.line.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line.setObjectName("line")
+ self.verticalLayout_7.addWidget(self.line)
+ self.strokes_cr_listview = QtWidgets.QListView(self.verticalLayoutWidget_7)
+ self.strokes_cr_listview.setObjectName("strokes_cr_listview")
+ self.verticalLayout_7.addWidget(self.strokes_cr_listview)
+ self.stroke_main_tabwidget = QtWidgets.QTabWidget(self.stroke_comparison_tab)
+ self.stroke_main_tabwidget.setGeometry(QtCore.QRect(130, 10, 851, 681))
+ self.stroke_main_tabwidget.setObjectName("stroke_main_tabwidget")
+ self.stroke_cr_tab = QtWidgets.QWidget()
+ self.stroke_cr_tab.setObjectName("stroke_cr_tab")
+ self.scrollArea_11 = QtWidgets.QScrollArea(self.stroke_cr_tab)
+ self.scrollArea_11.setGeometry(QtCore.QRect(10, 10, 600, 600))
+ self.scrollArea_11.setWidgetResizable(True)
+ self.scrollArea_11.setObjectName("scrollArea_11")
+ self.scrollAreaWidgetContents_11 = QtWidgets.QWidget()
+ self.scrollAreaWidgetContents_11.setGeometry(QtCore.QRect(0, 0, 598, 598))
+ self.scrollAreaWidgetContents_11.setObjectName("scrollAreaWidgetContents_11")
+ self.strokes_cr_gview = QtWidgets.QGraphicsView(self.scrollAreaWidgetContents_11)
+ self.strokes_cr_gview.setGeometry(QtCore.QRect(0, 0, 600, 600))
+ self.strokes_cr_gview.setObjectName("strokes_cr_gview")
+ self.scrollArea_11.setWidget(self.scrollAreaWidgetContents_11)
+ self.formLayoutWidget_8 = QtWidgets.QWidget(self.stroke_cr_tab)
+ self.formLayoutWidget_8.setGeometry(QtCore.QRect(640, 10, 201, 161))
+ self.formLayoutWidget_8.setObjectName("formLayoutWidget_8")
+ self.formLayout_8 = QtWidgets.QFormLayout(self.formLayoutWidget_8)
+ self.formLayout_8.setContentsMargins(0, 0, 0, 0)
+ self.formLayout_8.setObjectName("formLayout_8")
+ self.label_29 = QtWidgets.QLabel(self.formLayoutWidget_8)
+ self.label_29.setObjectName("label_29")
+ self.formLayout_8.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_29)
+ self.strokes_cr_label = QtWidgets.QLabel(self.formLayoutWidget_8)
+ self.strokes_cr_label.setObjectName("strokes_cr_label")
+ self.formLayout_8.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.strokes_cr_label)
+ self.strokes_cr_btn = QtWidgets.QPushButton(self.formLayoutWidget_8)
+ self.strokes_cr_btn.setObjectName("strokes_cr_btn")
+ self.formLayout_8.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.strokes_cr_btn)
+ self.verticalLayoutWidget_8 = QtWidgets.QWidget(self.stroke_cr_tab)
+ self.verticalLayoutWidget_8.setGeometry(QtCore.QRect(640, 190, 160, 80))
+ self.verticalLayoutWidget_8.setObjectName("verticalLayoutWidget_8")
+ self.verticalLayout_8 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_8)
+ self.verticalLayout_8.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_8.setObjectName("verticalLayout_8")
+ self.stroke_jingzi_rbtn = QtWidgets.QRadioButton(self.verticalLayoutWidget_8)
+ self.stroke_jingzi_rbtn.setObjectName("stroke_jingzi_rbtn")
+ self.verticalLayout_8.addWidget(self.stroke_jingzi_rbtn)
+ self.stroke_mizi_rbtn = QtWidgets.QRadioButton(self.verticalLayoutWidget_8)
+ self.stroke_mizi_rbtn.setObjectName("stroke_mizi_rbtn")
+ self.verticalLayout_8.addWidget(self.stroke_mizi_rbtn)
+ self.stroke_net_rbtn = QtWidgets.QRadioButton(self.verticalLayoutWidget_8)
+ self.stroke_net_rbtn.setObjectName("stroke_net_rbtn")
+ self.verticalLayout_8.addWidget(self.stroke_net_rbtn)
+ self.stroke_main_tabwidget.addTab(self.stroke_cr_tab, "")
+ self.stroke_segment_tab = QtWidgets.QWidget()
+ self.stroke_segment_tab.setObjectName("stroke_segment_tab")
+ self.scrollArea_12 = QtWidgets.QScrollArea(self.stroke_segment_tab)
+ self.scrollArea_12.setGeometry(QtCore.QRect(10, 10, 261, 241))
+ self.scrollArea_12.setWidgetResizable(True)
+ self.scrollArea_12.setObjectName("scrollArea_12")
+ self.scrollAreaWidgetContents_12 = QtWidgets.QWidget()
+ self.scrollAreaWidgetContents_12.setGeometry(QtCore.QRect(0, 0, 259, 239))
+ self.scrollAreaWidgetContents_12.setObjectName("scrollAreaWidgetContents_12")
+ self.graphicsView = QtWidgets.QGraphicsView(self.scrollAreaWidgetContents_12)
+ self.graphicsView.setGeometry(QtCore.QRect(0, 0, 261, 241))
+ self.graphicsView.setObjectName("graphicsView")
+ self.scrollArea_12.setWidget(self.scrollAreaWidgetContents_12)
+ self.scrollArea_13 = QtWidgets.QScrollArea(self.stroke_segment_tab)
+ self.scrollArea_13.setGeometry(QtCore.QRect(10, 290, 261, 241))
+ self.scrollArea_13.setWidgetResizable(True)
+ self.scrollArea_13.setObjectName("scrollArea_13")
+ self.scrollAreaWidgetContents_13 = QtWidgets.QWidget()
+ self.scrollAreaWidgetContents_13.setGeometry(QtCore.QRect(0, 0, 259, 239))
+ self.scrollAreaWidgetContents_13.setObjectName("scrollAreaWidgetContents_13")
+ self.graphicsView_2 = QtWidgets.QGraphicsView(self.scrollAreaWidgetContents_13)
+ self.graphicsView_2.setGeometry(QtCore.QRect(0, 0, 261, 241))
+ self.graphicsView_2.setObjectName("graphicsView_2")
+ self.scrollArea_13.setWidget(self.scrollAreaWidgetContents_13)
+ self.scrollArea_14 = QtWidgets.QScrollArea(self.stroke_segment_tab)
+ self.scrollArea_14.setGeometry(QtCore.QRect(310, 10, 531, 441))
+ self.scrollArea_14.setWidgetResizable(True)
+ self.scrollArea_14.setObjectName("scrollArea_14")
+ self.scrollAreaWidgetContents_14 = QtWidgets.QWidget()
+ self.scrollAreaWidgetContents_14.setGeometry(QtCore.QRect(0, 0, 529, 439))
+ self.scrollAreaWidgetContents_14.setObjectName("scrollAreaWidgetContents_14")
+ self.graphicsView_3 = QtWidgets.QGraphicsView(self.scrollAreaWidgetContents_14)
+ self.graphicsView_3.setGeometry(QtCore.QRect(0, 0, 531, 441))
+ self.graphicsView_3.setObjectName("graphicsView_3")
+ self.scrollArea_14.setWidget(self.scrollAreaWidgetContents_14)
+ self.label_30 = QtWidgets.QLabel(self.stroke_segment_tab)
+ self.label_30.setGeometry(QtCore.QRect(100, 260, 60, 16))
+ self.label_30.setObjectName("label_30")
+ self.label_31 = QtWidgets.QLabel(self.stroke_segment_tab)
+ self.label_31.setGeometry(QtCore.QRect(100, 540, 60, 16))
+ self.label_31.setObjectName("label_31")
+ self.label_32 = QtWidgets.QLabel(self.stroke_segment_tab)
+ self.label_32.setGeometry(QtCore.QRect(560, 460, 81, 16))
+ self.label_32.setObjectName("label_32")
+ self.pushButton = QtWidgets.QPushButton(self.stroke_segment_tab)
+ self.pushButton.setGeometry(QtCore.QRect(70, 570, 113, 32))
+ self.pushButton.setObjectName("pushButton")
+ self.listView_2 = QtWidgets.QListView(self.stroke_segment_tab)
+ self.listView_2.setGeometry(QtCore.QRect(310, 480, 171, 161))
+ self.listView_2.setObjectName("listView_2")
+ self.stroke_main_tabwidget.addTab(self.stroke_segment_tab, "")
+ self.comparison_main_tab.addTab(self.stroke_comparison_tab, "")
+ self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
+ self.verticalLayoutWidget.setGeometry(QtCore.QRect(10, 10, 171, 100))
+ self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
+ self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
+ self.verticalLayout.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.open_template_btn = QtWidgets.QPushButton(self.verticalLayoutWidget)
+ self.open_template_btn.setObjectName("open_template_btn")
+ self.verticalLayout.addWidget(self.open_template_btn)
+ self.open_target_btn = QtWidgets.QPushButton(self.verticalLayoutWidget)
+ self.open_target_btn.setObjectName("open_target_btn")
+ self.verticalLayout.addWidget(self.open_target_btn)
+ self.exit_btn = QtWidgets.QPushButton(self.verticalLayoutWidget)
+ self.exit_btn.setObjectName("exit_btn")
+ self.verticalLayout.addWidget(self.exit_btn)
+ self.scrollArea = QtWidgets.QScrollArea(self.centralwidget)
+ self.scrollArea.setGeometry(QtCore.QRect(10, 130, 321, 281))
+ self.scrollArea.setWidgetResizable(True)
+ self.scrollArea.setObjectName("scrollArea")
+ self.scrollAreaWidgetContents = QtWidgets.QWidget()
+ self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 319, 279))
+ self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
+ self.template_gview = QtWidgets.QGraphicsView(self.scrollAreaWidgetContents)
+ self.template_gview.setGeometry(QtCore.QRect(0, 0, 321, 281))
+ self.template_gview.setObjectName("template_gview")
+ self.scrollArea.setWidget(self.scrollAreaWidgetContents)
+ self.label = QtWidgets.QLabel(self.centralwidget)
+ self.label.setGeometry(QtCore.QRect(130, 110, 60, 16))
+ self.label.setObjectName("label")
+ self.label_2 = QtWidgets.QLabel(self.centralwidget)
+ self.label_2.setGeometry(QtCore.QRect(130, 420, 60, 16))
+ self.label_2.setObjectName("label_2")
+ self.scrollArea_2 = QtWidgets.QScrollArea(self.centralwidget)
+ self.scrollArea_2.setGeometry(QtCore.QRect(10, 440, 321, 281))
+ self.scrollArea_2.setWidgetResizable(True)
+ self.scrollArea_2.setObjectName("scrollArea_2")
+ self.scrollAreaWidgetContents_2 = QtWidgets.QWidget()
+ self.scrollAreaWidgetContents_2.setGeometry(QtCore.QRect(0, 0, 319, 279))
+ self.scrollAreaWidgetContents_2.setObjectName("scrollAreaWidgetContents_2")
+ self.target_gview = QtWidgets.QGraphicsView(self.scrollAreaWidgetContents_2)
+ self.target_gview.setGeometry(QtCore.QRect(0, 0, 321, 281))
+ self.target_gview.setObjectName("target_gview")
+ self.scrollArea_2.setWidget(self.scrollAreaWidgetContents_2)
+ MainWindow.setCentralWidget(self.centralwidget)
+ self.menubar = QtWidgets.QMenuBar(MainWindow)
+ self.menubar.setGeometry(QtCore.QRect(0, 0, 1343, 22))
+ self.menubar.setObjectName("menubar")
+ MainWindow.setMenuBar(self.menubar)
+ self.statusbar = QtWidgets.QStatusBar(MainWindow)
+ self.statusbar.setObjectName("statusbar")
+ MainWindow.setStatusBar(self.statusbar)
+
+ self.retranslateUi(MainWindow)
+ self.comparison_main_tab.setCurrentIndex(2)
+ self.whole_main_tabwidget.setCurrentIndex(3)
+ self.stroke_main_tabwidget.setCurrentIndex(0)
+ QtCore.QMetaObject.connectSlotsByName(MainWindow)
+
+ def retranslateUi(self, MainWindow):
+ _translate = QtCore.QCoreApplication.translate
+ MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
+ self.label_3.setText(_translate("MainWindow", "Max CR:"))
+ self.maxcr_label.setText(_translate("MainWindow", "0.0"))
+ self.mizi_rbtn.setText(_translate("MainWindow", "MiZi-Grid"))
+ self.jingzi_rbtn.setText(_translate("MainWindow", "JingZi-Grid"))
+ self.net_rbtn.setText(_translate("MainWindow", "20_Net-Grid"))
+ self.cr_calculate_btn.setText(_translate("MainWindow", "Calculate"))
+ self.whole_main_tabwidget.setTabText(self.whole_main_tabwidget.indexOf(self.whole_cr_tab), _translate("MainWindow", "Coverage Rate"))
+ self.whole_cog_mizi_rbtn.setText(_translate("MainWindow", "MiZi-Grid"))
+ self.whole_cog_jingzi_rbtn.setText(_translate("MainWindow", "JingZi-Grid"))
+ self.whole_cog_net_rbtn.setText(_translate("MainWindow", "20_Net-Grid"))
+ self.cog_btn.setText(_translate("MainWindow", "Center of Gravity"))
+ self.label_4.setText(_translate("MainWindow", "COG:"))
+ self.template_cog_label.setText(_translate("MainWindow", "(0, 0)"))
+ self.label_5.setText(_translate("MainWindow", "COG:"))
+ self.target_cog_label.setText(_translate("MainWindow", "(0, 0)"))
+ self.whole_main_tabwidget.setTabText(self.whole_main_tabwidget.indexOf(self.whole_goc_tab), _translate("MainWindow", "Gravity of Center"))
+ self.whole_ch_mizi_rbtn.setText(_translate("MainWindow", "MiZi-Grid"))
+ self.whole_ch_jingzi_rbtn.setText(_translate("MainWindow", "JingZi-Grid"))
+ self.whole_ch_net_rbtn.setText(_translate("MainWindow", "20_Net-Grid"))
+ self.convexhull_btn.setText(_translate("MainWindow", "Convex hull"))
+ self.label_6.setText(_translate("MainWindow", "Convex hull \n"
+" area ratio:"))
+ self.temp_convex_area_ratio_label.setText(_translate("MainWindow", "0.0"))
+ self.label_8.setText(_translate("MainWindow", "Valid pixels \n"
+" area ratio:"))
+ self.temp_valid_area_ratio_label.setText(_translate("MainWindow", "0.0"))
+ self.label_7.setText(_translate("MainWindow", "Convex hull \n"
+" area ratio:"))
+ self.targ_convex_area_ratio_label_2.setText(_translate("MainWindow", "0.0"))
+ self.label_9.setText(_translate("MainWindow", "Valid pixels \n"
+" area ratio:"))
+ self.targ_valid_area_ratio_label_2.setText(_translate("MainWindow", "0.0"))
+ self.label_10.setText(_translate("MainWindow", "Template image"))
+ self.label_11.setText(_translate("MainWindow", "Target image"))
+ self.whole_main_tabwidget.setTabText(self.whole_main_tabwidget.indexOf(self.whole_ch_tab), _translate("MainWindow", "Convex hull"))
+ self.project_btn.setText(_translate("MainWindow", "Project"))
+ self.label_12.setText(_translate("MainWindow", "X:"))
+ self.label_13.setText(_translate("MainWindow", "Y:"))
+ self.label_14.setText(_translate("MainWindow", "Template Image"))
+ self.label_15.setText(_translate("MainWindow", "Target Image"))
+ self.label_16.setText(_translate("MainWindow", "X axis "))
+ self.label_20.setText(_translate("MainWindow", "Mean:"))
+ self.temp_x_mean_label.setText(_translate("MainWindow", "0.0"))
+ self.label_17.setText(_translate("MainWindow", "Y axis "))
+ self.label_21.setText(_translate("MainWindow", "Mean:"))
+ self.temp_y_mean_label.setText(_translate("MainWindow", "0.0"))
+ self.label_18.setText(_translate("MainWindow", "Y axis "))
+ self.label_22.setText(_translate("MainWindow", "Mean:"))
+ self.targ_y_mean_label.setText(_translate("MainWindow", "0.0"))
+ self.label_19.setText(_translate("MainWindow", "X axis "))
+ self.label_23.setText(_translate("MainWindow", "Mean:"))
+ self.targ_x_mean_label.setText(_translate("MainWindow", "0.0"))
+ self.label_24.setText(_translate("MainWindow", "Variance"))
+ self.label_25.setText(_translate("MainWindow", "X-axis:"))
+ self.label_26.setText(_translate("MainWindow", "Y-axis:"))
+ self.variance_x_label.setText(_translate("MainWindow", "0.0"))
+ self.variance_y_label.setText(_translate("MainWindow", "0.0"))
+ self.whole_main_tabwidget.setTabText(self.whole_main_tabwidget.indexOf(self.whole_histogram_tab), _translate("MainWindow", "Histogram"))
+ self.comparison_main_tab.setTabText(self.comparison_main_tab.indexOf(self.whole_comparison_tab), _translate("MainWindow", "Whole Comparison"))
+ self.radicals_jingzi_rbtn.setText(_translate("MainWindow", "JingZi-Grid"))
+ self.radicals_mizi_rbtn.setText(_translate("MainWindow", "MiZi-Grid"))
+ self.radicals_net_rbtn.setText(_translate("MainWindow", "20_Net-Grid"))
+ self.stroke_layout_btn.setText(_translate("MainWindow", "Strokes Layout"))
+ self.label_27.setText(_translate("MainWindow", "Template image"))
+ self.label_33.setText(_translate("MainWindow", "Target image"))
+ self.comparison_main_tab.setTabText(self.comparison_main_tab.indexOf(self.radical_comparison_tab), _translate("MainWindow", "Radical Comparison"))
+ self.label_28.setText(_translate("MainWindow", "Strokes:"))
+ self.label_29.setText(_translate("MainWindow", "Max CR:"))
+ self.strokes_cr_label.setText(_translate("MainWindow", "0.0"))
+ self.strokes_cr_btn.setText(_translate("MainWindow", "Calculate"))
+ self.stroke_jingzi_rbtn.setText(_translate("MainWindow", "JingZi-Grid"))
+ self.stroke_mizi_rbtn.setText(_translate("MainWindow", "MiZi-Grid"))
+ self.stroke_net_rbtn.setText(_translate("MainWindow", "20_Net-Grid"))
+ self.stroke_main_tabwidget.setTabText(self.stroke_main_tabwidget.indexOf(self.stroke_cr_tab), _translate("MainWindow", "Coverage Rate"))
+ self.label_30.setText(_translate("MainWindow", "Template"))
+ self.label_31.setText(_translate("MainWindow", "Target"))
+ self.label_32.setText(_translate("MainWindow", "Comparison"))
+ self.pushButton.setText(_translate("MainWindow", "Segment"))
+ self.stroke_main_tabwidget.setTabText(self.stroke_main_tabwidget.indexOf(self.stroke_segment_tab), _translate("MainWindow", "Segmentation"))
+ self.comparison_main_tab.setTabText(self.comparison_main_tab.indexOf(self.stroke_comparison_tab), _translate("MainWindow", "Stroke Comparison"))
+ self.open_template_btn.setText(_translate("MainWindow", "Open Template"))
+ self.open_target_btn.setText(_translate("MainWindow", "Open Target"))
+ self.exit_btn.setText(_translate("MainWindow", "Exit"))
+ self.label.setText(_translate("MainWindow", "Original"))
+ self.label_2.setText(_translate("MainWindow", "Target"))
+
diff --git a/calligraphyEvaluationTool/calligraphycomparisonmainwindow.ui b/calligraphyEvaluationTool/calligraphycomparisonmainwindow.ui
new file mode 100644
index 0000000..c24f846
--- /dev/null
+++ b/calligraphyEvaluationTool/calligraphycomparisonmainwindow.ui
@@ -0,0 +1,1463 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 1343
+ 782
+
+
+
+ MainWindow
+
+
+
+
+
+ 339
+ 9
+ 1001
+ 721
+
+
+
+ 2
+
+
+
+ Whole Comparison
+
+
+
+
+ -1
+ 9
+ 1001
+ 681
+
+
+
+ 3
+
+
+
+ Coverage Rate
+
+
+
+
+ 0
+ 0
+ 821
+ 651
+
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 819
+ 649
+
+
+
+
+
+ -10
+ 0
+ 831
+ 651
+
+
+
+
+
+
+
+
+ 830
+ 90
+ 160
+ 186
+
+
+
+ -
+
+
-
+
+
+ Max CR:
+
+
+
+ -
+
+
+ 0.0
+
+
+
+
+
+ -
+
+
+ MiZi-Grid
+
+
+
+ -
+
+
+ JingZi-Grid
+
+
+
+ -
+
+
+ 20_Net-Grid
+
+
+
+ -
+
+
+ Calculate
+
+
+
+
+
+
+
+
+ Gravity of Center
+
+
+
+
+ 10
+ 10
+ 401
+ 311
+
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 399
+ 309
+
+
+
+
+
+ 0
+ 0
+ 401
+ 311
+
+
+
+
+
+
+
+
+ 430
+ 10
+ 401
+ 311
+
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 399
+ 309
+
+
+
+
+
+ 0
+ 0
+ 401
+ 311
+
+
+
+
+
+
+
+
+ 10
+ 360
+ 401
+ 291
+
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 399
+ 289
+
+
+
+
+
+ 0
+ 0
+ 401
+ 291
+
+
+
+
+
+
+
+
+ 460
+ 520
+ 146
+ 96
+
+
+
+ -
+
+
+ MiZi-Grid
+
+
+
+ -
+
+
+ JingZi-Grid
+
+
+
+ -
+
+
+ 20_Net-Grid
+
+
+
+ -
+
+
+ Center of Gravity
+
+
+
+
+
+
+
+
+ 110
+ 320
+ 191
+ 21
+
+
+
+ -
+
+
+ COG:
+
+
+
+ -
+
+
+ (0, 0)
+
+
+
+
+
+
+
+
+ 540
+ 320
+ 191
+ 21
+
+
+
+ -
+
+
+ COG:
+
+
+
+ -
+
+
+ (0, 0)
+
+
+
+
+
+
+
+
+ Convex hull
+
+
+
+
+ 10
+ 10
+ 421
+ 401
+
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 419
+ 399
+
+
+
+
+
+ 0
+ 0
+ 421
+ 401
+
+
+
+
+
+
+
+
+ 470
+ 10
+ 421
+ 401
+
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 419
+ 399
+
+
+
+
+
+ 0
+ 0
+ 421
+ 401
+
+
+
+
+
+
+
+
+ 30
+ 530
+ 160
+ 96
+
+
+
+ -
+
+
+ MiZi-Grid
+
+
+
+ -
+
+
+ JingZi-Grid
+
+
+
+ -
+
+
+ 20_Net-Grid
+
+
+
+ -
+
+
+ Convex hull
+
+
+
+
+
+
+
+
+ 230
+ 440
+ 201
+ 191
+
+
+
+ -
+
+
+ Convex hull
+ area ratio:
+
+
+
+ -
+
+
+ 0.0
+
+
+
+ -
+
+
+ Valid pixels
+ area ratio:
+
+
+
+ -
+
+
+ 0.0
+
+
+
+
+
+
+
+
+ 690
+ 440
+ 201
+ 191
+
+
+
+ -
+
+
+ Convex hull
+ area ratio:
+
+
+
+ -
+
+
+ 0.0
+
+
+
+ -
+
+
+ Valid pixels
+ area ratio:
+
+
+
+ -
+
+
+ 0.0
+
+
+
+
+
+
+
+
+ 170
+ 420
+ 111
+ 16
+
+
+
+ Template image
+
+
+
+
+
+ 620
+ 420
+ 111
+ 16
+
+
+
+ Target image
+
+
+
+
+
+ Histogram
+
+
+
+
+ 60
+ 570
+ 160
+ 51
+
+
+
+ -
+
+
+ Project
+
+
+
+
+
+
+
+
+ 20
+ 140
+ 31
+ 16
+
+
+
+ X:
+
+
+
+
+
+ 20
+ 380
+ 31
+ 16
+
+
+
+ Y:
+
+
+
+
+
+ 190
+ 540
+ 111
+ 16
+
+
+
+ Template Image
+
+
+
+
+
+ 650
+ 540
+ 111
+ 16
+
+
+
+ Target Image
+
+
+
+
+
+ 230
+ 570
+ 121
+ 51
+
+
+
+ -
+
+
+ X axis
+
+
+
+ -
+
+
+ Mean:
+
+
+
+ -
+
+
+ 0.0
+
+
+
+
+
+
+
+
+ 350
+ 570
+ 121
+ 51
+
+
+
+ -
+
+
+ Y axis
+
+
+
+ -
+
+
+ Mean:
+
+
+
+ -
+
+
+ 0.0
+
+
+
+
+
+
+
+
+ 70
+ 0
+ 391
+ 261
+
+
+
+
+
+
+
+ 510
+ 0
+ 391
+ 261
+
+
+
+
+
+
+
+ 70
+ 280
+ 391
+ 261
+
+
+
+
+
+
+
+ 510
+ 280
+ 391
+ 261
+
+
+
+
+
+
+
+ 630
+ 570
+ 121
+ 51
+
+
+
+ -
+
+
+ Y axis
+
+
+
+ -
+
+
+ Mean:
+
+
+
+ -
+
+
+ 0.0
+
+
+
+
+
+
+
+
+ 510
+ 570
+ 121
+ 51
+
+
+
+ -
+
+
+ X axis
+
+
+
+ -
+
+
+ Mean:
+
+
+
+ -
+
+
+ 0.0
+
+
+
+
+
+
+
+
+ 800
+ 550
+ 160
+ 80
+
+
+
+
+ -1
+
+ -
+
+
+ Variance
+
+
+
+ -
+
+
+ X-axis:
+
+
+
+ -
+
+
+ Y-axis:
+
+
+
+ -
+
+
+ 0.0
+
+
+
+ -
+
+
+ 0.0
+
+
+
+
+
+
+
+
+
+
+ Radical Comparison
+
+
+
+
+ 10
+ 50
+ 160
+ 96
+
+
+
+ -
+
+
+ JingZi-Grid
+
+
+
+ -
+
+
+ MiZi-Grid
+
+
+
+ -
+
+
+ 20_Net-Grid
+
+
+
+ -
+
+
+ Strokes Layout
+
+
+
+
+
+
+
+
+ 180
+ 20
+ 400
+ 400
+
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 398
+ 398
+
+
+
+
+
+ 0
+ 0
+ 400
+ 400
+
+
+
+
+
+
+
+
+ 590
+ 20
+ 400
+ 400
+
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 398
+ 398
+
+
+
+
+
+ 0
+ 0
+ 400
+ 400
+
+
+
+
+
+
+
+
+ 330
+ 430
+ 101
+ 16
+
+
+
+ Template image
+
+
+
+
+
+ 740
+ 430
+ 101
+ 16
+
+
+
+ Target image
+
+
+
+
+
+ Stroke Comparison
+
+
+
+
+ 0
+ 10
+ 131
+ 461
+
+
+
+ -
+
+
+ Strokes:
+
+
+
+ -
+
+
+ 3
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+
+
+
+
+ 130
+ 10
+ 851
+ 681
+
+
+
+ 0
+
+
+
+ Coverage Rate
+
+
+
+
+ 10
+ 10
+ 600
+ 600
+
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 598
+ 598
+
+
+
+
+
+ 0
+ 0
+ 600
+ 600
+
+
+
+
+
+
+
+
+ 640
+ 10
+ 201
+ 161
+
+
+
+ -
+
+
+ Max CR:
+
+
+
+ -
+
+
+ 0.0
+
+
+
+ -
+
+
+ Calculate
+
+
+
+
+
+
+
+
+ 640
+ 190
+ 160
+ 80
+
+
+
+ -
+
+
+ JingZi-Grid
+
+
+
+ -
+
+
+ MiZi-Grid
+
+
+
+ -
+
+
+ 20_Net-Grid
+
+
+
+
+
+
+
+
+ Segmentation
+
+
+
+
+ 10
+ 10
+ 261
+ 241
+
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 259
+ 239
+
+
+
+
+
+ 0
+ 0
+ 261
+ 241
+
+
+
+
+
+
+
+
+ 10
+ 290
+ 261
+ 241
+
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 259
+ 239
+
+
+
+
+
+ 0
+ 0
+ 261
+ 241
+
+
+
+
+
+
+
+
+ 310
+ 10
+ 531
+ 441
+
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 529
+ 439
+
+
+
+
+
+ 0
+ 0
+ 531
+ 441
+
+
+
+
+
+
+
+
+ 100
+ 260
+ 60
+ 16
+
+
+
+ Template
+
+
+
+
+
+ 100
+ 540
+ 60
+ 16
+
+
+
+ Target
+
+
+
+
+
+ 560
+ 460
+ 81
+ 16
+
+
+
+ Comparison
+
+
+
+
+
+ 70
+ 570
+ 113
+ 32
+
+
+
+ Segment
+
+
+
+
+
+ 310
+ 480
+ 171
+ 161
+
+
+
+
+
+
+
+
+
+
+ 10
+ 10
+ 171
+ 100
+
+
+
+
+ -1
+
+ -
+
+
+ Open Template
+
+
+
+ -
+
+
+ Open Target
+
+
+
+ -
+
+
+ Exit
+
+
+
+
+
+
+
+
+ 10
+ 130
+ 321
+ 281
+
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 319
+ 279
+
+
+
+
+
+ 0
+ 0
+ 321
+ 281
+
+
+
+
+
+
+
+
+ 130
+ 110
+ 60
+ 16
+
+
+
+ Original
+
+
+
+
+
+ 130
+ 420
+ 60
+ 16
+
+
+
+ Target
+
+
+
+
+
+ 10
+ 440
+ 321
+ 281
+
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 319
+ 279
+
+
+
+
+
+ 0
+ 0
+ 321
+ 281
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/calligraphycomparisonGUI.py b/calligraphyEvaluationToolGUI.py
similarity index 99%
rename from calligraphycomparisonGUI.py
rename to calligraphyEvaluationToolGUI.py
index 4ba02fb..91aebd5 100644
--- a/calligraphycomparisonGUI.py
+++ b/calligraphyEvaluationToolGUI.py
@@ -12,7 +12,7 @@
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
-from calligraphycomparisonmainwindow import Ui_MainWindow
+from calligraphyEvaluationTool.calligraphycomparisonmainwindow import Ui_MainWindow
from utils.Functions import coverTwoImages, shiftImageWithMaxCR, addIntersectedFig, addSquaredFig, resizeImages, \
calculateCoverageRate, rgb2qimage, getCenterOfGravity, getConvexHullOfImage, \
calculatePolygonArea, calculateValidPixelsArea, getSingleMaxBoundingBoxOfImage, \
diff --git a/calligraphySmoothingTool/__init__.py b/calligraphySmoothingTool/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/calligraphySmoothingTool/smoothcontourmainview.py b/calligraphySmoothingTool/smoothcontourmainview.py
new file mode 100644
index 0000000..8c67d95
--- /dev/null
+++ b/calligraphySmoothingTool/smoothcontourmainview.py
@@ -0,0 +1,314 @@
+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 smoothcontourmainwindow import Ui_MainWindow
+from utils.Functions import getContourOfImage, getNumberOfValidPixels, sortPointsOnContourOfImage, \
+ fitCurve, draw_cubic_bezier
+
+
+class SmoothContourMainWindow(QMainWindow, Ui_MainWindow):
+ def __init__(self):
+ super(SmoothContourMainWindow, self).__init__()
+ self.setupUi(self)
+
+ # original image scene
+ self.original_img_scene = QGraphicsScene()
+ self.original_img_gview.setScene(self.original_img_scene)
+
+ # original contour scene
+ self.original_contour_scene = QGraphicsScene()
+ self.original_contour_gview.setScene(self.original_contour_scene)
+
+ # smooth image scene
+ self.smooth_img_scene = QGraphicsScene()
+ self.smooth_img_gview.setScene(self.smooth_img_scene)
+
+ # smooth contour scene
+ self.smooth_contour_scene = QGraphicsScene()
+ self.smooth_contour_gview.setScene(self.smooth_contour_scene)
+
+ # add listen adapter
+ self.import_btn.clicked.connect(self.importBtn)
+ self.smooth_btn.clicked.connect(self.smoothBtn)
+ self.save_btn.clicked.connect(self.saveBtn)
+ self.exit_btn.clicked.connect(self.exitBtn)
+
+ # image path
+ self.image_path = ""
+ self.image_name = ""
+
+ self.image_gray = None
+ self.image_contour = None
+ self.image_contour_rgb = None
+
+ def importBtn(self):
+ """
+ Impprt image file
+ :return:
+ """
+ print("Import button clicked!")
+ # clean all views
+ self.original_img_scene.clear()
+ self.original_contour_scene.clear()
+ self.smooth_img_scene.clear()
+ self.smooth_contour_scene.clear()
+
+ # open image file
+ filename, _ = QFileDialog.getOpenFileName(None, "Open file", QDir.currentPath())
+ if filename:
+ # image file path
+ self.image_path = filename
+ self.image_name = os.path.splitext(os.path.basename(filename))[0]
+
+ qimage = QImage(filename)
+ if qimage.isNull():
+ QMessageBox.information(self, "Image viewer", "Cannot load %s." % filename)
+ return
+
+ # grayscale image
+ img_ = cv2.imread(filename, 0)
+ _, img_ = cv2.threshold(img_, 127, 255, cv2.THRESH_BINARY)
+
+ # grayscale image
+ self.image_gray = img_.copy()
+
+ # contour image
+ contour = getContourOfImage(img_)
+ contour = np.array(contour, dtype=np.uint8)
+ self.image_contour = contour.copy()
+ contour_rgb = cv2.cvtColor(contour, cv2.COLOR_GRAY2RGB)
+ self.image_contour_rgb = contour_rgb.copy()
+
+ # show on the views
+ image_pix = QPixmap.fromImage(qimage)
+ self.original_img_scene.addPixmap(image_pix)
+
+ qcontour = QImage(contour.data, contour.shape[1], contour.shape[0], contour.shape[1], \
+ QImage.Format_Indexed8)
+ contour_pix = QPixmap.fromImage(qcontour)
+ self.original_contour_scene.addPixmap(contour_pix)
+
+ self.original_img_scene.update()
+ self.original_contour_scene.update()
+
+ def smoothBtn(self):
+ """
+ Smooth contour.
+ :return:
+ """
+ self.smooth_img_scene.clear()
+ self.smooth_contour_scene.clear()
+ print("Smooth button clicked")
+ numOfFeaturePoints = 0
+ maxError = 0.
+
+ try:
+ numOfFeaturePoints = int(self.featurepts_ledit.text())
+ maxError = int(self.maxerror_ledit.text())
+ except:
+ print("Input error")
+ return
+ # smooth
+ contour_gray = self.image_contour.copy()
+ contour_rgb = self.image_contour_rgb.copy()
+
+ # fix breaking points on the contour
+ break_points = []
+ for y in range(1, contour_gray.shape[0] - 1):
+ for x in range(1, contour_gray.shape[1] - 1):
+ if contour_gray[y][x] == 0.0:
+ num_ = getNumberOfValidPixels(contour_gray, x, y)
+ if num_ == 1:
+ print((x, y))
+ break_points.append((x, y))
+ if len(break_points) != 0:
+ contour_gray = cv2.line(contour_gray, break_points[0], break_points[1], color=0, thickness=1)
+
+ # sort the points on the contour
+ contour_points_ordered = sortPointsOnContourOfImage(contour_gray)
+
+ # get the feature points on contour
+ corners = cv2.goodFeaturesToTrack(contour_gray, numOfFeaturePoints, 0.01, 10)
+ corners = np.int0(corners)
+
+ # keep the corner points should be all points on the contour
+ corner_points_ = []
+ for i in corners:
+ MAX_DIST = 10000
+ x, y = i.ravel()
+ pt_ = None
+ if (x, y - 1) in contour_points_ordered:
+ pt_ = (x, y - 1)
+ elif (x + 1, y - 1) in contour_points_ordered:
+ pt_ = (x + 1, y - 1)
+ elif (x + 1, y) in contour_points_ordered:
+ pt_ = (x + 1, y)
+ elif (x + 1, y + 1) in contour_points_ordered:
+ pt_ = (x + 1, y + 1)
+ elif (x, y + 1) in contour_points_ordered:
+ pt_ = (x, y + 1)
+ elif (x - 1, y + 1) in contour_points_ordered:
+ pt_ = (x - 1, y + 1)
+ elif (x - 1, y) in contour_points_ordered:
+ pt_ = (x - 1, y)
+ elif (x - 1, y - 1) in contour_points_ordered:
+ pt_ = (x - 1, y - 1)
+ else:
+ # find the nearest point on the contour
+ minx = 0
+ miny = 0
+ for cp in contour_points_ordered:
+ dist = math.sqrt((x - cp[0]) ** 2 + (y - cp[1]) ** 2)
+ if dist < MAX_DIST:
+ MAX_DIST = dist
+ minx = cp[0]
+ miny = cp[1]
+ pt_ = (minx, miny)
+ corner_points_.append(pt_)
+ # sorted the corner points with clockwise direction
+ corner_points = []
+ index = 0
+ for pt in contour_points_ordered:
+ if pt in corner_points_:
+ corner_points.append(pt)
+ cv2.circle(contour_rgb, (pt[0], pt[1]), 3, (255, 0, 0), -1)
+ cv2.putText(contour_rgb, str(index), (pt[0], pt[1]), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2,
+ cv2.LINE_AA)
+ index += 1
+
+ # contour segemetations based on the corner points
+ contour_lines = []
+ for id in range(len(corner_points)):
+ start_point = corner_points[id]
+ end_point = start_point
+ if id == len(corner_points) - 1:
+ end_point = corner_points[0]
+ else:
+ end_point = corner_points[id + 1]
+ # contour segmentation
+ contour_segmentation = []
+ start_index = contour_points_ordered.index(start_point)
+ end_index = contour_points_ordered.index(end_point)
+
+ if start_index <= end_index:
+ # normal index
+ contour_segmentation = contour_points_ordered[start_index: end_index + 1]
+ else:
+ # end is at
+ contour_segmentation = contour_points_ordered[start_index: len(contour_points_ordered)] + \
+ contour_points_ordered[0: end_index + 1]
+ contour_lines.append(contour_segmentation)
+
+ # using different color to display the contour segmentations
+ for id in range(len(contour_lines)):
+ if id % 3 == 0:
+ # red lines
+ for pt in contour_lines[id]:
+ contour_rgb[pt[1]][pt[0]] = (0, 0, 255)
+ elif id % 3 == 1:
+ # blue line
+ for pt in contour_lines[id]:
+ contour_rgb[pt[1]][pt[0]] = (255, 0, 0)
+ elif id % 3 == 2:
+ # green line
+ for pt in contour_lines[id]:
+ contour_rgb[pt[1]][pt[0]] = (0, 255, 0)
+ contour_rgb = np.array(contour_rgb)
+ print(contour_rgb.shape)
+ qoriginal_contour_rgb = rgb2qimage(contour_rgb)
+ # qoriginal_contour_rgb = QImage(contour_rgb.data, contour_rgb.shape[1], contour_rgb.shape[0], contour_rgb.shape[1]*3, \
+ # QImage.Format_RGB32)
+ qorignal_contour_rgb_pix = QPixmap.fromImage(qoriginal_contour_rgb)
+ self.smooth_img_scene.addPixmap(qorignal_contour_rgb_pix)
+ self.smooth_img_scene.update()
+
+ # smooth segmentations
+ smoothed_contour_points = []
+ for id in range(len(contour_lines)):
+
+ # smooth contour segmentation
+ li_points = np.array(contour_lines[id])
+
+ beziers = fitCurve(li_points, maxError=30)
+ print("len bezier: %d" % len(beziers))
+ # # print(beziers)
+ for bez in beziers:
+ print(len(bez))
+ bezier_points = draw_cubic_bezier(bez[0], bez[1], bez[2], bez[3])
+ smoothed_contour_points += bezier_points
+
+ # fill the smooth contour region
+ smoothed_contour_points = np.array([smoothed_contour_points], "int32")
+ fill_smooth_contour = np.ones(self.image_gray.shape) * 255
+ fill_smooth_contour = np.array(fill_smooth_contour, dtype=np.uint8)
+ fill_smooth_contour = cv2.fillPoly(fill_smooth_contour, smoothed_contour_points, 0)
+
+ qfill_smooth_contour = QImage(fill_smooth_contour.data, fill_smooth_contour.shape[1], fill_smooth_contour.shape[0], fill_smooth_contour.shape[1], \
+ QImage.Format_Indexed8)
+ qfill_smooth_contour_pix = QPixmap.fromImage(qfill_smooth_contour)
+ self.smooth_contour_scene.addPixmap(qfill_smooth_contour_pix)
+ self.smooth_contour_scene.update()
+
+
+
+ def saveBtn(self):
+ """
+ Save smooth contour images
+ :return:
+ """
+ print("Save button clicked")
+
+ def exitBtn(self):
+ """
+ Exit button clicked.
+ :return:
+ """
+ qApp = QApplication.instance()
+ sys.exit(qApp.exec_())
+
+
+def rgb2qimage(rgb):
+ """Convert the 3D numpy array `rgb` into a 32-bit QImage. `rgb` must
+ have three dimensions with the vertical, horizontal and RGB image axes.
+
+ ATTENTION: This QImage carries an attribute `ndimage` with a
+ reference to the underlying numpy array that holds the data. On
+ Windows, the conversion into a QPixmap does not copy the data, so
+ that you have to take care that the QImage does not get garbage
+ collected (otherwise PyQt will throw away the wrapper, effectively
+ freeing the underlying memory - boom!)."""
+ if len(rgb.shape) != 3:
+ raise ValueError("rgb2QImage can only convert 3D arrays")
+ if rgb.shape[2] not in (3, 4):
+ raise ValueError(
+ "rgb2QImage can expects the last dimension to contain exactly three (R,G,B) or four (R,G,B,A) channels")
+
+ h, w, channels = rgb.shape
+
+ # Qt expects 32bit BGRA data for color images:
+ bgra = np.empty((h, w, 4), np.uint8, 'C')
+ bgra[..., 0] = rgb[..., 2]
+ bgra[..., 1] = rgb[..., 1]
+ bgra[..., 2] = rgb[..., 0]
+ if rgb.shape[2] == 3:
+ bgra[..., 3].fill(255)
+ fmt = QImage.Format_RGB32
+ else:
+ bgra[..., 3] = rgb[..., 3]
+ fmt = QImage.Format_ARGB32
+
+ result = QImage(bgra.data, w, h, fmt)
+ result.ndarray = bgra
+ return result
+
+if __name__ == '__main__':
+ app = QApplication(sys.argv)
+ mainWindow = SmoothContourMainWindow()
+ mainWindow.show()
+ sys.exit(app.exec_())
\ No newline at end of file
diff --git a/calligraphySmoothingTool/smoothcontourmainwindow.py b/calligraphySmoothingTool/smoothcontourmainwindow.py
new file mode 100644
index 0000000..deeb340
--- /dev/null
+++ b/calligraphySmoothingTool/smoothcontourmainwindow.py
@@ -0,0 +1,146 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'smoothcontourmainwindow.ui'
+#
+# Created by: PyQt5 UI code generator 5.10.1
+#
+# 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(1056, 905)
+ self.centralwidget = QtWidgets.QWidget(MainWindow)
+ self.centralwidget.setObjectName("centralwidget")
+ self.scrollArea = QtWidgets.QScrollArea(self.centralwidget)
+ self.scrollArea.setGeometry(QtCore.QRect(200, 0, 400, 400))
+ self.scrollArea.setWidgetResizable(True)
+ self.scrollArea.setObjectName("scrollArea")
+ self.scrollAreaWidgetContents = QtWidgets.QWidget()
+ self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 398, 398))
+ self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
+ self.original_img_gview = QtWidgets.QGraphicsView(self.scrollAreaWidgetContents)
+ self.original_img_gview.setGeometry(QtCore.QRect(0, 0, 400, 400))
+ self.original_img_gview.setObjectName("original_img_gview")
+ self.scrollArea.setWidget(self.scrollAreaWidgetContents)
+ self.scrollArea_2 = QtWidgets.QScrollArea(self.centralwidget)
+ self.scrollArea_2.setGeometry(QtCore.QRect(630, 0, 400, 400))
+ self.scrollArea_2.setWidgetResizable(True)
+ self.scrollArea_2.setObjectName("scrollArea_2")
+ self.scrollAreaWidgetContents_2 = QtWidgets.QWidget()
+ self.scrollAreaWidgetContents_2.setGeometry(QtCore.QRect(0, 0, 398, 398))
+ self.scrollAreaWidgetContents_2.setObjectName("scrollAreaWidgetContents_2")
+ self.smooth_img_gview = QtWidgets.QGraphicsView(self.scrollAreaWidgetContents_2)
+ self.smooth_img_gview.setGeometry(QtCore.QRect(0, 0, 400, 400))
+ self.smooth_img_gview.setObjectName("smooth_img_gview")
+ self.scrollArea_2.setWidget(self.scrollAreaWidgetContents_2)
+ self.scrollArea_3 = QtWidgets.QScrollArea(self.centralwidget)
+ self.scrollArea_3.setGeometry(QtCore.QRect(200, 430, 400, 400))
+ self.scrollArea_3.setWidgetResizable(True)
+ self.scrollArea_3.setObjectName("scrollArea_3")
+ self.scrollAreaWidgetContents_3 = QtWidgets.QWidget()
+ self.scrollAreaWidgetContents_3.setGeometry(QtCore.QRect(0, 0, 398, 398))
+ self.scrollAreaWidgetContents_3.setObjectName("scrollAreaWidgetContents_3")
+ self.original_contour_gview = QtWidgets.QGraphicsView(self.scrollAreaWidgetContents_3)
+ self.original_contour_gview.setGeometry(QtCore.QRect(0, 0, 400, 400))
+ self.original_contour_gview.setObjectName("original_contour_gview")
+ self.scrollArea_3.setWidget(self.scrollAreaWidgetContents_3)
+ self.scrollArea_4 = QtWidgets.QScrollArea(self.centralwidget)
+ self.scrollArea_4.setGeometry(QtCore.QRect(630, 430, 400, 400))
+ self.scrollArea_4.setWidgetResizable(True)
+ self.scrollArea_4.setObjectName("scrollArea_4")
+ self.scrollAreaWidgetContents_4 = QtWidgets.QWidget()
+ self.scrollAreaWidgetContents_4.setGeometry(QtCore.QRect(0, 0, 398, 398))
+ self.scrollAreaWidgetContents_4.setObjectName("scrollAreaWidgetContents_4")
+ self.smooth_contour_gview = QtWidgets.QGraphicsView(self.scrollAreaWidgetContents_4)
+ self.smooth_contour_gview.setGeometry(QtCore.QRect(0, 0, 400, 400))
+ self.smooth_contour_gview.setObjectName("smooth_contour_gview")
+ self.scrollArea_4.setWidget(self.scrollAreaWidgetContents_4)
+ self.label_2 = QtWidgets.QLabel(self.centralwidget)
+ self.label_2.setGeometry(QtCore.QRect(330, 840, 101, 16))
+ self.label_2.setObjectName("label_2")
+ self.label_3 = QtWidgets.QLabel(self.centralwidget)
+ self.label_3.setGeometry(QtCore.QRect(760, 400, 101, 16))
+ self.label_3.setObjectName("label_3")
+ self.label_4 = QtWidgets.QLabel(self.centralwidget)
+ self.label_4.setGeometry(QtCore.QRect(780, 830, 91, 16))
+ self.label_4.setObjectName("label_4")
+ self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
+ self.verticalLayoutWidget.setGeometry(QtCore.QRect(10, 20, 181, 81))
+ self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
+ self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
+ self.verticalLayout.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.import_btn = QtWidgets.QPushButton(self.verticalLayoutWidget)
+ self.import_btn.setObjectName("import_btn")
+ self.verticalLayout.addWidget(self.import_btn)
+ self.exit_btn = QtWidgets.QPushButton(self.verticalLayoutWidget)
+ self.exit_btn.setObjectName("exit_btn")
+ self.verticalLayout.addWidget(self.exit_btn)
+ self.verticalLayoutWidget_2 = QtWidgets.QWidget(self.centralwidget)
+ self.verticalLayoutWidget_2.setGeometry(QtCore.QRect(10, 110, 181, 121))
+ self.verticalLayoutWidget_2.setObjectName("verticalLayoutWidget_2")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_2)
+ self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ self.label_5 = QtWidgets.QLabel(self.verticalLayoutWidget_2)
+ self.label_5.setObjectName("label_5")
+ self.horizontalLayout_2.addWidget(self.label_5)
+ self.featurepts_ledit = QtWidgets.QLineEdit(self.verticalLayoutWidget_2)
+ self.featurepts_ledit.setObjectName("featurepts_ledit")
+ self.horizontalLayout_2.addWidget(self.featurepts_ledit)
+ self.verticalLayout_2.addLayout(self.horizontalLayout_2)
+ self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_4.setObjectName("horizontalLayout_4")
+ self.label_6 = QtWidgets.QLabel(self.verticalLayoutWidget_2)
+ self.label_6.setObjectName("label_6")
+ self.horizontalLayout_4.addWidget(self.label_6)
+ self.maxerror_ledit = QtWidgets.QLineEdit(self.verticalLayoutWidget_2)
+ self.maxerror_ledit.setObjectName("maxerror_ledit")
+ self.horizontalLayout_4.addWidget(self.maxerror_ledit)
+ self.verticalLayout_2.addLayout(self.horizontalLayout_4)
+ self.verticalLayoutWidget_3 = QtWidgets.QWidget(self.centralwidget)
+ self.verticalLayoutWidget_3.setGeometry(QtCore.QRect(10, 250, 181, 91))
+ self.verticalLayoutWidget_3.setObjectName("verticalLayoutWidget_3")
+ self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_3)
+ self.verticalLayout_3.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_3.setObjectName("verticalLayout_3")
+ self.smooth_btn = QtWidgets.QPushButton(self.verticalLayoutWidget_3)
+ self.smooth_btn.setObjectName("smooth_btn")
+ self.verticalLayout_3.addWidget(self.smooth_btn)
+ self.save_btn = QtWidgets.QPushButton(self.verticalLayoutWidget_3)
+ self.save_btn.setObjectName("save_btn")
+ self.verticalLayout_3.addWidget(self.save_btn)
+ self.label = QtWidgets.QLabel(self.centralwidget)
+ self.label.setGeometry(QtCore.QRect(360, 400, 91, 16))
+ self.label.setObjectName("label")
+ MainWindow.setCentralWidget(self.centralwidget)
+ self.menubar = QtWidgets.QMenuBar(MainWindow)
+ self.menubar.setGeometry(QtCore.QRect(0, 0, 1056, 22))
+ self.menubar.setObjectName("menubar")
+ MainWindow.setMenuBar(self.menubar)
+ self.statusbar = QtWidgets.QStatusBar(MainWindow)
+ self.statusbar.setObjectName("statusbar")
+ MainWindow.setStatusBar(self.statusbar)
+
+ self.retranslateUi(MainWindow)
+ QtCore.QMetaObject.connectSlotsByName(MainWindow)
+
+ def retranslateUi(self, MainWindow):
+ _translate = QtCore.QCoreApplication.translate
+ MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
+ self.label_2.setText(_translate("MainWindow", "Original Contour"))
+ self.label_3.setText(_translate("MainWindow", "Smooth Contour"))
+ self.label_4.setText(_translate("MainWindow", "Smooth"))
+ self.import_btn.setText(_translate("MainWindow", "Import"))
+ self.exit_btn.setText(_translate("MainWindow", "Exit"))
+ self.label_5.setText(_translate("MainWindow", "Feature Points:"))
+ self.label_6.setText(_translate("MainWindow", "Max Error:"))
+ self.smooth_btn.setText(_translate("MainWindow", "Smooth"))
+ self.save_btn.setText(_translate("MainWindow", "Save"))
+ self.label.setText(_translate("MainWindow", "Original "))
+
diff --git a/calligraphySmoothingTool/smoothcontourmainwindow.ui b/calligraphySmoothingTool/smoothcontourmainwindow.ui
new file mode 100644
index 0000000..5be1484
--- /dev/null
+++ b/calligraphySmoothingTool/smoothcontourmainwindow.ui
@@ -0,0 +1,308 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 1056
+ 905
+
+
+
+ MainWindow
+
+
+
+
+
+ 200
+ 0
+ 400
+ 400
+
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 398
+ 398
+
+
+
+
+
+ 0
+ 0
+ 400
+ 400
+
+
+
+
+
+
+
+
+ 630
+ 0
+ 400
+ 400
+
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 398
+ 398
+
+
+
+
+
+ 0
+ 0
+ 400
+ 400
+
+
+
+
+
+
+
+
+ 200
+ 430
+ 400
+ 400
+
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 398
+ 398
+
+
+
+
+
+ 0
+ 0
+ 400
+ 400
+
+
+
+
+
+
+
+
+ 630
+ 430
+ 400
+ 400
+
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 398
+ 398
+
+
+
+
+
+ 0
+ 0
+ 400
+ 400
+
+
+
+
+
+
+
+
+ 330
+ 840
+ 101
+ 16
+
+
+
+ Original Contour
+
+
+
+
+
+ 760
+ 400
+ 101
+ 16
+
+
+
+ Smooth Contour
+
+
+
+
+
+ 780
+ 830
+ 91
+ 16
+
+
+
+ Smooth
+
+
+
+
+
+ 10
+ 20
+ 181
+ 81
+
+
+
+ -
+
+
+ Import
+
+
+
+ -
+
+
+ Exit
+
+
+
+
+
+
+
+
+ 10
+ 110
+ 181
+ 121
+
+
+
+ -
+
+
-
+
+
+ Feature Points:
+
+
+
+ -
+
+
+
+
+ -
+
+
-
+
+
+ Max Error:
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+ 10
+ 250
+ 181
+ 91
+
+
+
+ -
+
+
+ Smooth
+
+
+
+ -
+
+
+ Save
+
+
+
+
+
+
+
+
+ 360
+ 400
+ 91
+ 16
+
+
+
+ Original
+
+
+
+
+
+
+
+
+
diff --git a/smoothmanuallyGUI.py b/calligraphySmoothingTool/smoothmanuallyGUI.py
similarity index 100%
rename from smoothmanuallyGUI.py
rename to calligraphySmoothingTool/smoothmanuallyGUI.py
diff --git a/calligraphySmoothingTool/smoothmanuallymainwindow.py b/calligraphySmoothingTool/smoothmanuallymainwindow.py
new file mode 100644
index 0000000..9fa04d2
--- /dev/null
+++ b/calligraphySmoothingTool/smoothmanuallymainwindow.py
@@ -0,0 +1,88 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'smoothmanuallymainwindow.ui'
+#
+# Created by: PyQt5 UI code generator 5.10.1
+#
+# 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.setObjectName("verticalLayoutWidget")
+ self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
+ self.verticalLayout.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.open_btn = QtWidgets.QPushButton(self.verticalLayoutWidget)
+ self.open_btn.setObjectName("open_btn")
+ self.verticalLayout.addWidget(self.open_btn)
+ self.clear_btn = QtWidgets.QPushButton(self.verticalLayoutWidget)
+ self.clear_btn.setObjectName("clear_btn")
+ self.verticalLayout.addWidget(self.clear_btn)
+ self.contour_btn = QtWidgets.QPushButton(self.verticalLayoutWidget)
+ self.contour_btn.setObjectName("contour_btn")
+ self.verticalLayout.addWidget(self.contour_btn)
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.label = QtWidgets.QLabel(self.verticalLayoutWidget)
+ self.label.setObjectName("label")
+ self.horizontalLayout.addWidget(self.label)
+ 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.smooth_btn = QtWidgets.QPushButton(self.verticalLayoutWidget)
+ self.smooth_btn.setObjectName("smooth_btn")
+ self.verticalLayout.addWidget(self.smooth_btn)
+ self.autosmooth_btn = QtWidgets.QPushButton(self.verticalLayoutWidget)
+ self.autosmooth_btn.setObjectName("autosmooth_btn")
+ self.verticalLayout.addWidget(self.autosmooth_btn)
+ self.save_btn = QtWidgets.QPushButton(self.verticalLayoutWidget)
+ self.save_btn.setObjectName("save_btn")
+ self.verticalLayout.addWidget(self.save_btn)
+ self.exit_btn = QtWidgets.QPushButton(self.verticalLayoutWidget)
+ self.exit_btn.setObjectName("exit_btn")
+ self.verticalLayout.addWidget(self.exit_btn)
+ self.scrollArea = QtWidgets.QScrollArea(self.centralwidget)
+ self.scrollArea.setGeometry(QtCore.QRect(220, 10, 801, 611))
+ self.scrollArea.setWidgetResizable(True)
+ self.scrollArea.setObjectName("scrollArea")
+ self.scrollAreaWidgetContents = QtWidgets.QWidget()
+ self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 799, 609))
+ self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
+ self.contour_gview = QtWidgets.QGraphicsView(self.scrollAreaWidgetContents)
+ self.contour_gview.setGeometry(QtCore.QRect(0, 0, 801, 611))
+ self.contour_gview.setObjectName("contour_gview")
+ self.scrollArea.setWidget(self.scrollAreaWidgetContents)
+ MainWindow.setCentralWidget(self.centralwidget)
+ self.menubar = QtWidgets.QMenuBar(MainWindow)
+ self.menubar.setGeometry(QtCore.QRect(0, 0, 1030, 22))
+ self.menubar.setObjectName("menubar")
+ MainWindow.setMenuBar(self.menubar)
+ self.statusbar = QtWidgets.QStatusBar(MainWindow)
+ self.statusbar.setObjectName("statusbar")
+ MainWindow.setStatusBar(self.statusbar)
+
+ self.retranslateUi(MainWindow)
+ QtCore.QMetaObject.connectSlotsByName(MainWindow)
+
+ def retranslateUi(self, MainWindow):
+ _translate = QtCore.QCoreApplication.translate
+ MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
+ self.open_btn.setText(_translate("MainWindow", "Open"))
+ self.clear_btn.setText(_translate("MainWindow", "Clear"))
+ self.contour_btn.setText(_translate("MainWindow", "Contour"))
+ self.label.setText(_translate("MainWindow", "Max Error:"))
+ self.maxerror_ledit.setPlaceholderText(_translate("MainWindow", "0"))
+ self.smooth_btn.setText(_translate("MainWindow", "Smooth"))
+ self.autosmooth_btn.setText(_translate("MainWindow", "Auto-Smooth"))
+ self.save_btn.setText(_translate("MainWindow", "Save"))
+ self.exit_btn.setText(_translate("MainWindow", "Exit"))
+
diff --git a/calligraphySmoothingTool/smoothmanuallymainwindow.ui b/calligraphySmoothingTool/smoothmanuallymainwindow.ui
new file mode 100644
index 0000000..51a43fc
--- /dev/null
+++ b/calligraphySmoothingTool/smoothmanuallymainwindow.ui
@@ -0,0 +1,144 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 1030
+ 681
+
+
+
+ MainWindow
+
+
+
+
+
+ 20
+ 10
+ 181
+ 321
+
+
+
+ -
+
+
+ Open
+
+
+
+ -
+
+
+ Clear
+
+
+
+ -
+
+
+ Contour
+
+
+
+ -
+
+
-
+
+
+ Max Error:
+
+
+
+ -
+
+
+ 0
+
+
+
+
+
+ -
+
+
+ Smooth
+
+
+
+ -
+
+
+ Auto-Smooth
+
+
+
+ -
+
+
+ Save
+
+
+
+ -
+
+
+ Exit
+
+
+
+
+
+
+
+
+ 220
+ 10
+ 801
+ 611
+
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 799
+ 609
+
+
+
+
+
+ 0
+ 0
+ 801
+ 611
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/calligraphySmoothingToolGUI.py b/calligraphySmoothingToolGUI.py
new file mode 100644
index 0000000..21c8bbe
--- /dev/null
+++ b/calligraphySmoothingToolGUI.py
@@ -0,0 +1,289 @@
+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 smoothmanuallymainwindow import Ui_MainWindow
+from utils.Functions import getContourOfImage, removeBreakPointsOfContour, getNumberOfValidPixels, \
+ sortPointsOnContourOfImage, fitCurve, draw_cubic_bezier
+
+
+class SmoothManuallyGUI(QMainWindow, Ui_MainWindow):
+ def __init__(self):
+ super(SmoothManuallyGUI, self).__init__()
+ self.setupUi(self)
+
+ # init GUI
+ self.scene = GraphicsScene()
+ self.scene.setBackgroundBrush(Qt.gray)
+ self.contour_gview.setScene(self.scene)
+
+ self.image_pix = QPixmap()
+ self.temp_image_pix = QPixmap()
+
+ self.contour_pix = QPixmap()
+ self.temp_contour_pix = QPixmap()
+
+ # data
+ self.image_path = ""
+ self.image_name = ""
+
+ self.feature_points = []
+ self.contour_segmentations = []
+ self.smoothed_contour_points = []
+
+ self.image_gray = None
+ self.contour_gray = None
+
+ # add listener
+ self.open_btn.clicked.connect(self.openBtn)
+ self.clear_btn.clicked.connect(self.clearBtn)
+ self.contour_btn.clicked.connect(self.contourBtn)
+ self.smooth_btn.clicked.connect(self.smoothBtn)
+ self.autosmooth_btn.clicked.connect(self.autoSmoothBtn)
+ self.save_btn.clicked.connect(self.saveBtn)
+ self.exit_btn.clicked.connect(self.exitBtn)
+
+ def openBtn(self):
+ """
+ Open button
+ :return:
+ """
+ print("Open button clicked!")
+ self.scene.clear()
+
+ filename, _ = QFileDialog.getOpenFileName(None, "Open file", QDir.currentPath())
+ if filename:
+ # image file path and name
+ self.image_path = filename
+ self.image_name = os.path.splitext(os.path.basename(filename))[0]
+
+ qimage = QImage(filename)
+ if qimage.isNull():
+ QMessageBox.information(self, "Image viewer", "Cannot not load %s." % filename)
+ return
+ # grayscale image
+ img_ = cv2.imread(filename, 0)
+ _, img_ = cv2.threshold(img_, 127, 255, cv2.THRESH_BINARY)
+ self.image_gray = img_.copy()
+
+ self.image_pix = QPixmap.fromImage(qimage)
+ self.temp_image_pix = self.image_pix.copy()
+ self.scene.addPixmap(self.image_pix)
+ self.scene.update()
+ self.statusbar.showMessage("Open image %s successed!" % self.image_name)
+
+ # clean
+ del img_, qimage
+
+ def clearBtn(self):
+ """
+ Clear button clicked
+ :return:
+ """
+ print("Clear button clicked")
+ # clean data
+ self.scene.points = []
+
+ self.scene.addPixmap(self.image_pix)
+ self.scene.update()
+ self.statusbar.showMessage("Clear successed!")
+
+ def contourBtn(self):
+ """
+ Obtain the contour of image
+ :return:
+ """
+ print("Contour button clicked")
+ contour_ = getContourOfImage(self.image_gray)
+
+ # remove the break points
+ contour_ = removeBreakPointsOfContour(contour_)
+
+ self.contour_gray = contour_.copy()
+ qimg = QImage(contour_.data, contour_.shape[1], contour_.shape[0], contour_.shape[1], QImage.Format_Indexed8)
+
+ self.contour_pix = QPixmap.fromImage(qimg)
+ self.temp_contour_pix = self.contour_pix.copy()
+ self.scene.addPixmap(self.contour_pix)
+ self.scene.update()
+ self.statusbar.showMessage("Contour successed!")
+ del contour_, qimg
+
+ def smoothBtn(self):
+ """
+ Smooth button clicked!
+ :return:
+ """
+ print("Smooth button clicked")
+ if self.scene.points is None or len(self.scene.points) == 0:
+ return
+ # max Error
+ max_error = int(self.maxerror_ledit.text())
+
+ # new contour image
+ contour_img = np.array(np.ones_like(self.contour_gray) * 255, dtype=np.uint8)
+
+ # smooth the contour segmentations.
+ contour_sorted = sortPointsOnContourOfImage(self.contour_gray.copy())
+
+ feature_points = []
+ for pt in self.scene.points:
+ nearest_pt = None
+ max_dist = 1000000
+ for cpt in contour_sorted:
+ dist_ = math.sqrt((pt[0]-cpt[0])**2 + (pt[1]-cpt[1])**2)
+ if dist_ < max_dist:
+ max_dist = dist_
+ nearest_pt = cpt
+ # select feature points
+ feature_points.append(nearest_pt)
+ # add first point as the last end point
+ feature_points.append(feature_points[0])
+ print(self.scene.points)
+ print(feature_points)
+
+ self.feature_points = feature_points.copy()
+
+ # extract segmentations based on the feature points
+ contour_segmentations = []
+ for id in range(len(feature_points)-1):
+ start_pt = feature_points[id]
+ end_pt = feature_points[id+1]
+
+ start_index = contour_sorted.index(start_pt)
+ end_index = contour_sorted.index(end_pt)
+
+ if start_index < end_index:
+ segmentation = contour_sorted[start_index: end_index]
+ if end_index == len(contour_sorted)-1:
+ segmentation.append(contour_sorted[0])
+ else:
+ segmentation.append(contour_sorted[end_index+1])
+ elif start_index >= end_index:
+ segmentation = contour_sorted[start_index: len(contour_sorted)] + contour_sorted[0: end_index+1]
+
+ contour_segmentations.append(segmentation)
+ print("contour segmentation len: %d" % len(contour_segmentations))
+ self.contour_segmentations = contour_segmentations.copy()
+
+ # smooth contour segmentations
+ smoothed_contour_points = []
+ for id in range(len(self.contour_segmentations)):
+ print("Line index: %d" % id)
+
+ # smooth contour segmentation
+ li_seg = np.array(self.contour_segmentations[id])
+
+ beziers = fitCurve(li_seg, maxError=max_error)
+
+ for bez in beziers:
+ bezier_points = draw_cubic_bezier(bez[0], bez[1], bez[2], bez[3])
+
+ for id in range(len(bezier_points) - 1):
+ start_pt = bezier_points[id]
+ end_pt = bezier_points[id+1]
+ cv2.line(contour_img, start_pt, end_pt, color=0, thickness=1)
+ smoothed_contour_points += bezier_points
+
+ qimg = QImage(contour_img.data, contour_img.shape[1], contour_img.shape[0], contour_img.shape[1], \
+ QImage.Format_Indexed8)
+
+ contour_pix = QPixmap.fromImage(qimg)
+
+ self.scene.addPixmap(contour_pix)
+ self.scene.update()
+
+ # clean data
+ self.scene.points = []
+ self.contour_gray = contour_img.copy()
+ self.smoothed_contour_points = smoothed_contour_points.copy()
+
+ # update status bar
+ self.statusbar.showMessage("Smooth successed!")
+ del contour_img, contour_pix, contour_segmentations, contour_sorted, smoothed_contour_points, \
+ qimg, feature_points
+
+ def autoSmoothBtn(self):
+ """
+ Auto-smooth button clicked
+ :return:
+ """
+ print("Auto-smooth button clicked")
+
+ def saveBtn(self):
+ """
+ Save button
+ :return:
+ """
+ print("Save button clicked")
+
+ # contour image
+ contour_img = self.contour_gray.copy()
+
+ # fill the contour with black color
+ smoothed_contour_points = self.smoothed_contour_points.copy()
+ smoothed_contour_points = np.array([smoothed_contour_points], "int32")
+
+ fill_contour_smooth = np.ones(contour_img.shape) * 255
+ fill_contour_smooth = np.array(fill_contour_smooth, dtype=np.uint8)
+ fill_contour_smooth = cv2.fillPoly(fill_contour_smooth, smoothed_contour_points, 0)
+
+ # save path
+ fileName, _ = QFileDialog.getSaveFileName(self, "save file", QDir.currentPath())
+ cv2.imwrite(fileName, fill_contour_smooth)
+
+ self.statusbar.showMessage("Save image successed!")
+
+ del contour_img
+ del smoothed_contour_points
+ del fill_contour_smooth
+
+ def exitBtn(self):
+ """
+ Exit
+ :return:
+ """
+ qApp = QApplication.instance()
+ sys.exit(qApp.exec_())
+
+
+class GraphicsScene(QGraphicsScene):
+ def __init__(self, parent=None):
+ QGraphicsScene.__init__(self, parent)
+
+ # usually the points are sorted by user.
+ self.points = []
+
+ def setOption(self, opt):
+ self.opt = opt
+
+ def mousePressEvent(self, event):
+ """
+ Mouse press clicked!
+ :param event:
+ :return:
+ """
+ pen = QPen(Qt.red)
+ brush = QBrush(Qt.red)
+
+ x = event.scenePos().x()
+ y = event.scenePos().y()
+
+ # add point
+ self.addEllipse(x, y, 3, 3, pen, brush)
+
+ self.points.append((x, y))
+
+
+
+
+if __name__ == '__main__':
+ app = QApplication(sys.argv)
+ mainWindow = SmoothManuallyGUI()
+ mainWindow.show()
+ sys.exit(app.exec_())
\ No newline at end of file
diff --git a/kaiFontDatasetGeneration/__init__.py b/kaiFontDatasetGeneration/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/kaiFontDatasetGeneration/characters_dict.txt b/kaiFontDatasetGeneration/characters_dict.txt
new file mode 100644
index 0000000..c947274
--- /dev/null
+++ b/kaiFontDatasetGeneration/characters_dict.txt
@@ -0,0 +1,6958 @@
+一
+丁
+七
+万
+丈
+三
+上
+下
+丌
+不
+与
+丐
+丑
+专
+且
+丕
+世
+丘
+丙
+业
+丛
+东
+丝
+丞
+丢
+两
+严
+丧
+丨
+个
+丫
+丬
+中
+丰
+串
+临
+丶
+丸
+丹
+为
+主
+丽
+举
+丿
+乃
+久
+乇
+么
+义
+之
+乌
+乍
+乎
+乏
+乐
+乒
+乓
+乔
+乖
+乘
+乙
+乜
+九
+乞
+也
+习
+乡
+书
+乩
+买
+乱
+乳
+乾
+了
+予
+争
+事
+二
+亍
+于
+亏
+云
+互
+亓
+五
+井
+亘
+亚
+些
+亟
+亠
+亡
+亢
+交
+亥
+亦
+产
+亨
+亩
+享
+京
+亭
+亮
+亲
+毫
+亵
+人
+亻
+亿
+什
+仁
+仂
+仃
+仄
+仅
+仆
+仇
+仉
+今
+介
+仍
+从
+仑
+仓
+仔
+仕
+他
+仗
+付
+仙
+仝
+仞
+仟
+仡
+代
+令
+以
+仨
+仪
+仫
+们
+仰
+仲
+仳
+仵
+件
+价
+任
+份
+仿
+企
+伉
+伊
+伍
+伎
+伏
+伐
+休
+众
+优
+伙
+会
+伛
+伞
+伟
+传
+伢
+伤
+伥
+伦
+伧
+伪
+伫
+伯
+估
+伲
+伴
+伶
+伸
+伺
+似
+伽
+佃
+但
+位
+低
+住
+佐
+佑
+体
+何
+佗
+佘
+余
+佚
+佛
+作
+佝
+佞
+佟
+你
+佣
+佤
+佥
+佧
+佩
+佬
+佯
+佰
+佳
+佴
+佶
+佻
+佼
+佾
+使
+侃
+侄
+侈
+侉
+例
+侍
+侏
+侑
+侔
+侗
+供
+依
+侠
+侣
+侥
+侦
+侧
+侨
+侩
+侪
+侬
+侮
+侯
+侵
+便
+促
+俄
+俅
+俊
+俎
+俏
+俐
+俑
+俗
+俘
+俚
+俜
+保
+俞
+俟
+信
+俣
+俦
+俨
+俩
+俪
+俭
+修
+俯
+俱
+俳
+俸
+俺
+俾
+倌
+倍
+倏
+倒
+倔
+倘
+候
+倚
+倜
+借
+倡
+倥
+倦
+倨
+倩
+倪
+倬
+倭
+倮
+债
+值
+倾
+偃
+假
+偈
+偌
+偎
+偏
+偕
+做
+停
+健
+偬
+偶
+偷
+偻
+偾
+偿
+傀
+傅
+傈
+傍
+傣
+傥
+傧
+储
+傩
+催
+傲
+傺
+傻
+像
+僖
+僚
+僦
+僧
+僬
+僭
+僮
+僳
+僵
+僻
+儆
+儇
+儋
+儒
+儡
+儿
+兀
+允
+元
+兄
+充
+兆
+先
+光
+克
+免
+兑
+兔
+兕
+兖
+党
+兜
+兢
+入
+全
+八
+公
+六
+兮
+兰
+共
+关
+兴
+兵
+其
+具
+典
+兹
+养
+兼
+兽
+冀
+冁
+冂
+内
+冈
+冉
+册
+再
+冒
+冕
+冖
+冗
+写
+军
+农
+冠
+冢
+冤
+冥
+冫
+冬
+冯
+冰
+冱
+冲
+决
+况
+冶
+冷
+冻
+冼
+冽
+净
+凄
+准
+凇
+凉
+凋
+凌
+减
+凑
+凛
+凝
+几
+凡
+凤
+凫
+凭
+凯
+凰
+凳
+凵
+凶
+凸
+凹
+出
+击
+凼
+函
+凿
+刀
+刁
+刂
+刃
+分
+切
+刈
+刊
+刍
+刎
+刑
+划
+刖
+列
+刘
+则
+刚
+创
+初
+删
+判
+刨
+利
+别
+刭
+刮
+到
+刳
+制
+刷
+券
+刹
+刺
+刻
+刽
+刿
+剀
+剁
+剂
+剃
+削
+剌
+前
+剐
+剑
+剔
+剖
+剜
+剞
+剡
+剥
+剧
+剩
+剪
+副
+割
+剽
+剿
+劁
+劂
+劈
+劐
+劓
+力
+劝
+办
+功
+加
+务
+劢
+劣
+动
+助
+努
+劫
+劬
+劭
+励
+劲
+劳
+劾
+势
+勃
+勇
+勉
+勋
+勐
+勒
+勖
+勘
+募
+勤
+勰
+勹
+勺
+勾
+勿
+匀
+包
+匆
+匈
+匍
+匏
+匐
+匕
+化
+北
+匙
+匚
+匝
+匠
+匡
+匣
+匦
+匪
+匮
+匹
+区
+医
+匾
+匿
+十
+千
+卅
+升
+午
+卉
+半
+华
+协
+卑
+卒
+卓
+单
+卖
+南
+博
+卜
+卞
+卟
+占
+卡
+卢
+卣
+卤
+卦
+卧
+卩
+卫
+卮
+卯
+印
+危
+即
+却
+卵
+卷
+卸
+卺
+卿
+厂
+厄
+厅
+历
+厉
+压
+厌
+厍
+厕
+厘
+厚
+厝
+原
+厢
+厣
+厥
+厦
+厨
+厩
+厮
+厶
+去
+县
+叁
+参
+又
+叉
+及
+友
+双
+反
+发
+叔
+取
+受
+变
+叙
+叛
+叟
+叠
+口
+古
+句
+另
+叨
+叩
+只
+叫
+召
+叭
+叮
+可
+台
+叱
+史
+右
+叵
+叶
+号
+司
+叹
+叻
+叼
+叽
+吁
+吃
+各
+吆
+合
+吉
+吊
+同
+名
+后
+吏
+吐
+向
+吒
+吓
+吕
+吖
+吗
+君
+吝
+吞
+吟
+吠
+吡
+吣
+否
+吧
+吨
+吩
+含
+听
+吭
+吮
+启
+吱
+吲
+吴
+吵
+吸
+吹
+吻
+吼
+吾
+呀
+呃
+呆
+呈
+告
+呋
+呐
+呒
+呓
+呔
+呕
+呖
+呗
+员
+呙
+呛
+呜
+呢
+呤
+呦
+周
+呱
+呲
+味
+呵
+呶
+呷
+呸
+呻
+呼
+命
+咀
+咂
+咄
+咆
+咋
+和
+咎
+咏
+咐
+咒
+咔
+咕
+咖
+咙
+咚
+咛
+咝
+咣
+咤
+咦
+咧
+咨
+咩
+咪
+咫
+咬
+咭
+咯
+咱
+咳
+咴
+咸
+咻
+咽
+咿
+哀
+品
+哂
+哄
+哆
+哇
+哈
+哉
+哌
+响
+哎
+哏
+哐
+哑
+哒
+哓
+哔
+哕
+哗
+哙
+哚
+哜
+哝
+哞
+哟
+哥
+哦
+哧
+哨
+哩
+哪
+哭
+哮
+哲
+哳
+哺
+哼
+哽
+哿
+唁
+唆
+唇
+唉
+唏
+唐
+唑
+唔
+唛
+唠
+唢
+唣
+唤
+唧
+唪
+唬
+售
+唯
+唰
+唱
+唳
+唷
+唼
+唾
+唿
+啁
+啃
+啄
+商
+啉
+啊
+啐
+啕
+啖
+啜
+啡
+啤
+啥
+啦
+啧
+啪
+啬
+啭
+啮
+啵
+啶
+啷
+啸
+啻
+啼
+啾
+喀
+喁
+喂
+喃
+善
+喇
+喈
+喉
+喊
+喋
+喏
+喑
+喔
+喘
+喙
+喜
+喝
+喟
+喧
+喱
+喳
+喵
+喷
+喹
+喻
+喽
+喾
+嗄
+嗅
+嗉
+嗌
+嗍
+嗑
+嗒
+嗓
+嗔
+嗖
+嗜
+嗝
+嗟
+嗡
+嗣
+嗤
+嗥
+嗦
+嗨
+嗪
+嗫
+嗬
+嗯
+嗲
+嗳
+嗵
+嗷
+嗽
+嗾
+嘀
+嘁
+嘈
+嘉
+嘌
+嘎
+嘏
+嘘
+嘛
+嘞
+嘟
+嘣
+嘤
+嘧
+嘬
+嘭
+嘱
+嘲
+嘴
+嘶
+嘹
+嘻
+嘿
+噌
+噍
+噎
+噔
+噗
+噘
+噙
+噜
+噢
+噤
+器
+噩
+噪
+噫
+噬
+噱
+噶
+噻
+噼
+嚅
+嚆
+嚎
+嚏
+嚓
+嚣
+嚯
+嚷
+嚼
+囊
+囔
+囗
+囚
+四
+囝
+回
+囟
+因
+囡
+团
+囤
+囫
+园
+困
+囱
+围
+囵
+囹
+固
+国
+图
+囿
+圃
+圄
+圆
+圈
+圉
+圊
+圜
+土
+圣
+在
+圩
+圪
+圬
+圭
+圮
+圯
+地
+圳
+圹
+场
+圻
+圾
+址
+坂
+均
+坊
+坌
+坍
+坎
+坏
+坐
+坑
+块
+坚
+坛
+坜
+坝
+坞
+坟
+坠
+坡
+坤
+坦
+坨
+坩
+坪
+坫
+坭
+坯
+坳
+坶
+坷
+坻
+坼
+垂
+垃
+垄
+垅
+垆
+型
+垌
+垒
+垓
+垛
+垠
+垡
+垢
+垣
+垤
+垦
+垧
+垩
+垫
+垭
+垮
+垲
+垴
+垸
+埂
+埃
+埋
+城
+埏
+埒
+埔
+埕
+埘
+埙
+埚
+埝
+域
+埠
+埤
+埭
+埯
+埴
+埸
+培
+基
+埽
+堀
+堂
+堆
+堇
+堋
+堍
+堑
+堕
+堙
+堞
+堠
+堡
+堤
+堪
+堰
+堵
+塄
+塌
+塍
+塑
+塔
+塘
+塞
+塥
+填
+塬
+塾
+墀
+墁
+境
+墅
+墉
+墑
+墓
+墙
+墚
+增
+墟
+墨
+墩
+墼
+壁
+壅
+壑
+壕
+壤
+士
+壬
+壮
+声
+壳
+壶
+壹
+夂
+处
+备
+复
+夏
+夔
+夕
+外
+夙
+多
+夜
+够
+夤
+夥
+大
+天
+太
+夫
+夭
+央
+夯
+失
+头
+夷
+夸
+夹
+夺
+夼
+奁
+奂
+奄
+奇
+奈
+奉
+奋
+奎
+奏
+契
+奔
+奕
+奖
+套
+奘
+奚
+奠
+奢
+奥
+女
+奴
+奶
+奸
+她
+好
+妁
+如
+妃
+妄
+妆
+妇
+妈
+妊
+妍
+妒
+妓
+妖
+妗
+妙
+妞
+妣
+妤
+妥
+妨
+妩
+妪
+妫
+妮
+妯
+妲
+妹
+妻
+妾
+姆
+姊
+始
+姐
+姑
+姒
+姓
+委
+姗
+姘
+姚
+姜
+姝
+姣
+姥
+姨
+姬
+姹
+姻
+姿
+威
+娃
+娄
+娅
+娆
+娇
+娈
+娉
+娌
+娑
+娓
+娘
+娜
+娟
+娠
+娣
+娥
+娩
+娱
+娲
+娴
+娶
+娼
+婀
+婆
+婉
+婊
+婕
+婚
+婢
+婧
+婪
+婴
+婵
+婶
+婷
+婺
+婿
+媒
+媚
+媛
+媪
+媲
+媳
+媵
+媸
+媾
+嫁
+嫂
+嫉
+嫌
+嫒
+嫔
+嫖
+嫘
+嫜
+嫠
+嫡
+嫣
+嫦
+嫩
+嫫
+嫱
+嬉
+嬖
+嬗
+嬲
+嬴
+嬷
+孀
+子
+孑
+孓
+孔
+孕
+字
+存
+孙
+孚
+孛
+孜
+孝
+孟
+孢
+季
+孤
+孥
+学
+孩
+孪
+孬
+孰
+孱
+孶
+孵
+孺
+孽
+宀
+宁
+它
+宄
+宅
+宇
+守
+安
+宋
+完
+宏
+宓
+宕
+宗
+官
+宙
+定
+宛
+宜
+宝
+实
+宠
+审
+客
+宣
+室
+宥
+宦
+宪
+宫
+宰
+害
+宴
+宵
+家
+宸
+容
+宽
+宾
+宿
+寂
+寄
+寅
+密
+寇
+富
+寐
+寒
+寓
+寝
+寞
+察
+寡
+寤
+寥
+寨
+寮
+寰
+寸
+对
+寺
+寻
+导
+寿
+封
+射
+将
+尉
+尊
+小
+少
+尔
+尕
+尖
+尘
+尚
+尜
+尝
+尢
+尤
+尥
+尧
+尬
+就
+尴
+尸
+尹
+尺
+尻
+尼
+尽
+尾
+尿
+局
+屁
+层
+居
+屈
+屉
+届
+屋
+屎
+屏
+屐
+屑
+展
+屙
+属
+屠
+屡
+屣
+履
+屦
+屮
+屯
+山
+屹
+屺
+屿
+岁
+岂
+岈
+岌
+岍
+岐
+岑
+岔
+岖
+岗
+岘
+岙
+岚
+岛
+岜
+岢
+岣
+岩
+岫
+岬
+岭
+岱
+岳
+岵
+岷
+岸
+岽
+岿
+峁
+峄
+峋
+峒
+峙
+峡
+峤
+峥
+峦
+峨
+峪
+峭
+峰
+峻
+崂
+崃
+崆
+崇
+崎
+崔
+崖
+崛
+崞
+崤
+崦
+崧
+崩
+崭
+崮
+崴
+崽
+崾
+嵇
+嵊
+嵋
+嵌
+嵘
+嵛
+嵝
+嵩
+嵫
+嵬
+嵯
+嵴
+嶂
+嶙
+嶝
+嶷
+巅
+巍
+巛
+川
+州
+巡
+巢
+工
+左
+巧
+巨
+巩
+巫
+差
+巯
+己
+已
+巳
+巴
+巷
+巽
+巾
+币
+市
+布
+帅
+帆
+师
+希
+帏
+帐
+帑
+帔
+帕
+帖
+帘
+帙
+帚
+帛
+帜
+帝
+带
+帧
+席
+帮
+帱
+帷
+常
+帻
+帼
+帽
+幂
+幄
+幅
+幌
+幔
+幕
+幛
+幞
+幡
+幢
+干
+平
+年
+并
+幸
+幺
+幻
+幼
+幽
+广
+庀
+庄
+庆
+庇
+床
+庋
+序
+庐
+庑
+库
+应
+底
+庖
+店
+庙
+庚
+府
+庞
+废
+庠
+庥
+度
+座
+庭
+庳
+庵
+庶
+康
+庸
+庹
+庾
+廉
+廊
+廑
+廒
+廓
+廖
+廛
+廨
+廪
+廴
+延
+廷
+建
+廾
+廿
+开
+弁
+异
+弃
+弄
+弈
+弊
+弋
+式
+弒
+弓
+引
+弗
+弘
+弛
+弟
+张
+弥
+弦
+弧
+弩
+弪
+弭
+弯
+弱
+弹
+强
+弼
+彀
+彐
+归
+当
+录
+彖
+彗
+彘
+彝
+彡
+形
+彤
+彦
+彩
+彪
+彬
+彭
+彰
+影
+彳
+彷
+役
+彻
+彼
+往
+征
+徂
+径
+待
+徇
+很
+徉
+徊
+律
+後
+徐
+徒
+徕
+得
+徘
+徙
+徜
+御
+徨
+循
+徭
+微
+徵
+德
+徼
+徽
+心
+忄
+必
+忆
+忉
+忌
+忍
+忏
+忐
+忑
+忒
+忖
+志
+忘
+忙
+忝
+忠
+忡
+忤
+忧
+忪
+快
+忭
+忮
+忱
+念
+忸
+忻
+忽
+忾
+忿
+怀
+态
+怂
+怃
+怄
+怅
+怆
+怊
+怍
+怎
+怏
+怒
+怔
+怕
+怖
+怛
+怜
+思
+怙
+怠
+怡
+急
+怦
+性
+怨
+怩
+怪
+怫
+怯
+怵
+怼
+怿
+总
+恁
+恂
+恃
+恋
+恍
+恐
+恒
+恕
+恙
+恚
+恝
+恢
+恣
+恤
+恧
+恨
+恩
+恪
+恫
+恬
+恭
+息
+恰
+恳
+恶
+恸
+恹
+恺
+恻
+恼
+恽
+恿
+悃
+悄
+悉
+悌
+悍
+悒
+悔
+悖
+悚
+悛
+悝
+悟
+悠
+患
+悦
+您
+悫
+悬
+悭
+悯
+悱
+悲
+悴
+悸
+悻
+悼
+情
+惆
+惊
+惋
+惑
+惕
+惘
+惚
+惜
+惝
+惟
+惠
+惦
+惧
+惨
+惩
+惫
+惬
+惭
+惮
+惯
+惰
+想
+惴
+惶
+惹
+惺
+愀
+愁
+愆
+愈
+愉
+愍
+愎
+意
+愕
+愚
+感
+愠
+愣
+愤
+愦
+愧
+愫
+愿
+慈
+慊
+慌
+慎
+慑
+慕
+慝
+慢
+慧
+慨
+慰
+慵
+慷
+憋
+憎
+憔
+憝
+憧
+憨
+憩
+憬
+憷
+憾
+懂
+懈
+懊
+懋
+懑
+懒
+懔
+懦
+懵
+懿
+戆
+戈
+戊
+戋
+戌
+戍
+戎
+戏
+成
+我
+戒
+戕
+或
+戗
+战
+戚
+戛
+戟
+戡
+戢
+戤
+戥
+截
+戬
+戮
+戳
+戴
+户
+戽
+戾
+房
+所
+扁
+扃
+扇
+扈
+扉
+手
+扌
+才
+扎
+扑
+扒
+打
+扔
+托
+扛
+扣
+扦
+执
+扩
+扪
+扫
+扬
+扭
+扮
+扯
+扰
+扳
+扶
+批
+扼
+找
+承
+技
+抄
+抉
+把
+抑
+抒
+抓
+投
+抖
+抗
+抚
+抛
+抟
+折
+抠
+抡
+抢
+护
+报
+抨
+披
+抬
+抱
+抵
+抹
+抻
+押
+抽
+抿
+拂
+拄
+担
+拆
+拇
+拈
+拉
+拊
+拌
+拍
+拎
+拐
+拒
+拓
+拔
+拖
+拗
+拘
+拙
+拚
+招
+拜
+拟
+拢
+拣
+拥
+拦
+拧
+拨
+择
+括
+拭
+拮
+拯
+拱
+拳
+拴
+拶
+拷
+拼
+拽
+拾
+拿
+持
+挂
+指
+挈
+按
+挎
+挑
+挖
+挚
+挛
+挝
+挞
+挟
+挠
+挡
+挢
+挣
+挤
+挥
+挨
+挪
+挫
+振
+挲
+挹
+挺
+挽
+捂
+捃
+捅
+捆
+捉
+捋
+捌
+捍
+捎
+捏
+捐
+捕
+捞
+损
+捡
+换
+捣
+捧
+捩
+捭
+据
+捱
+捶
+捷
+捺
+捻
+掀
+掂
+掇
+授
+掉
+掊
+掌
+掎
+掏
+掐
+排
+掖
+掘
+掠
+探
+掣
+接
+控
+推
+掩
+措
+掬
+掭
+掮
+掰
+掳
+掴
+掷
+掸
+掺
+掼
+掾
+揄
+揆
+揉
+揍
+揎
+描
+提
+插
+揖
+揞
+揠
+握
+揣
+揩
+揪
+揭
+揲
+援
+揶
+揸
+揽
+揿
+搀
+搁
+搂
+搅
+搋
+搌
+搏
+搐
+搓
+搔
+搛
+搜
+搞
+搠
+搡
+搦
+搪
+搬
+搭
+搴
+携
+搽
+搿
+摁
+摄
+摅
+摆
+摇
+摈
+摊
+摒
+摔
+摘
+摞
+摧
+摩
+摭
+摸
+摹
+摺
+撂
+撄
+撅
+撇
+撑
+撒
+撕
+撖
+撙
+撞
+撤
+撩
+撬
+播
+撮
+撰
+撵
+撷
+撸
+撺
+撼
+擀
+擂
+擅
+操
+擎
+擐
+擒
+擗
+擘
+擞
+擢
+擤
+擦
+攀
+攉
+攒
+攘
+攥
+攫
+攮
+支
+攴
+攵
+收
+攸
+改
+攻
+放
+政
+故
+效
+敉
+敌
+敏
+救
+敕
+敖
+教
+敛
+敝
+敞
+敢
+散
+敦
+敫
+敬
+数
+敲
+整
+敷
+文
+斋
+斌
+斐
+斑
+斓
+斗
+料
+斛
+斜
+斟
+斡
+斤
+斥
+斧
+斩
+斫
+断
+斯
+新
+方
+於
+施
+旁
+旃
+旄
+旅
+旆
+旋
+旌
+旎
+族
+旒
+旖
+旗
+无
+既
+日
+旦
+旧
+旨
+早
+旬
+旭
+旮
+旯
+旰
+旱
+时
+旷
+旺
+昀
+昂
+昃
+昆
+昊
+昌
+明
+昏
+易
+昔
+昕
+昙
+昝
+星
+映
+春
+昧
+昨
+昭
+是
+昱
+昴
+昵
+昶
+昼
+显
+晁
+晃
+晋
+晌
+晏
+晒
+晓
+晔
+晕
+晖
+晗
+晚
+晟
+晡
+晤
+晦
+晨
+普
+景
+晰
+晴
+晶
+晷
+智
+晾
+暂
+暄
+暇
+暌
+暑
+暖
+暗
+暝
+暧
+暨
+暮
+暴
+暹
+暾
+曙
+曛
+曜
+曝
+曦
+曩
+曰
+曲
+曳
+更
+曷
+曹
+曼
+曾
+替
+最
+月
+有
+朊
+朋
+服
+朐
+朔
+朕
+朗
+望
+朝
+期
+朦
+木
+未
+末
+本
+札
+术
+朱
+朴
+朵
+机
+朽
+杀
+杂
+权
+杆
+杈
+杉
+杌
+李
+杏
+材
+村
+杓
+杖
+杜
+杞
+束
+杠
+条
+来
+杨
+杩
+杪
+杭
+杯
+杰
+杲
+杳
+杵
+杷
+杼
+松
+板
+极
+构
+枇
+枉
+枋
+析
+枕
+林
+枘
+枚
+果
+枝
+枞
+枢
+枣
+枥
+枧
+枨
+枪
+枫
+枭
+枯
+枰
+枳
+枵
+架
+枷
+枸
+柁
+柃
+柄
+柏
+某
+柑
+柒
+染
+柔
+柘
+柙
+柚
+柜
+柝
+柞
+柠
+柢
+查
+柩
+柬
+柯
+柰
+柱
+柳
+柴
+柽
+柿
+栀
+栅
+标
+栈
+栉
+栊
+栋
+栌
+栎
+栏
+树
+栓
+栖
+栗
+栝
+校
+栩
+株
+栲
+栳
+样
+核
+根
+格
+栽
+栾
+桀
+桁
+桂
+桃
+桄
+桅
+框
+案
+桉
+桊
+桌
+桎
+桐
+桑
+桓
+桔
+桕
+桠
+桡
+桢
+档
+桤
+桥
+桦
+桧
+桨
+桩
+桫
+桴
+桶
+桷
+梁
+梃
+梅
+梆
+梏
+梓
+梗
+梢
+梦
+梧
+梨
+梭
+梯
+械
+梳
+梵
+检
+棂
+棉
+棋
+棍
+棒
+棕
+棘
+棚
+棠
+棣
+森
+棰
+棱
+棵
+棹
+棺
+棼
+椁
+椅
+椋
+植
+椎
+椐
+椒
+椟
+椠
+椤
+椭
+椰
+椴
+椹
+椽
+椿
+楂
+楔
+楗
+楚
+楝
+楞
+楠
+楣
+楦
+楫
+楮
+楱
+楷
+楸
+楹
+楼
+榀
+概
+榄
+榆
+榇
+榈
+榉
+榍
+榔
+榕
+榘
+榛
+榜
+榧
+榨
+榫
+榭
+榱
+榴
+榷
+榻
+槁
+槊
+槌
+槎
+槐
+槔
+槛
+槟
+槠
+槭
+槲
+槽
+槿
+樊
+樗
+樘
+樟
+模
+樨
+横
+樯
+樱
+樵
+樽
+樾
+橄
+橇
+橐
+橘
+橙
+橛
+橡
+橥
+橱
+橹
+橼
+檀
+檄
+檎
+檐
+檑
+檗
+檠
+檩
+檫
+檬
+欠
+次
+欢
+欣
+欤
+欧
+欲
+欷
+欹
+欺
+款
+歃
+歆
+歇
+歉
+歌
+歙
+止
+正
+此
+步
+武
+歧
+歪
+歹
+死
+歼
+殁
+殂
+殃
+殄
+殆
+殇
+殉
+殊
+残
+殍
+殒
+殓
+殖
+殚
+殛
+殡
+殪
+殳
+殴
+段
+殷
+殿
+毁
+毂
+毅
+毋
+母
+每
+毒
+毓
+比
+毕
+毖
+毗
+毙
+毛
+毡
+毪
+毫
+毯
+毳
+毵
+毹
+毽
+氅
+氆
+氇
+氍
+氏
+氐
+民
+氓
+气
+氕
+氖
+氘
+氙
+氚
+氛
+氟
+氡
+氢
+氤
+氦
+氧
+氨
+氩
+氪
+氮
+氯
+氰
+氲
+水
+氵
+永
+氽
+汀
+汁
+求
+汆
+汇
+汉
+汊
+汐
+汔
+汕
+汗
+汛
+汜
+汝
+汞
+江
+池
+污
+汤
+汨
+汩
+汪
+汰
+汲
+汴
+汶
+汹
+汽
+汾
+沁
+沂
+沃
+沅
+沆
+沈
+沉
+沌
+沏
+沐
+沓
+沔
+沙
+沛
+沟
+没
+沣
+沤
+沥
+沦
+沧
+沩
+沪
+沫
+沭
+沮
+沱
+沲
+河
+沸
+油
+治
+沼
+沽
+沾
+沿
+泄
+泅
+泉
+泊
+泌
+泐
+泓
+泔
+法
+泖
+泗
+泛
+泞
+泠
+泡
+波
+泣
+泥
+注
+泪
+泫
+泮
+泯
+泰
+泱
+泳
+泵
+泶
+泷
+泸
+泺
+泻
+泼
+泽
+泾
+洁
+洄
+洇
+洋
+洌
+洎
+洒
+洗
+洙
+洚
+洛
+洞
+津
+洧
+洪
+洫
+洮
+洱
+洲
+洳
+洵
+洹
+活
+洼
+洽
+派
+流
+浃
+浅
+浆
+浇
+浈
+浊
+测
+浍
+济
+浏
+浑
+浒
+浓
+浔
+浙
+浚
+浜
+浞
+浠
+浣
+浦
+浩
+浪
+浮
+浯
+浴
+海
+浸
+浼
+涂
+涅
+消
+涉
+涌
+涎
+涑
+涓
+涔
+涕
+涛
+涝
+涞
+涟
+涠
+涡
+涣
+涤
+润
+涧
+涨
+涩
+涪
+涫
+涮
+涯
+液
+涵
+涸
+涿
+淀
+淄
+淅
+淆
+淇
+淋
+淌
+淑
+淖
+淘
+淙
+淝
+淞
+淠
+淡
+淤
+淦
+淫
+淬
+淮
+深
+淳
+混
+淹
+添
+淼
+清
+渊
+渌
+渍
+渎
+渐
+渑
+渔
+渖
+渗
+渚
+渝
+渠
+渡
+渣
+渤
+渥
+温
+渫
+渭
+港
+渲
+渴
+游
+渺
+湃
+湄
+湍
+湎
+湓
+湔
+湖
+湘
+湛
+湟
+湫
+湮
+湾
+湿
+溃
+溅
+溆
+溉
+溏
+源
+溘
+溜
+溟
+溢
+溥
+溧
+溪
+溯
+溱
+溲
+溴
+溶
+溷
+溺
+溻
+溽
+滁
+滂
+滇
+滋
+滏
+滑
+滓
+滔
+滕
+滗
+滚
+滞
+滟
+滠
+满
+滢
+滤
+滥
+滦
+滨
+滩
+滴
+滹
+漂
+漆
+漉
+漏
+漓
+演
+漕
+漠
+漤
+漩
+漪
+漫
+漭
+漯
+漱
+漳
+漶
+漾
+潆
+潇
+潋
+潍
+潘
+潜
+潞
+潢
+潦
+潭
+潮
+潲
+潴
+潸
+潺
+潼
+澄
+澈
+澉
+澌
+澍
+澎
+澜
+澡
+澧
+澳
+澶
+澹
+激
+濂
+濉
+濑
+濒
+濞
+濠
+濡
+濮
+濯
+瀑
+瀚
+瀛
+瀣
+瀵
+瀹
+灌
+灏
+灞
+火
+灬
+灭
+灯
+灰
+灵
+灶
+灸
+灼
+灾
+灿
+炀
+炅
+炉
+炊
+炎
+炒
+炔
+炕
+炖
+炙
+炜
+炝
+炫
+炬
+炭
+炮
+炯
+炱
+炳
+炷
+炸
+点
+炻
+炼
+炽
+烀
+烁
+烂
+烃
+烈
+烊
+烘
+烙
+烛
+烟
+烤
+烦
+烧
+烨
+烩
+烫
+烬
+热
+烯
+烷
+烹
+烽
+焉
+焊
+焐
+焓
+焕
+焖
+焘
+焙
+焚
+焦
+焯
+焰
+焱
+然
+煅
+煊
+煌
+煎
+煜
+煞
+煤
+煦
+照
+煨
+煮
+煲
+煳
+煸
+煺
+煽
+熄
+熊
+熏
+熔
+熘
+熙
+熟
+熠
+熨
+熬
+熳
+熵
+熹
+燃
+燎
+燔
+燕
+燠
+燥
+燧
+燮
+燹
+爆
+爝
+爨
+爪
+爬
+爰
+爱
+爵
+父
+爷
+爸
+爹
+爻
+爽
+爿
+片
+版
+牌
+牍
+牒
+牗
+牙
+牛
+牝
+牟
+牡
+牢
+牦
+牧
+物
+牮
+牯
+牲
+牵
+特
+牺
+牾
+牿
+犀
+犁
+犄
+犊
+犋
+犍
+犏
+犒
+犟
+犬
+犭
+犯
+犰
+犴
+状
+犷
+犸
+犹
+狁
+狂
+狃
+狄
+狈
+狍
+狎
+狐
+狒
+狗
+狙
+狞
+狠
+狡
+狨
+狩
+独
+狭
+狮
+狯
+狰
+狱
+狲
+狳
+狴
+狷
+狸
+狺
+狻
+狼
+猁
+猃
+猊
+猎
+猓
+猕
+猖
+猗
+猛
+猜
+猝
+猞
+猡
+猢
+猥
+猩
+猪
+猫
+猬
+献
+猱
+猴
+猷
+猸
+猹
+猾
+猿
+獍
+獐
+獒
+獗
+獠
+獬
+獭
+獯
+獾
+玄
+率
+玉
+王
+玎
+玑
+玖
+玛
+玟
+玢
+玩
+玫
+玮
+环
+现
+玲
+玳
+玷
+玺
+玻
+珀
+珂
+珈
+珉
+珊
+珍
+珏
+珐
+珑
+珙
+珞
+珠
+珥
+珧
+珩
+班
+珲
+球
+琅
+理
+琉
+琊
+琏
+琐
+琚
+琛
+琢
+琥
+琦
+琨
+琪
+琬
+琮
+琰
+琳
+琴
+琵
+琶
+琼
+瑁
+瑕
+瑗
+瑙
+瑚
+瑛
+瑜
+瑞
+瑟
+瑭
+瑰
+瑶
+瑷
+瑾
+璀
+璁
+璃
+璇
+璋
+璎
+璐
+璜
+璞
+璧
+璨
+璩
+璺
+瓒
+瓜
+瓞
+瓠
+瓢
+瓣
+瓤
+瓦
+瓮
+瓯
+瓴
+瓶
+瓷
+瓿
+甄
+甍
+甏
+甑
+甓
+甘
+甙
+甚
+甜
+生
+甥
+用
+甩
+甫
+甬
+甭
+甯
+田
+由
+甲
+申
+电
+男
+甸
+町
+画
+甾
+畀
+畅
+畈
+畋
+界
+畎
+畏
+畔
+留
+畚
+畛
+畜
+略
+畦
+番
+畬
+畴
+畸
+畹
+畿
+疃
+疆
+疋
+疏
+疑
+疒
+疔
+疖
+疗
+疙
+疚
+疝
+疟
+疠
+疡
+疣
+疤
+疥
+疫
+疬
+疮
+疯
+疰
+疱
+疲
+疳
+疴
+疵
+疸
+疹
+疼
+疽
+疾
+痂
+痃
+痄
+病
+症
+痈
+痉
+痊
+痍
+痒
+痔
+痕
+痖
+痘
+痛
+痞
+痢
+痣
+痤
+痦
+痧
+痨
+痪
+痫
+痰
+痱
+痴
+痹
+痼
+痿
+瘀
+瘁
+瘃
+瘅
+瘊
+瘌
+瘐
+瘕
+瘗
+瘘
+瘙
+瘛
+瘟
+瘠
+瘢
+瘤
+瘥
+瘦
+瘩
+瘪
+瘫
+瘭
+瘰
+瘳
+瘴
+瘵
+瘸
+瘼
+瘾
+瘿
+癀
+癃
+癌
+癍
+癔
+癖
+癜
+癞
+癣
+癫
+癯
+癸
+登
+白
+百
+皂
+的
+皆
+皇
+皈
+皋
+皎
+皑
+皓
+皖
+皙
+皤
+皮
+皱
+皲
+皴
+皿
+盂
+盅
+盆
+盈
+益
+盍
+盎
+盏
+盐
+监
+盒
+盔
+盖
+盗
+盘
+盛
+盟
+盥
+目
+盯
+盱
+盲
+直
+相
+盹
+盼
+盾
+省
+眄
+眇
+眈
+眉
+看
+眍
+眙
+眚
+真
+眠
+眢
+眦
+眨
+眩
+眭
+眯
+眵
+眶
+眷
+眸
+眺
+眼
+着
+睁
+睃
+睇
+睐
+睑
+睚
+睛
+睡
+睢
+督
+睥
+睦
+睨
+睫
+睬
+睹
+睽
+睾
+睿
+瞀
+瞄
+瞅
+瞌
+瞍
+瞎
+瞑
+瞒
+瞟
+瞠
+瞢
+瞥
+瞧
+瞩
+瞪
+瞬
+瞰
+瞳
+瞵
+瞻
+瞽
+瞿
+矍
+矗
+矛
+矜
+矢
+矣
+知
+矧
+矩
+矫
+矬
+短
+矮
+石
+矶
+矸
+矽
+矾
+矿
+砀
+码
+砂
+砉
+砌
+砍
+砑
+砒
+研
+砖
+砗
+砘
+砚
+砜
+砝
+砟
+砣
+砥
+砦
+砧
+砩
+砬
+砭
+砰
+破
+砷
+砸
+砹
+砺
+砻
+砼
+砾
+础
+硅
+硇
+硌
+硎
+硐
+硒
+硕
+硖
+硗
+硝
+硪
+硫
+硬
+硭
+确
+硷
+硼
+碇
+碉
+碌
+碍
+碎
+碑
+碓
+碗
+碘
+碚
+碛
+碜
+碟
+碡
+碣
+碥
+碧
+碰
+碱
+碲
+碳
+碴
+碹
+碾
+磁
+磅
+磉
+磊
+磋
+磐
+磔
+磕
+磙
+磨
+磬
+磲
+磴
+磷
+磺
+礁
+礅
+礓
+礞
+礤
+礴
+示
+礻
+礼
+社
+祀
+祁
+祅
+祈
+祉
+祓
+祖
+祗
+祚
+祛
+祜
+祝
+神
+祟
+祠
+祢
+祥
+祧
+票
+祭
+祯
+祷
+祸
+祺
+禀
+禁
+禄
+禅
+禊
+福
+禚
+禧
+禳
+禹
+禺
+离
+禽
+禾
+秀
+私
+秃
+秆
+秉
+秋
+种
+科
+秒
+秕
+秘
+租
+秣
+秤
+秦
+秧
+秩
+秫
+秭
+积
+称
+秸
+移
+秽
+稀
+稂
+稃
+稆
+程
+稍
+税
+稔
+稗
+稚
+稞
+稠
+稣
+稳
+稷
+稹
+稻
+稼
+稽
+稿
+穆
+穑
+穗
+穰
+穴
+究
+穷
+穸
+穹
+空
+穿
+窀
+突
+窃
+窄
+窆
+窈
+窍
+窑
+窒
+窕
+窖
+窗
+窘
+窜
+窝
+窟
+窠
+窥
+窦
+窨
+窬
+窭
+窳
+窿
+立
+竖
+站
+竞
+竟
+章
+竣
+童
+竦
+竭
+端
+竹
+竺
+竽
+竿
+笃
+笄
+笆
+笈
+笊
+笋
+笏
+笑
+笔
+笕
+笙
+笛
+笞
+笠
+笤
+笥
+符
+笨
+笪
+笫
+第
+笮
+笱
+笳
+笸
+笺
+笼
+笾
+筅
+筇
+等
+筋
+筌
+筏
+筐
+筑
+筒
+答
+策
+筘
+筚
+筛
+筝
+筠
+筢
+筮
+筱
+筲
+筵
+筷
+筹
+筻
+签
+简
+箅
+箍
+箐
+箔
+箕
+算
+箜
+箝
+管
+箢
+箦
+箧
+箨
+箩
+箪
+箫
+箬
+箭
+箱
+箴
+箸
+篁
+篆
+篇
+篌
+篑
+篓
+篙
+篚
+篝
+篡
+篥
+篦
+篪
+篮
+篱
+篷
+篼
+篾
+簇
+簋
+簌
+簏
+簖
+簟
+簦
+簧
+簪
+簸
+簿
+籀
+籁
+籍
+米
+籴
+类
+籼
+籽
+粉
+粑
+粒
+粕
+粗
+粘
+粜
+粝
+粞
+粟
+粢
+粤
+粥
+粪
+粮
+粱
+粲
+粳
+粹
+粼
+粽
+精
+糁
+糅
+糇
+糈
+糊
+糌
+糍
+糕
+糖
+糗
+糙
+糜
+糟
+糠
+糨
+糯
+糸
+系
+紊
+素
+索
+紧
+紫
+累
+絮
+絷
+綦
+綮
+縻
+繁
+繇
+纂
+纛
+纟
+纠
+纡
+红
+纣
+纤
+纥
+约
+级
+纨
+纩
+纪
+纫
+纬
+纭
+纯
+纰
+纱
+纲
+纳
+纵
+纶
+纷
+纸
+纹
+纺
+纽
+纾
+线
+绀
+绁
+绂
+练
+组
+绅
+细
+织
+终
+绉
+绊
+绋
+绌
+绍
+绎
+经
+绐
+绑
+绒
+结
+绔
+绕
+绗
+绘
+给
+绚
+绛
+络
+绝
+绞
+统
+绠
+绡
+绢
+绣
+绥
+绦
+继
+绨
+绩
+绪
+绫
+续
+绮
+绯
+绰
+绱
+绲
+绳
+维
+绵
+绶
+绷
+绸
+绺
+绻
+综
+绽
+绾
+绿
+缀
+缁
+缂
+缃
+缄
+缅
+缆
+缇
+缈
+缉
+缋
+缌
+缍
+缎
+缏
+缑
+缒
+缓
+缔
+缕
+编
+缗
+缘
+缙
+缚
+缛
+缜
+缝
+缟
+缠
+缡
+缢
+缣
+缤
+缥
+缦
+缧
+缨
+缩
+缪
+缫
+缬
+缭
+缮
+缯
+缰
+缱
+缲
+缳
+缴
+缵
+缶
+缸
+缺
+罂
+罄
+罅
+罐
+网
+罔
+罕
+罗
+罘
+罚
+罟
+罡
+罢
+罨
+罩
+罪
+置
+罱
+署
+罴
+罹
+罾
+羁
+羊
+羌
+美
+羔
+羚
+羝
+羞
+羟
+羡
+群
+羧
+羯
+羰
+羲
+羸
+羹
+羼
+羽
+羿
+翁
+翅
+翊
+翌
+翎
+翔
+翕
+翘
+翟
+翠
+翡
+翥
+翦
+翩
+翮
+翰
+翱
+翳
+翻
+翼
+耀
+老
+考
+耄
+者
+耆
+耊
+而
+耍
+耐
+耒
+耔
+耕
+耖
+耗
+耘
+耙
+耜
+耠
+耢
+耥
+耦
+耧
+耨
+耩
+耪
+耱
+耳
+耵
+耶
+耷
+耸
+耻
+耽
+耿
+聂
+聃
+聆
+聊
+聋
+职
+聍
+聒
+联
+聘
+聚
+聩
+聪
+聱
+聿
+肀
+肃
+肄
+肆
+肇
+肉
+肋
+肌
+肓
+肖
+肘
+肚
+肛
+肜
+肝
+肟
+肠
+股
+肢
+肤
+肥
+肩
+肪
+肫
+肭
+肮
+肯
+肱
+育
+肴
+肷
+肺
+肼
+肽
+肾
+肿
+胀
+胁
+胂
+胃
+胄
+胆
+背
+胍
+胎
+胖
+胗
+胙
+胚
+胛
+胜
+胝
+胞
+胡
+胤
+胥
+胧
+胨
+胩
+胪
+胫
+胬
+胭
+胯
+胰
+胱
+胲
+胳
+胴
+胶
+胸
+胺
+胼
+能
+脂
+脆
+脉
+脊
+脍
+脎
+脏
+脐
+脑
+脒
+脓
+脔
+脖
+脘
+脚
+脞
+脟
+脯
+脱
+脲
+脶
+脸
+脾
+腆
+腈
+腊
+腋
+腌
+腐
+腑
+腓
+腔
+腕
+腙
+腚
+腠
+腥
+腧
+腩
+腭
+腮
+腰
+腱
+腴
+腹
+腺
+腻
+腼
+腽
+腾
+腿
+膀
+膂
+膈
+膊
+膏
+膑
+膘
+膛
+膜
+膝
+膣
+膦
+膨
+膪
+膳
+膺
+膻
+臀
+臁
+臂
+臃
+臆
+臊
+臌
+臣
+臧
+自
+臬
+臭
+至
+致
+臻
+臼
+臾
+舀
+舁
+舂
+舄
+舅
+舆
+舌
+舍
+舐
+舒
+舔
+舛
+舜
+舞
+舟
+舡
+舢
+舣
+舨
+航
+舫
+般
+舭
+舯
+舰
+舱
+舳
+舴
+舵
+舶
+舷
+舸
+船
+舻
+舾
+艄
+艇
+艉
+艋
+艏
+艘
+艚
+艟
+艨
+艮
+良
+艰
+色
+艳
+艴
+艹
+艺
+艽
+艾
+艿
+节
+芄
+芈
+芊
+芋
+芍
+芎
+芏
+芑
+芒
+芗
+芘
+芙
+芜
+芝
+芟
+芡
+芤
+芥
+芦
+芨
+芩
+芪
+芫
+芬
+芭
+芮
+芯
+芰
+花
+芳
+芴
+芷
+芸
+芹
+芽
+芾
+苁
+苄
+苇
+苈
+苊
+苋
+苌
+苍
+苎
+苏
+苑
+苒
+苓
+苔
+苕
+苗
+苘
+苛
+苜
+苞
+苟
+苠
+苡
+苣
+苤
+若
+苦
+苫
+苯
+英
+苴
+苷
+苹
+苻
+茁
+茂
+范
+茄
+茅
+茆
+茇
+茈
+茉
+茌
+茎
+茏
+茑
+茔
+茕
+茗
+茚
+茛
+茜
+茧
+茨
+茫
+茬
+茭
+茯
+茱
+茳
+茴
+茵
+茶
+茸
+茹
+茺
+茼
+荀
+荃
+荆
+荇
+草
+荏
+荐
+荑
+荒
+荔
+荚
+荛
+荜
+荞
+荟
+荠
+荡
+荣
+荤
+荥
+荦
+荧
+荨
+荩
+荪
+荫
+荬
+荭
+荮
+药
+荷
+荸
+荻
+荼
+荽
+莅
+莆
+莉
+莎
+莒
+莓
+莘
+莛
+莜
+莞
+莠
+莨
+莩
+莪
+莫
+莰
+莱
+莲
+莳
+莴
+莶
+获
+莸
+莹
+莺
+莼
+莽
+菀
+菁
+菅
+菇
+菊
+菌
+菏
+菔
+菖
+菘
+菜
+菝
+菟
+菠
+菡
+菥
+菩
+菪
+菰
+菱
+菲
+菸
+菹
+菽
+萁
+萃
+萄
+萆
+萋
+萌
+萍
+萎
+萏
+萑
+萘
+萜
+萝
+萤
+营
+萦
+萧
+萨
+萱
+萸
+萼
+落
+葆
+葑
+著
+葙
+葚
+葛
+葜
+葡
+董
+葩
+葫
+葬
+葭
+葱
+葳
+葵
+葶
+葸
+葺
+蒂
+蒇
+蒈
+蒉
+蒋
+蒌
+蒎
+蒗
+蒙
+蒜
+蒡
+蒯
+蒲
+蒴
+蒸
+蒹
+蒺
+蒽
+蒿
+蓁
+蓄
+蓉
+蓊
+蓍
+蓐
+蓑
+蓓
+蓖
+蓝
+蓟
+蓠
+蓣
+蓥
+蓦
+蓬
+蓰
+蓼
+蓿
+蔌
+蔑
+蔓
+蔗
+蔚
+蔟
+蔡
+蔫
+蔬
+蔷
+蔸
+蔹
+蔺
+蔻
+蔼
+蔽
+蕃
+蕈
+蕉
+蕊
+蕖
+蕙
+蕞
+蕤
+蕨
+蕲
+蕴
+蕹
+蕺
+蕻
+蕾
+薄
+薅
+薇
+薏
+薛
+薜
+薤
+薧
+薪
+薮
+薯
+薰
+薷
+薹
+藁
+藉
+藏
+藐
+藓
+藕
+藜
+藤
+藩
+藻
+藿
+蘅
+蘑
+蘖
+蘧
+蘩
+蘸
+蘼
+虍
+虎
+虏
+虐
+虑
+虔
+虚
+虞
+虢
+虫
+虬
+虮
+虱
+虹
+虺
+虻
+虼
+虽
+虾
+虿
+蚀
+蚁
+蚂
+蚊
+蚋
+蚌
+蚍
+蚓
+蚕
+蚜
+蚝
+蚣
+蚤
+蚧
+蚨
+蚩
+蚪
+蚬
+蚯
+蚰
+蚱
+蚴
+蚵
+蚶
+蚺
+蛀
+蛄
+蛆
+蛇
+蛉
+蛊
+蛋
+蛎
+蛏
+蛐
+蛑
+蛔
+蛘
+蛙
+蛛
+蛞
+蛟
+蛤
+蛩
+蛭
+蛮
+蛰
+蛱
+蛲
+蛳
+蛴
+蛸
+蛹
+蛾
+蜀
+蜂
+蜃
+蜇
+蜈
+蜉
+蜊
+蜍
+蜒
+蜓
+蜕
+蜗
+蜘
+蜚
+蜜
+蜞
+蜡
+蜢
+蜣
+蜥
+蜩
+蜮
+蜱
+蜴
+蜷
+蜻
+蜾
+蜿
+蝇
+蝈
+蝉
+蝌
+蝎
+蝓
+蝗
+蝙
+蝠
+蝣
+蝤
+蝥
+蝮
+蝰
+蝴
+蝶
+蝻
+蝼
+蝽
+蝾
+螂
+螃
+螅
+螈
+螋
+融
+螓
+螗
+螟
+螨
+螫
+螬
+螭
+螯
+螳
+螵
+螺
+螽
+蟀
+蟆
+蟊
+蟋
+蟑
+蟒
+蟓
+蟛
+蟠
+蟥
+蟪
+蟮
+蟹
+蟾
+蠃
+蠊
+蠓
+蠕
+蠖
+蠛
+蠡
+蠢
+蠲
+蠹
+蠼
+血
+衄
+衅
+行
+衍
+衔
+街
+衙
+衡
+衢
+衣
+衤
+补
+表
+衩
+衫
+衬
+衮
+衰
+衲
+衷
+衽
+衾
+衿
+袁
+袂
+袄
+袅
+袈
+袋
+袍
+袒
+袖
+袜
+袢
+袤
+被
+袭
+袱
+袷
+袼
+裁
+裂
+装
+裆
+裉
+裎
+裒
+裔
+裕
+裘
+裙
+裟
+裢
+裣
+裤
+裥
+裨
+裰
+裱
+裳
+裴
+裸
+裹
+裼
+裾
+褂
+褊
+褐
+褒
+褓
+褙
+褚
+褛
+褡
+褥
+褪
+褫
+褰
+褴
+褶
+襁
+襄
+襞
+襟
+襦
+襻
+西
+要
+覃
+覆
+见
+观
+规
+觅
+视
+觇
+览
+觉
+觊
+觋
+觌
+觎
+觏
+觐
+觑
+角
+觖
+觚
+觜
+觞
+解
+觥
+触
+觫
+觯
+觳
+言
+訇
+訾
+詈
+詹
+誉
+誊
+誓
+謇
+謦
+警
+譬
+讠
+计
+订
+讣
+认
+讥
+讦
+讧
+讨
+让
+讪
+讫
+训
+议
+讯
+记
+讲
+讳
+讴
+讵
+讶
+讷
+许
+讹
+论
+讼
+讽
+设
+访
+诀
+证
+诂
+诃
+评
+诅
+识
+诈
+诉
+诊
+诋
+诌
+词
+诎
+诏
+译
+诒
+诓
+诔
+试
+诖
+诗
+诘
+诙
+诚
+诛
+诜
+话
+诞
+诟
+诠
+诡
+询
+诣
+诤
+该
+详
+诧
+诨
+诩
+诫
+诬
+语
+诮
+误
+诰
+诱
+诲
+诳
+说
+诵
+诶
+请
+诸
+诹
+诺
+读
+诼
+诽
+课
+诿
+谀
+谁
+谂
+调
+谄
+谅
+谆
+谇
+谈
+谊
+谋
+谌
+谍
+谎
+谏
+谐
+谑
+谒
+谓
+谔
+谕
+谖
+谗
+谘
+谙
+谚
+谛
+谜
+谝
+谟
+谠
+谡
+谢
+谣
+谤
+谥
+谦
+谧
+谨
+谩
+谪
+谫
+谬
+谭
+谮
+谯
+谰
+谱
+谲
+谳
+谴
+谵
+谶
+谷
+豁
+豆
+豇
+豉
+豌
+豕
+豚
+象
+豢
+豪
+豫
+豳
+豸
+豹
+豺
+貂
+貅
+貉
+貊
+貌
+貔
+貘
+贝
+贞
+负
+贡
+财
+责
+贤
+败
+账
+货
+质
+贩
+贪
+贫
+贬
+购
+贮
+贯
+贰
+贱
+贲
+贳
+贴
+贵
+贶
+贷
+贸
+费
+贺
+贻
+贼
+贽
+贾
+贿
+赀
+赁
+赂
+赃
+资
+赅
+赆
+赇
+赈
+赉
+赊
+赋
+赌
+赍
+赎
+赏
+赐
+赓
+赔
+赕
+赖
+赘
+赙
+赚
+赛
+赜
+赝
+赞
+赠
+赡
+赢
+赣
+赤
+赦
+赧
+赫
+赭
+走
+赳
+赴
+赵
+起
+赶
+趁
+趄
+超
+越
+趋
+趑
+趔
+趟
+趣
+趱
+足
+趴
+趵
+趸
+趺
+趼
+趾
+趿
+跃
+跄
+跆
+跋
+跌
+跎
+跏
+跑
+跖
+跗
+跚
+跛
+距
+跞
+跟
+跣
+跤
+跨
+跪
+跫
+跬
+路
+跳
+践
+跷
+跸
+跹
+跺
+跻
+跽
+踅
+踉
+踊
+踌
+踏
+踔
+踝
+踞
+踟
+踢
+踣
+踩
+踪
+踬
+踮
+踯
+踱
+踵
+踹
+踺
+踽
+蹀
+蹁
+蹂
+蹄
+蹇
+蹈
+蹉
+蹊
+蹋
+蹑
+蹒
+蹙
+蹦
+蹩
+蹬
+蹭
+蹯
+蹰
+蹲
+蹴
+蹶
+蹼
+蹿
+躁
+躅
+躇
+躏
+躐
+躔
+躜
+躞
+身
+躬
+躯
+躲
+躺
+軎
+车
+轧
+轨
+轩
+轫
+转
+轭
+轮
+软
+轰
+轱
+轲
+轳
+轴
+轵
+轶
+轷
+轸
+轹
+轺
+轻
+轼
+载
+轾
+轿
+辁
+辂
+较
+辄
+辅
+辆
+辇
+辈
+辉
+辊
+辋
+辍
+辎
+辏
+辐
+辑
+输
+辔
+辕
+辖
+辗
+辘
+辙
+辚
+辛
+辜
+辞
+辟
+辣
+辨
+辩
+辫
+辰
+辱
+辶
+边
+辽
+达
+迁
+迂
+迄
+迅
+过
+迈
+迎
+运
+近
+迓
+返
+迕
+还
+这
+进
+远
+违
+连
+迟
+迢
+迤
+迥
+迦
+迨
+迩
+迪
+迫
+迭
+迮
+述
+迳
+迷
+迸
+迹
+追
+退
+送
+适
+逃
+逄
+逅
+逆
+选
+逊
+逋
+逍
+透
+逐
+逑
+递
+途
+逖
+逗
+通
+逛
+逝
+逞
+速
+造
+逡
+逢
+逦
+逭
+逮
+逯
+逵
+逶
+逸
+逻
+逼
+逾
+遁
+遂
+遄
+遇
+遍
+遏
+遐
+遑
+遒
+道
+遗
+遘
+遛
+遢
+遣
+遥
+遨
+遭
+遮
+遴
+遵
+遽
+避
+邀
+邂
+邃
+邈
+邋
+邑
+邓
+邕
+邗
+邙
+邛
+邝
+邡
+邢
+那
+邦
+邪
+邬
+邮
+邯
+邰
+邱
+邳
+邴
+邵
+邶
+邸
+邹
+邺
+邻
+邾
+郁
+郄
+郅
+郇
+郊
+郎
+郏
+郐
+郑
+郓
+郗
+郛
+郜
+郝
+郡
+郢
+郦
+郧
+部
+郫
+郭
+郯
+郴
+郸
+都
+郾
+鄂
+鄄
+鄙
+鄞
+鄢
+鄣
+鄯
+鄱
+鄹
+酃
+酆
+酉
+酊
+酋
+酌
+配
+酎
+酏
+酐
+酒
+酗
+酚
+酝
+酞
+酡
+酢
+酣
+酤
+酥
+酩
+酪
+酬
+酮
+酯
+酰
+酱
+酲
+酴
+酵
+酶
+酷
+酸
+酹
+酽
+酾
+酿
+醅
+醇
+醉
+醋
+醌
+醍
+醐
+醑
+醒
+醚
+醛
+醢
+醣
+醪
+醭
+醮
+醯
+醴
+醵
+醺
+采
+釉
+释
+里
+重
+野
+量
+金
+釜
+鉴
+銎
+銮
+鋈
+錾
+鍪
+鎏
+鏊
+鏖
+鐾
+鑫
+钅
+钆
+钇
+针
+钉
+钊
+钋
+钌
+钍
+钎
+钏
+钐
+钒
+钓
+钔
+钕
+钗
+钙
+钚
+钛
+钜
+钝
+钞
+钟
+钠
+钡
+钢
+钣
+钤
+钥
+钦
+钧
+钨
+钩
+钪
+钫
+钬
+钭
+钮
+钯
+钰
+钱
+钲
+钳
+钴
+钵
+钶
+钷
+钸
+钹
+钺
+钻
+钼
+钽
+钾
+钿
+铀
+铁
+铂
+铃
+铄
+铅
+铆
+铈
+铉
+铊
+铋
+铌
+铍
+铎
+铐
+铑
+铒
+铕
+铖
+铗
+铘
+铙
+铛
+铜
+铝
+铞
+铟
+铠
+铡
+铢
+铣
+铤
+铥
+铧
+铨
+铩
+铪
+铫
+铬
+铭
+铮
+铯
+铰
+铱
+铲
+铳
+铴
+铵
+银
+铷
+铸
+铹
+铺
+铼
+铽
+链
+铿
+销
+锁
+锂
+锃
+锄
+锅
+锆
+锇
+锈
+锉
+锊
+锋
+锌
+锍
+锎
+锏
+锐
+锑
+锒
+锓
+锔
+锕
+锖
+锗
+锘
+错
+锚
+锛
+锝
+锞
+锟
+锡
+锢
+锣
+锤
+锥
+锦
+锨
+锩
+锪
+锫
+锬
+锭
+键
+锯
+锰
+锱
+锲
+锴
+锵
+锶
+锷
+锸
+锹
+锺
+锻
+锼
+锾
+锿
+镀
+镁
+镂
+镄
+镅
+镆
+镇
+镉
+镊
+镌
+镍
+镎
+镏
+镐
+镑
+镒
+镓
+镔
+镖
+镗
+镘
+镙
+镛
+镜
+镝
+镞
+镟
+镡
+镢
+镣
+镤
+镥
+镦
+镧
+镨
+镩
+镪
+镫
+镬
+镭
+镯
+镰
+镱
+镲
+镳
+镶
+长
+门
+闩
+闪
+闫
+闭
+问
+闯
+闰
+闱
+闲
+闳
+间
+闵
+闶
+闷
+闸
+闹
+闺
+闻
+闼
+闽
+闾
+阀
+阁
+阂
+阃
+阄
+阅
+阆
+阈
+阉
+阊
+阋
+阌
+阍
+阎
+阏
+阐
+阑
+阒
+阔
+阕
+阖
+阗
+阙
+阚
+阜
+阝
+队
+阡
+阢
+阪
+阮
+阱
+防
+阳
+阴
+阵
+阶
+阻
+阼
+阽
+阿
+陀
+陂
+附
+际
+陆
+陇
+陈
+陉
+陋
+陌
+降
+限
+陔
+陕
+陛
+陟
+陡
+院
+除
+陧
+陨
+险
+陪
+陬
+陲
+陴
+陵
+陶
+陷
+隅
+隆
+隈
+隋
+隍
+随
+隐
+隔
+隗
+隘
+隙
+障
+隧
+隰
+隳
+隶
+隹
+隼
+隽
+难
+雀
+雁
+雄
+雅
+集
+雇
+雉
+雌
+雍
+雎
+雏
+雒
+雕
+雠
+雨
+雩
+雪
+雯
+雳
+零
+雷
+雹
+雾
+需
+霁
+霄
+霆
+震
+霈
+霉
+霍
+霎
+霏
+霓
+霖
+霜
+霞
+霪
+霭
+霰
+露
+霸
+霹
+霾
+青
+靓
+靖
+静
+靛
+非
+靠
+靡
+面
+靥
+革
+靳
+靴
+靶
+靼
+鞅
+鞋
+鞍
+鞑
+鞒
+鞔
+鞘
+鞠
+鞣
+鞫
+鞭
+鞯
+鞲
+鞴
+韦
+韧
+韩
+韪
+韫
+韬
+韭
+音
+韵
+韶
+页
+顶
+顷
+顸
+项
+顺
+须
+顼
+顽
+顾
+顿
+颀
+颁
+颂
+颃
+预
+颅
+领
+颇
+颈
+颉
+颊
+颌
+颍
+颏
+颐
+频
+颓
+颔
+颖
+颗
+题
+颚
+颛
+颜
+额
+颞
+颟
+颠
+颡
+颢
+颤
+颥
+颦
+颧
+风
+飑
+飒
+飓
+飕
+飘
+飙
+飚
+飞
+食
+飧
+飨
+餍
+餐
+餮
+饔
+饕
+饣
+饥
+饧
+饨
+饩
+饪
+饫
+饬
+饭
+饮
+饯
+饰
+饱
+饲
+饴
+饵
+饶
+饷
+饺
+饼
+饽
+饿
+馀
+馁
+馄
+馅
+馆
+馇
+馈
+馊
+馋
+馍
+馏
+馐
+馑
+馒
+馓
+馔
+馕
+首
+馗
+馘
+香
+馥
+馨
+马
+驭
+驮
+驯
+驰
+驱
+驳
+驴
+驵
+驶
+驷
+驸
+驹
+驺
+驻
+驼
+驽
+驾
+驿
+骀
+骁
+骂
+骄
+骅
+骆
+骇
+骈
+骊
+骋
+验
+骏
+骐
+骑
+骒
+骓
+骖
+骗
+骘
+骚
+骛
+骜
+骝
+骞
+骟
+骠
+骡
+骢
+骣
+骤
+骥
+骧
+骨
+骰
+骱
+骶
+骷
+骸
+骺
+骼
+髀
+髁
+髂
+髅
+髋
+髌
+髑
+髓
+高
+髟
+髡
+髦
+髫
+髭
+髯
+髹
+髺
+鬃
+鬈
+鬏
+鬓
+鬟
+鬣
+鬯
+鬲
+鬻
+鬼
+魁
+魂
+魃
+魄
+魅
+魇
+魈
+魉
+魍
+魏
+魑
+魔
+鱼
+鱿
+鲁
+鲂
+鲅
+鲆
+鲇
+鲈
+鲋
+鲍
+鲎
+鲐
+鲑
+鲒
+鲔
+鲕
+鲚
+鲛
+鲜
+鲝
+鲟
+鲠
+鲡
+鲢
+鲣
+鲤
+鲥
+鲦
+鲧
+鲨
+鲩
+鲫
+鲭
+鲮
+鲰
+鲱
+鲲
+鲳
+鲴
+鲵
+鲶
+鲷
+鲸
+鲺
+鲻
+鲼
+鲽
+鳃
+鳄
+鳅
+鳆
+鳇
+鳊
+鳋
+鳌
+鳍
+鳎
+鳏
+鳐
+鳓
+鳔
+鳕
+鳖
+鳗
+鳘
+鳙
+鳜
+鳝
+鳞
+鳟
+鳢
+鸟
+鸠
+鸡
+鸢
+鸣
+鸥
+鸦
+鸨
+鸩
+鸪
+鸫
+鸬
+鸭
+鸯
+鸱
+鸲
+鸳
+鸵
+鸶
+鸷
+鸸
+鸹
+鸺
+鸽
+鸾
+鸿
+鹁
+鹂
+鹃
+鹄
+鹅
+鹆
+鹇
+鹈
+鹉
+鹊
+鹋
+鹌
+鹎
+鹏
+鹑
+鹕
+鹗
+鹘
+鹚
+鹛
+鹜
+鹞
+鹣
+鹤
+鹦
+鹧
+鹨
+鹩
+鹪
+鹫
+鹬
+鹭
+鹰
+鹱
+鹳
+鹾
+鹿
+麂
+麇
+麈
+麋
+麒
+麓
+麝
+麟
+麦
+麴
+麸
+麻
+麽
+麾
+黄
+黉
+黍
+黎
+黏
+黑
+黔
+默
+黛
+黜
+黝
+黟
+黠
+黢
+黥
+黧
+黩
+黪
+黯
+黹
+黻
+黼
+黾
+鼋
+鼍
+鼎
+鼐
+鼓
+鼗
+鼙
+鼠
+鼢
+鼬
+鼯
+鼷
+鼹
+鼻
+鼽
+鼾
+齄
+齐
+齑
+齿
+龀
+龃
+龄
+龅
+龆
+龇
+龈
+龉
+龊
+龋
+龌
+龙
+龚
+龛
+龟
+龠
+丂
+丏
+並
+丩
+丷
+乂
+乚
+乛
+亅
+亶
+亾
+仌
+仒
+倠
+僕
+兒
+兟
+冊
+冋
+冏
+冓
+冘
+冡
+刅
+劦
+匃
+匊
+匋
+匛
+匸
+匽
+卂
+卝
+卪
+卬
+厈
+厓
+厷
+厸
+厺
+厽
+叐
+叒
+叕
+叚
+吂
+吅
+咅
+咠
+咢
+啇
+啚
+喿
+囷
+圂
+圡
+圤
+圼
+坒
+坴
+垔
+壯
+壴
+夃
+夅
+夆
+夊
+夋
+夌
+夗
+夬
+妟
+媷
+孖
+孨
+寽
+尃
+尌
+尗
+尞
+屚
+屰
+嵒
+巜
+巟
+巤
+巸
+巿
+幹
+彑
+復
+怱
+悤
+惢
+戉
+戶
+拑
+敃
+敄
+斿
+旉
+旡
+昘
+昫
+昬
+昷
+朁
+朩
+朿
+枼
+桼
+棥
+楙
+欮
+欶
+歨
+歺
+殸
+殹
+毌
+氺
+氾
+氿
+汒
+沝
+泙
+洰
+炏
+爫
+犮
+犾
+狊
+猋
+玨
+甤
+甹
+畐
+畟
+畧
+異
+畺
+畾
+疐
+癶
+皃
+皅
+睘
+矞
+砳
+祘
+穵
+粦
+糞
+縣
+缊
+罙
+羕
+翏
+習
+耂
+耑
+耤
+耴
+肙
+臺
+臽
+臿
+茾
+荅
+菐
+蒦
+虒
+虖
+號
+豊
+豐
+豖
+豙
+豦
+豩
+貍
+車
+迶
+遀
+釆
+镸
+阞
+陏
+隺
+隻
+雚
+霝
+韰
+麥
+黽
\ No newline at end of file
diff --git a/kaiFontDatasetGeneration/chinese_characters.txt b/kaiFontDatasetGeneration/chinese_characters.txt
new file mode 100644
index 0000000..77401e0
--- /dev/null
+++ b/kaiFontDatasetGeneration/chinese_characters.txt
@@ -0,0 +1,6722 @@
+一
+丁
+七
+万
+丈
+三
+上
+下
+丌
+不
+与
+丐
+丑
+专
+且
+丕
+世
+丘
+丙
+业
+丛
+东
+丝
+丞
+丢
+两
+严
+丧
+个
+丫
+中
+丰
+串
+临
+丸
+丹
+为
+主
+丽
+举
+乃
+久
+乇
+么
+义
+之
+乌
+乍
+乎
+乏
+乐
+乒
+乓
+乔
+乖
+乘
+乙
+乜
+九
+乞
+也
+习
+乡
+书
+乩
+买
+乱
+乳
+乾
+了
+予
+争
+事
+二
+亍
+于
+亏
+云
+互
+亓
+五
+井
+亘
+亚
+些
+亟
+亡
+亢
+交
+亥
+亦
+产
+亨
+亩
+享
+京
+亭
+亮
+亲
+亳
+亵
+人
+亿
+什
+仁
+仂
+仃
+仄
+仅
+仆
+仇
+仉
+今
+介
+仍
+从
+仑
+仓
+仔
+仕
+他
+仗
+付
+仙
+仝
+仞
+仟
+仡
+代
+令
+以
+仨
+仪
+仫
+们
+仰
+仲
+仳
+仵
+件
+价
+任
+份
+仿
+企
+伉
+伊
+伍
+伎
+伏
+伐
+休
+众
+优
+伙
+会
+伛
+伞
+伟
+传
+伢
+伤
+伥
+伦
+伧
+伪
+伫
+伯
+估
+伲
+伴
+伶
+伸
+伺
+似
+伽
+佃
+但
+位
+低
+住
+佐
+佑
+体
+何
+佗
+佘
+余
+佚
+佛
+作
+佝
+佞
+佟
+你
+佣
+佤
+佥
+佧
+佩
+佬
+佯
+佰
+佳
+佴
+佶
+佻
+佼
+佾
+使
+侃
+侄
+侈
+侉
+例
+侍
+侏
+侑
+侔
+侗
+供
+依
+侠
+侣
+侥
+侦
+侧
+侨
+侩
+侪
+侬
+侮
+侯
+侵
+便
+促
+俄
+俅
+俊
+俎
+俏
+俐
+俑
+俗
+俘
+俚
+俜
+保
+俞
+俟
+信
+俣
+俦
+俨
+俩
+俪
+俭
+修
+俯
+俱
+俳
+俸
+俺
+俾
+倌
+倍
+倏
+倒
+倔
+倘
+候
+倚
+倜
+借
+倡
+倥
+倦
+倨
+倩
+倪
+倬
+倭
+倮
+债
+值
+倾
+偃
+假
+偈
+偌
+偎
+偏
+偕
+做
+停
+健
+偬
+偶
+偷
+偻
+偾
+偿
+傀
+傅
+傈
+傍
+傣
+傥
+傧
+储
+傩
+催
+傲
+傺
+傻
+像
+僖
+僚
+僦
+僧
+僬
+僭
+僮
+僳
+僵
+僻
+儆
+儇
+儋
+儡
+儿
+兀
+允
+元
+兄
+充
+兆
+先
+光
+克
+免
+兑
+兔
+兕
+兖
+党
+兜
+兢
+入
+全
+八
+公
+六
+兮
+兰
+共
+关
+兴
+兵
+其
+具
+典
+兹
+养
+兼
+兽
+冀
+冁
+内
+冈
+冉
+册
+再
+冒
+冕
+冗
+写
+军
+农
+冠
+冢
+冤
+冥
+冬
+冯
+冰
+冱
+冲
+决
+况
+冶
+冷
+冻
+冼
+冽
+净
+凄
+准
+凇
+凉
+凋
+凌
+减
+凑
+凛
+凝
+几
+凡
+凤
+凫
+凭
+凯
+凰
+凳
+凶
+凸
+凹
+出
+击
+凼
+函
+凿
+刀
+刁
+刃
+分
+切
+刈
+刊
+刍
+刎
+刑
+划
+刖
+列
+刘
+则
+刚
+创
+初
+删
+判
+刨
+利
+别
+刭
+刮
+到
+刳
+制
+刷
+券
+刹
+刺
+刻
+刽
+刿
+剀
+剁
+剂
+剃
+削
+剌
+前
+剐
+剑
+剔
+剖
+剜
+剞
+剡
+剥
+剧
+剩
+剪
+副
+割
+剽
+剿
+劁
+劂
+劈
+劐
+劓
+力
+劝
+办
+功
+加
+务
+劢
+劣
+动
+助
+努
+劫
+劬
+劭
+励
+劲
+劳
+劾
+势
+勃
+勇
+勉
+勋
+勐
+勒
+勖
+勘
+募
+勤
+勰
+勺
+勾
+勿
+匀
+包
+匆
+匈
+匍
+匏
+匐
+匕
+化
+北
+匙
+匝
+匠
+匡
+匣
+匦
+匪
+匮
+匹
+区
+医
+匾
+匿
+十
+千
+卅
+升
+午
+卉
+半
+华
+协
+卑
+卒
+卓
+单
+卖
+南
+博
+卜
+卞
+卟
+占
+卡
+卢
+卣
+卤
+卦
+卧
+卫
+卮
+卯
+印
+危
+即
+却
+卵
+卷
+卸
+卺
+卿
+厂
+厄
+厅
+历
+厉
+压
+厌
+厍
+厕
+厘
+厚
+厝
+原
+厢
+厣
+厥
+厦
+厨
+厩
+厮
+厶
+去
+县
+叁
+参
+又
+叉
+及
+友
+双
+反
+发
+叔
+取
+受
+变
+叙
+叛
+叟
+叠
+口
+古
+句
+另
+叨
+叩
+只
+叫
+召
+叭
+叮
+可
+台
+叱
+史
+右
+叵
+叶
+号
+司
+叹
+叻
+叼
+叽
+吁
+吃
+各
+吆
+合
+吉
+吊
+同
+名
+后
+吏
+吐
+向
+吒
+吓
+吕
+吖
+吗
+君
+吝
+吞
+吟
+吠
+吡
+吣
+否
+吧
+吨
+吩
+含
+听
+吭
+吮
+启
+吱
+吲
+吴
+吵
+吸
+吹
+吻
+吼
+吾
+呀
+呃
+呆
+呈
+告
+呋
+呐
+呒
+呓
+呔
+呕
+呖
+呗
+员
+呙
+呛
+呜
+呢
+呤
+呦
+周
+呱
+呲
+味
+呵
+呶
+呷
+呸
+呻
+呼
+命
+咀
+咂
+咄
+咆
+咋
+和
+咎
+咏
+咐
+咒
+咔
+咕
+咖
+咙
+咚
+咛
+咝
+咣
+咤
+咦
+咧
+咨
+咩
+咪
+咫
+咬
+咭
+咯
+咱
+咳
+咴
+咸
+咻
+咽
+咿
+哀
+品
+哂
+哄
+哆
+哇
+哈
+哉
+哌
+响
+哎
+哏
+哐
+哑
+哒
+哓
+哔
+哕
+哗
+哙
+哚
+哜
+哝
+哞
+哟
+哥
+哦
+哧
+哨
+哩
+哪
+哭
+哮
+哲
+哳
+哺
+哼
+哽
+哿
+唁
+唆
+唇
+唉
+唏
+唐
+唑
+唔
+唛
+唠
+唢
+唣
+唤
+唧
+唪
+唬
+售
+唯
+唰
+唱
+唳
+唷
+唼
+唾
+唿
+啁
+啃
+啄
+商
+啉
+啊
+啐
+啕
+啖
+啜
+啡
+啤
+啥
+啦
+啧
+啪
+啬
+啭
+啮
+啵
+啶
+啷
+啸
+啻
+啼
+啾
+喀
+喁
+喂
+喃
+善
+喇
+喈
+喉
+喊
+喋
+喏
+喑
+喔
+喘
+喙
+喜
+喝
+喟
+喧
+喱
+喳
+喵
+喷
+喹
+喻
+喽
+喾
+嗄
+嗅
+嗉
+嗌
+嗍
+嗑
+嗒
+嗓
+嗔
+嗖
+嗜
+嗝
+嗟
+嗡
+嗣
+嗤
+嗥
+嗦
+嗨
+嗪
+嗫
+嗬
+嗯
+嗲
+嗳
+嗵
+嗷
+嗽
+嗾
+嘀
+嘁
+嘈
+嘉
+嘌
+嘎
+嘏
+嘘
+嘛
+嘞
+嘟
+嘣
+嘤
+嘧
+嘬
+嘭
+嘱
+嘲
+嘴
+嘶
+嘹
+嘻
+嘿
+噌
+噍
+噎
+噔
+噗
+噘
+噙
+噜
+噢
+噤
+器
+噩
+噪
+噫
+噬
+噱
+噶
+噻
+噼
+嚅
+嚆
+嚎
+嚏
+嚓
+嚣
+嚯
+嚷
+嚼
+囊
+囔
+囗
+囚
+四
+囝
+回
+囟
+因
+囡
+团
+囤
+囫
+园
+困
+囱
+围
+囵
+囹
+固
+国
+图
+囿
+圃
+圄
+圆
+圈
+圉
+圊
+圜
+土
+圣
+在
+圩
+圪
+圬
+圭
+圮
+圯
+地
+圳
+圹
+场
+圻
+圾
+址
+坂
+均
+坊
+坌
+坍
+坎
+坏
+坐
+坑
+块
+坚
+坛
+坜
+坝
+坞
+坟
+坠
+坡
+坤
+坦
+坨
+坩
+坪
+坫
+坭
+坯
+坳
+坶
+坷
+坻
+坼
+垂
+垃
+垄
+垅
+垆
+型
+垌
+垒
+垓
+垛
+垠
+垡
+垢
+垣
+垤
+垦
+垧
+垩
+垫
+垭
+垮
+垲
+垴
+垸
+埂
+埃
+埋
+城
+埏
+埒
+埔
+埕
+埘
+埙
+埚
+埝
+域
+埠
+埤
+埭
+埯
+埴
+埸
+培
+基
+埽
+堀
+堂
+堆
+堇
+堋
+堍
+堑
+堕
+堙
+堞
+堠
+堡
+堤
+堪
+堰
+堵
+塄
+塌
+塍
+塑
+塔
+塘
+塞
+塥
+填
+塬
+塾
+墀
+墁
+境
+墅
+墉
+墒
+墓
+墙
+墚
+增
+墟
+墨
+墩
+墼
+壁
+壅
+壑
+壕
+壤
+士
+壬
+壮
+声
+壳
+壶
+壹
+处
+备
+复
+夏
+夔
+夕
+外
+夙
+多
+夜
+够
+夤
+夥
+大
+天
+太
+夫
+夭
+央
+夯
+失
+头
+夷
+夸
+夹
+夺
+夼
+奁
+奂
+奄
+奇
+奈
+奉
+奋
+奎
+奏
+契
+奔
+奕
+奖
+套
+奘
+奚
+奠
+奢
+奥
+女
+奴
+奶
+奸
+她
+好
+妁
+如
+妃
+妄
+妆
+妇
+妈
+妊
+妍
+妒
+妓
+妖
+妗
+妙
+妞
+妣
+妤
+妥
+妨
+妩
+妪
+妫
+妮
+妯
+妲
+妹
+妻
+妾
+姆
+姊
+始
+姐
+姑
+姒
+姓
+委
+姗
+姘
+姚
+姜
+姝
+姣
+姥
+姨
+姬
+姹
+姻
+姿
+威
+娃
+娄
+娅
+娆
+娇
+娈
+娉
+娌
+娑
+娓
+娘
+娜
+娟
+娠
+娣
+娥
+娩
+娱
+娲
+娴
+娶
+娼
+婀
+婆
+婉
+婊
+婕
+婚
+婢
+婧
+婪
+婴
+婵
+婶
+婷
+婺
+婿
+媒
+媚
+媛
+媪
+媲
+媳
+媵
+媸
+媾
+嫁
+嫂
+嫉
+嫌
+嫒
+嫔
+嫖
+嫘
+嫜
+嫠
+嫡
+嫣
+嫦
+嫩
+嫫
+嫱
+嬉
+嬖
+嬗
+嬲
+嬴
+嬷
+孀
+子
+孑
+孓
+孔
+孕
+字
+存
+孙
+孚
+孛
+孜
+孝
+孟
+孢
+季
+孤
+孥
+学
+孩
+孪
+孬
+孰
+孱
+孳
+孵
+孺
+孽
+宁
+它
+宄
+宅
+宇
+守
+安
+宋
+完
+宏
+宓
+宕
+宗
+官
+宙
+定
+宛
+宜
+宝
+实
+宠
+审
+客
+宣
+室
+宥
+宦
+宪
+宫
+宰
+害
+宴
+宵
+家
+宸
+容
+宽
+宾
+宿
+寂
+寄
+寅
+密
+寇
+富
+寐
+寒
+寓
+寝
+寞
+察
+寡
+寤
+寥
+寨
+寮
+寰
+寸
+对
+寺
+寻
+导
+寿
+封
+射
+将
+尉
+尊
+小
+少
+尔
+尕
+尖
+尘
+尚
+尜
+尝
+尢
+尤
+尥
+尧
+尬
+就
+尴
+尸
+尹
+尺
+尻
+尼
+尽
+尾
+尿
+局
+屁
+层
+居
+屈
+屉
+届
+屋
+屎
+屏
+屐
+屑
+展
+屙
+属
+屠
+屡
+屣
+履
+屦
+屯
+山
+屹
+屺
+屿
+岁
+岂
+岈
+岌
+岍
+岐
+岑
+岔
+岖
+岗
+岘
+岙
+岚
+岛
+岜
+岢
+岣
+岩
+岫
+岬
+岭
+岱
+岳
+岵
+岷
+岸
+岽
+岿
+峁
+峄
+峋
+峒
+峙
+峡
+峤
+峥
+峦
+峨
+峪
+峭
+峰
+峻
+崂
+崃
+崆
+崇
+崎
+崔
+崖
+崛
+崞
+崤
+崦
+崧
+崩
+崭
+崮
+崴
+崽
+崾
+嵇
+嵊
+嵋
+嵌
+嵘
+嵛
+嵝
+嵩
+嵫
+嵬
+嵯
+嵴
+嶂
+嶙
+嶝
+嶷
+巅
+巍
+巛
+川
+州
+巡
+巢
+工
+左
+巧
+巨
+巩
+巫
+差
+巯
+己
+已
+巳
+巴
+巷
+巽
+巾
+币
+市
+布
+帅
+帆
+师
+希
+帏
+帐
+帑
+帔
+帕
+帖
+帘
+帙
+帚
+帛
+帜
+帝
+带
+帧
+席
+帮
+帱
+帷
+常
+帻
+帼
+帽
+幂
+幄
+幅
+幌
+幔
+幕
+幛
+幞
+幡
+幢
+干
+平
+年
+并
+幸
+幺
+幻
+幼
+幽
+广
+庀
+庄
+庆
+庇
+床
+庋
+序
+庐
+庑
+库
+应
+底
+庖
+店
+庙
+庚
+府
+庞
+废
+庠
+庥
+度
+座
+庭
+庳
+庵
+庶
+康
+庸
+庹
+庾
+廉
+廊
+廑
+廒
+廓
+廖
+廛
+廨
+廪
+延
+廷
+建
+廾
+廿
+开
+弁
+异
+弃
+弄
+弈
+弊
+弋
+式
+弑
+弓
+引
+弗
+弘
+弛
+弟
+张
+弥
+弦
+弧
+弩
+弪
+弭
+弯
+弱
+弹
+强
+弼
+彀
+归
+当
+录
+彖
+彗
+彘
+彝
+形
+彤
+彦
+彩
+彪
+彬
+彭
+彰
+影
+彳
+彷
+役
+彻
+彼
+往
+征
+徂
+径
+待
+徇
+很
+徉
+徊
+律
+後
+徐
+徒
+徕
+得
+徘
+徙
+徜
+御
+徨
+循
+徭
+微
+徵
+德
+徼
+徽
+心
+必
+忆
+忉
+忌
+忍
+忏
+忐
+忑
+忒
+忖
+志
+忘
+忙
+忝
+忠
+忡
+忤
+忧
+忪
+快
+忭
+忮
+忱
+念
+忸
+忻
+忽
+忾
+忿
+怀
+态
+怂
+怃
+怄
+怅
+怆
+怊
+怍
+怎
+怏
+怒
+怔
+怕
+怖
+怙
+怛
+怜
+思
+怠
+怡
+急
+怦
+性
+怨
+怩
+怪
+怫
+怯
+怵
+总
+怼
+怿
+恁
+恂
+恃
+恋
+恍
+恐
+恒
+恕
+恙
+恚
+恝
+恢
+恣
+恤
+恧
+恨
+恩
+恪
+恫
+恬
+恭
+息
+恰
+恳
+恶
+恸
+恹
+恺
+恻
+恼
+恽
+恿
+悃
+悄
+悉
+悌
+悍
+悒
+悔
+悖
+悚
+悛
+悝
+悟
+悠
+患
+悦
+您
+悫
+悬
+悭
+悯
+悱
+悲
+悴
+悸
+悻
+悼
+情
+惆
+惊
+惋
+惑
+惕
+惘
+惚
+惜
+惝
+惟
+惠
+惦
+惧
+惨
+惩
+惫
+惬
+惭
+惮
+惯
+惰
+想
+惴
+惶
+惹
+惺
+愀
+愁
+愆
+愈
+愉
+愍
+愎
+意
+愕
+愚
+感
+愠
+愣
+愤
+愦
+愧
+愫
+愿
+慈
+慊
+慌
+慎
+慑
+慕
+慝
+慢
+慧
+慨
+慰
+慵
+慷
+憋
+憎
+憔
+憝
+憧
+憨
+憩
+憬
+憷
+憾
+懂
+懈
+懊
+懋
+懑
+懒
+懔
+懦
+懵
+懿
+戆
+戈
+戊
+戋
+戌
+戍
+戎
+戏
+成
+我
+戒
+戕
+或
+戗
+战
+戚
+戛
+戟
+戡
+戢
+戤
+戥
+截
+戬
+戮
+戳
+戴
+户
+戽
+戾
+房
+所
+扁
+扃
+扇
+扈
+扉
+手
+才
+扎
+扑
+扒
+打
+扔
+托
+扛
+扣
+扦
+执
+扩
+扪
+扫
+扬
+扭
+扮
+扯
+扰
+扳
+扶
+批
+扼
+找
+承
+技
+抄
+抉
+把
+抑
+抒
+抓
+投
+抖
+抗
+折
+抚
+抛
+抟
+抠
+抡
+抢
+护
+报
+抨
+披
+抬
+抱
+抵
+抹
+抻
+押
+抽
+抿
+拂
+拄
+担
+拆
+拇
+拈
+拉
+拊
+拌
+拍
+拎
+拐
+拒
+拓
+拔
+拖
+拗
+拘
+拙
+拚
+招
+拜
+拟
+拢
+拣
+拥
+拦
+拧
+拨
+择
+括
+拭
+拮
+拯
+拱
+拳
+拴
+拶
+拷
+拼
+拽
+拾
+拿
+持
+挂
+指
+挈
+按
+挎
+挑
+挖
+挚
+挛
+挝
+挞
+挟
+挠
+挡
+挢
+挣
+挤
+挥
+挨
+挪
+挫
+振
+挲
+挹
+挺
+挽
+捂
+捃
+捅
+捆
+捉
+捋
+捌
+捍
+捎
+捏
+捐
+捕
+捞
+损
+捡
+换
+捣
+捧
+捩
+捭
+据
+捱
+捶
+捷
+捺
+捻
+掀
+掂
+掇
+授
+掉
+掊
+掌
+掎
+掏
+掐
+排
+掖
+掘
+掠
+探
+掣
+接
+控
+推
+掩
+措
+掬
+掭
+掮
+掰
+掳
+掴
+掷
+掸
+掺
+掼
+掾
+揄
+揆
+揉
+揍
+揎
+描
+提
+插
+揖
+揞
+揠
+握
+揣
+揩
+揪
+揭
+揲
+援
+揶
+揸
+揽
+揿
+搀
+搁
+搂
+搅
+搋
+搌
+搏
+搐
+搓
+搔
+搛
+搜
+搞
+搠
+搡
+搦
+搪
+搬
+搭
+搴
+携
+搽
+搿
+摁
+摄
+摅
+摆
+摇
+摈
+摊
+摒
+摔
+摘
+摞
+摧
+摩
+摭
+摸
+摹
+摺
+撂
+撄
+撅
+撇
+撑
+撒
+撕
+撖
+撙
+撞
+撤
+撩
+撬
+播
+撮
+撰
+撵
+撷
+撸
+撺
+撼
+擀
+擂
+擅
+操
+擎
+擐
+擒
+擗
+擘
+擞
+擢
+擤
+擦
+攀
+攉
+攒
+攘
+攥
+攫
+攮
+支
+攴
+收
+攸
+改
+攻
+放
+政
+故
+效
+敉
+敌
+敏
+救
+敕
+敖
+教
+敛
+敝
+敞
+敢
+散
+敦
+敫
+敬
+数
+敲
+整
+敷
+文
+斋
+斌
+斐
+斑
+斓
+斗
+料
+斛
+斜
+斟
+斡
+斤
+斥
+斧
+斩
+斫
+断
+斯
+新
+方
+於
+施
+旁
+旃
+旄
+旅
+旆
+旋
+旌
+旎
+族
+旒
+旖
+旗
+无
+既
+日
+旦
+旧
+旨
+早
+旬
+旭
+旮
+旯
+旰
+旱
+时
+旷
+旺
+昀
+昂
+昃
+昆
+昊
+昌
+明
+昏
+易
+昔
+昕
+昙
+昝
+星
+映
+春
+昧
+昨
+昭
+是
+昱
+昴
+昵
+昶
+昼
+显
+晁
+晃
+晋
+晌
+晏
+晒
+晓
+晔
+晕
+晖
+晗
+晚
+晟
+晡
+晤
+晦
+晨
+普
+景
+晰
+晴
+晶
+晷
+智
+晾
+暂
+暄
+暇
+暌
+暑
+暖
+暗
+暝
+暧
+暨
+暮
+暴
+暹
+暾
+曙
+曛
+曜
+曝
+曦
+曩
+曰
+曲
+曳
+更
+曷
+曹
+曼
+曾
+替
+最
+月
+有
+朊
+朋
+服
+朐
+朔
+朕
+朗
+望
+朝
+期
+朦
+木
+未
+末
+本
+札
+术
+朱
+朴
+朵
+机
+朽
+杀
+杂
+权
+杆
+杈
+杉
+杌
+李
+杏
+材
+村
+杓
+杖
+杜
+杞
+束
+杠
+条
+来
+杨
+杩
+杪
+杭
+杯
+杰
+杲
+杳
+杵
+杷
+杼
+松
+板
+极
+构
+枇
+枉
+枋
+析
+枕
+林
+枘
+枚
+果
+枝
+枞
+枢
+枣
+枥
+枧
+枨
+枪
+枫
+枭
+枯
+枰
+枳
+枵
+架
+枷
+枸
+柁
+柃
+柄
+柏
+某
+柑
+柒
+染
+柔
+柘
+柙
+柚
+柜
+柝
+柞
+柠
+柢
+查
+柩
+柬
+柯
+柰
+柱
+柳
+柴
+柽
+柿
+栀
+栅
+标
+栈
+栉
+栊
+栋
+栌
+栎
+栏
+树
+栓
+栖
+栗
+栝
+校
+栩
+株
+栲
+栳
+样
+核
+根
+格
+栽
+栾
+桀
+桁
+桂
+桃
+桄
+桅
+框
+案
+桉
+桊
+桌
+桎
+桐
+桑
+桓
+桔
+桕
+桠
+桡
+桢
+档
+桤
+桥
+桦
+桧
+桨
+桩
+桫
+桴
+桶
+桷
+梁
+梃
+梅
+梆
+梏
+梓
+梗
+梢
+梦
+梧
+梨
+梭
+梯
+械
+梳
+梵
+检
+棂
+棉
+棋
+棍
+棒
+棕
+棘
+棚
+棠
+棣
+森
+棰
+棱
+棵
+棹
+棺
+棼
+椁
+椅
+椋
+植
+椎
+椐
+椒
+椟
+椠
+椤
+椭
+椰
+椴
+椹
+椽
+椿
+楂
+楔
+楗
+楚
+楝
+楞
+楠
+楣
+楦
+楫
+楮
+楱
+楷
+楸
+楹
+楼
+榀
+概
+榄
+榆
+榇
+榈
+榉
+榍
+榔
+榕
+榘
+榛
+榜
+榧
+榨
+榫
+榭
+榱
+榴
+榷
+榻
+槁
+槊
+槌
+槎
+槐
+槔
+槛
+槟
+槠
+槭
+槲
+槽
+槿
+樊
+樗
+樘
+樟
+模
+樨
+横
+樯
+樱
+樵
+樽
+樾
+橄
+橇
+橐
+橘
+橙
+橛
+橡
+橥
+橱
+橹
+橼
+檀
+檄
+檎
+檐
+檑
+檗
+檠
+檩
+檫
+檬
+欠
+次
+欢
+欣
+欤
+欧
+欲
+欷
+欹
+欺
+款
+歃
+歆
+歇
+歉
+歌
+歙
+止
+正
+此
+步
+武
+歧
+歪
+歹
+死
+歼
+殁
+殂
+殃
+殄
+殆
+殇
+殉
+殊
+残
+殍
+殒
+殓
+殖
+殚
+殛
+殡
+殪
+殳
+殴
+段
+殷
+殿
+毁
+毂
+毅
+毋
+母
+每
+毒
+毓
+比
+毕
+毖
+毗
+毙
+毛
+毡
+毪
+毫
+毯
+毳
+毵
+毹
+毽
+氅
+氆
+氇
+氍
+氏
+氐
+民
+氓
+气
+氕
+氖
+氘
+氙
+氚
+氛
+氟
+氡
+氢
+氤
+氦
+氧
+氨
+氩
+氪
+氮
+氯
+氰
+氲
+水
+永
+氽
+汀
+汁
+求
+汇
+汉
+汊
+汐
+汔
+汕
+汗
+汛
+汜
+汝
+汞
+江
+池
+污
+汤
+汨
+汩
+汪
+汰
+汲
+汴
+汶
+汹
+汽
+汾
+沁
+沂
+沃
+沅
+沆
+沈
+沉
+沌
+沏
+沐
+沓
+沔
+沙
+沛
+沟
+没
+没
+沣
+沤
+沥
+沦
+沧
+沩
+沪
+沫
+沭
+沮
+沱
+沲
+河
+沸
+油
+治
+沼
+沽
+沾
+沿
+泄
+泅
+泉
+泊
+泌
+泐
+泓
+泔
+法
+泖
+泗
+泛
+泞
+泠
+泡
+波
+泣
+泥
+注
+泪
+泫
+泮
+泯
+泰
+泱
+泳
+泵
+泶
+泷
+泸
+泺
+泻
+泼
+泽
+泾
+洁
+洄
+洇
+洋
+洌
+洎
+洒
+洗
+洙
+洚
+洛
+洞
+津
+洧
+洪
+洫
+洮
+洱
+洲
+洳
+洵
+洹
+活
+洼
+洽
+派
+流
+浃
+浅
+浆
+浇
+浈
+浊
+测
+浍
+济
+浏
+浑
+浒
+浓
+浔
+浙
+浚
+浜
+浞
+浠
+浣
+浦
+浩
+浪
+浮
+浯
+浴
+海
+浸
+浼
+涂
+涅
+消
+涉
+涌
+涎
+涑
+涓
+涔
+涕
+涛
+涝
+涞
+涟
+涠
+涡
+涣
+涤
+润
+涧
+涨
+涩
+涪
+涫
+涮
+涯
+液
+涵
+涸
+涿
+淀
+淄
+淅
+淆
+淇
+淋
+淌
+淑
+淖
+淘
+淙
+淝
+淞
+淠
+淡
+淤
+淦
+淫
+淬
+淮
+深
+淳
+混
+淹
+添
+淼
+清
+渊
+渌
+渍
+渎
+渐
+渑
+渔
+渖
+渗
+渚
+渝
+渠
+渡
+渣
+渤
+渥
+温
+渫
+渭
+港
+渲
+渴
+游
+渺
+湃
+湄
+湍
+湎
+湓
+湔
+湖
+湘
+湛
+湟
+湫
+湮
+湾
+湿
+溃
+溅
+溆
+溉
+溏
+源
+溘
+溜
+溟
+溢
+溥
+溧
+溪
+溯
+溱
+溲
+溴
+溶
+溷
+溺
+溻
+溽
+滁
+滂
+滇
+滋
+滏
+滑
+滓
+滔
+滕
+滗
+滚
+滞
+滟
+滠
+满
+滢
+滤
+滥
+滦
+滨
+滩
+滴
+滹
+漂
+漆
+漉
+漏
+漓
+演
+漕
+漠
+漤
+漩
+漪
+漫
+漭
+漯
+漱
+漳
+漶
+漾
+潆
+潇
+潋
+潍
+潘
+潜
+潞
+潢
+潦
+潭
+潮
+潲
+潴
+潸
+潺
+潼
+澄
+澈
+澉
+澌
+澍
+澎
+澜
+澡
+澧
+澳
+澶
+澹
+激
+濂
+濉
+濑
+濒
+濞
+濠
+濡
+濮
+濯
+瀑
+瀚
+瀛
+瀣
+瀵
+瀹
+灌
+灏
+灞
+火
+灭
+灯
+灰
+灵
+灶
+灸
+灼
+灾
+灿
+炀
+炅
+炉
+炊
+炎
+炒
+炔
+炕
+炖
+炙
+炜
+炝
+炫
+炬
+炭
+炮
+炯
+炱
+炳
+炷
+炸
+点
+炻
+炼
+炽
+烀
+烁
+烂
+烃
+烈
+烊
+烘
+烙
+烛
+烟
+烤
+烦
+烧
+烨
+烩
+烫
+烬
+热
+烯
+烷
+烹
+烽
+焉
+焊
+焐
+焓
+焕
+焖
+焘
+焙
+焚
+焦
+焯
+焰
+焱
+然
+煅
+煊
+煌
+煎
+煜
+煞
+煤
+煦
+照
+煨
+煮
+煲
+煳
+煸
+煺
+煽
+熄
+熊
+熏
+熔
+熘
+熙
+熟
+熠
+熨
+熬
+熳
+熵
+熹
+燃
+燎
+燔
+燕
+燠
+燥
+燧
+燮
+燹
+爆
+爝
+爨
+爪
+爬
+爰
+爱
+爵
+父
+爷
+爸
+爹
+爻
+爽
+爿
+片
+版
+牌
+牍
+牒
+牖
+牙
+牛
+牝
+牟
+牡
+牢
+牦
+牧
+物
+牮
+牯
+牲
+牵
+特
+牺
+牾
+牿
+犀
+犁
+犄
+犊
+犋
+犍
+犏
+犒
+犟
+犬
+犯
+犰
+犴
+状
+犷
+犸
+犹
+狁
+狂
+狃
+狄
+狈
+狍
+狎
+狐
+狒
+狗
+狙
+狞
+狠
+狡
+狨
+狩
+独
+狭
+狮
+狯
+狰
+狱
+狲
+狳
+狴
+狷
+狸
+狺
+狻
+狼
+猁
+猃
+猊
+猎
+猓
+猕
+猖
+猗
+猛
+猜
+猝
+猞
+猡
+猢
+猥
+猩
+猪
+猫
+猬
+献
+猱
+猴
+猷
+猸
+猹
+猾
+猿
+獍
+獐
+獒
+獗
+獠
+獬
+獭
+獯
+獾
+玄
+率
+玉
+王
+玎
+玑
+玖
+玛
+玟
+玢
+玩
+玫
+玮
+环
+现
+玲
+玳
+玷
+玺
+玻
+珀
+珂
+珈
+珉
+珊
+珍
+珏
+珐
+珑
+珙
+珞
+珠
+珥
+珧
+珩
+班
+珲
+球
+琅
+理
+琉
+琊
+琏
+琐
+琚
+琛
+琢
+琥
+琦
+琨
+琪
+琬
+琮
+琰
+琳
+琴
+琵
+琶
+琼
+瑁
+瑕
+瑗
+瑙
+瑚
+瑛
+瑜
+瑞
+瑟
+瑭
+瑰
+瑶
+瑷
+瑾
+璀
+璁
+璃
+璇
+璋
+璎
+璐
+璜
+璞
+璧
+璨
+璩
+璺
+瓒
+瓜
+瓞
+瓠
+瓢
+瓣
+瓤
+瓦
+瓮
+瓯
+瓴
+瓶
+瓷
+瓿
+甄
+甍
+甏
+甑
+甓
+甘
+甙
+甚
+甜
+生
+甥
+用
+甩
+甫
+甬
+甭
+甯
+田
+由
+甲
+申
+电
+男
+甸
+町
+画
+甾
+畀
+畅
+畈
+畋
+界
+畎
+畏
+畔
+留
+畚
+畛
+畜
+略
+畦
+番
+畲
+畴
+畸
+畹
+畿
+疃
+疆
+疋
+疏
+疑
+疔
+疖
+疗
+疙
+疚
+疝
+疟
+疠
+疡
+疣
+疤
+疥
+疫
+疬
+疮
+疯
+疰
+疱
+疲
+疳
+疴
+疵
+疸
+疹
+疼
+疽
+疾
+痂
+痃
+痄
+病
+症
+痈
+痉
+痊
+痍
+痒
+痔
+痕
+痖
+痘
+痛
+痞
+痢
+痣
+痤
+痦
+痧
+痨
+痪
+痫
+痰
+痱
+痴
+痹
+痼
+痿
+瘀
+瘁
+瘃
+瘅
+瘊
+瘌
+瘐
+瘕
+瘗
+瘘
+瘙
+瘛
+瘟
+瘠
+瘢
+瘤
+瘥
+瘦
+瘩
+瘪
+瘫
+瘭
+瘰
+瘳
+瘴
+瘵
+瘸
+瘼
+瘾
+瘿
+癀
+癃
+癌
+癍
+癔
+癖
+癜
+癞
+癣
+癫
+癯
+癸
+登
+白
+百
+皂
+的
+皆
+皇
+皈
+皋
+皎
+皑
+皓
+皖
+皙
+皤
+皮
+皱
+皲
+皴
+皿
+盂
+盅
+盆
+盈
+益
+盍
+盎
+盏
+盐
+监
+盒
+盔
+盖
+盗
+盘
+盛
+盟
+盥
+目
+盯
+盲
+直
+相
+盹
+盼
+盾
+省
+眄
+眇
+眈
+眉
+看
+眍
+眙
+眚
+真
+眠
+眢
+眦
+眨
+眩
+眭
+眯
+眵
+眶
+眷
+眸
+眺
+眼
+着
+睁
+睃
+睇
+睐
+睑
+睚
+睛
+睡
+睢
+督
+睥
+睦
+睨
+睫
+睬
+睹
+睽
+睾
+睿
+瞀
+瞄
+瞅
+瞌
+瞍
+瞎
+瞑
+瞒
+瞟
+瞠
+瞢
+瞥
+瞧
+瞩
+瞪
+瞬
+瞰
+瞳
+瞵
+瞻
+瞽
+瞿
+矍
+矗
+矛
+矜
+矢
+矣
+知
+矧
+矩
+矫
+矬
+短
+矮
+石
+矶
+矸
+矽
+矾
+矿
+砀
+码
+砂
+砉
+砌
+砍
+砑
+砒
+研
+砖
+砗
+砘
+砚
+砜
+砝
+砟
+砣
+砥
+砦
+砧
+砩
+砬
+砭
+砰
+破
+砷
+砸
+砹
+砺
+砻
+砼
+砾
+础
+硅
+硇
+硌
+硎
+硐
+硒
+硕
+硖
+硗
+硝
+硪
+硫
+硬
+硭
+确
+硷
+硼
+碇
+碉
+碌
+碍
+碎
+碑
+碓
+碗
+碘
+碚
+碛
+碜
+碟
+碡
+碣
+碥
+碧
+碰
+碱
+碲
+碳
+碴
+碹
+碾
+磁
+磅
+磉
+磊
+磋
+磐
+磔
+磕
+磙
+磨
+磬
+磲
+磴
+磷
+磺
+礁
+礅
+礓
+礞
+礤
+礴
+示
+礼
+社
+祀
+祁
+祆
+祈
+祉
+祓
+祖
+祗
+祚
+祛
+祜
+祝
+神
+祟
+祠
+祢
+祥
+祧
+票
+祭
+祯
+祷
+祸
+祺
+禀
+禁
+禄
+禅
+禊
+福
+禚
+禧
+禳
+禹
+禺
+离
+禽
+禾
+秀
+私
+秃
+秆
+秉
+秋
+种
+科
+秒
+秕
+秘
+租
+秣
+秤
+秦
+秧
+秩
+秫
+秭
+积
+称
+秸
+移
+秽
+稀
+稂
+稃
+稆
+程
+稍
+税
+稔
+稗
+稚
+稞
+稠
+稣
+稳
+稷
+稹
+稻
+稼
+稽
+稿
+穆
+穑
+穗
+穰
+穴
+究
+穷
+穸
+穹
+空
+穿
+窀
+突
+窃
+窄
+窆
+窈
+窍
+窑
+窒
+窕
+窖
+窗
+窘
+窜
+窝
+窟
+窠
+窥
+窦
+窨
+窬
+窭
+窳
+窿
+立
+竖
+站
+竞
+竟
+章
+竣
+童
+竦
+竭
+端
+竹
+竺
+竽
+竿
+笃
+笄
+笆
+笈
+笊
+笋
+笏
+笑
+笔
+笕
+笙
+笛
+笞
+笠
+笤
+笥
+符
+笨
+笪
+笫
+第
+笮
+笱
+笳
+笸
+笺
+笼
+笾
+筅
+等
+筋
+筌
+筏
+筐
+筑
+筒
+答
+策
+筘
+筚
+筛
+筝
+筠
+筢
+筮
+筱
+筲
+筵
+筷
+筹
+筻
+签
+简
+箅
+箍
+箐
+箔
+箕
+算
+箜
+箝
+管
+箢
+箦
+箧
+箨
+箩
+箪
+箫
+箬
+箭
+箱
+箴
+箸
+篁
+篆
+篇
+篌
+篑
+篓
+篙
+篚
+篝
+篡
+篥
+篦
+篪
+篮
+篱
+篷
+篼
+篾
+簇
+簋
+簌
+簏
+簖
+簟
+簦
+簧
+簪
+簸
+簿
+籀
+籁
+籍
+米
+籴
+类
+籼
+籽
+粉
+粑
+粒
+粕
+粗
+粘
+粜
+粝
+粞
+粟
+粢
+粤
+粥
+粪
+粮
+粱
+粲
+粳
+粹
+粼
+粽
+精
+糁
+糅
+糇
+糈
+糊
+糌
+糍
+糕
+糖
+糗
+糙
+糜
+糟
+糠
+糨
+糯
+糸
+系
+紊
+素
+索
+紧
+紫
+累
+絮
+絷
+綦
+綮
+縻
+繁
+繇
+纂
+纛
+纠
+纡
+红
+纣
+纤
+纥
+约
+级
+纨
+纩
+纪
+纫
+纬
+纭
+纯
+纰
+纱
+纲
+纳
+纵
+纶
+纷
+纸
+纹
+纺
+纽
+纾
+线
+绀
+绁
+绂
+练
+组
+绅
+细
+织
+终
+绉
+绊
+绋
+绌
+绍
+绎
+经
+绐
+绑
+绒
+结
+绔
+绕
+绗
+绘
+给
+绚
+绛
+络
+绝
+绞
+统
+绠
+绡
+绢
+绣
+绥
+绦
+继
+绨
+绩
+绪
+绫
+续
+绮
+绯
+绰
+绱
+绲
+绳
+维
+绵
+绶
+绷
+绸
+绺
+绻
+综
+绽
+绾
+绿
+缀
+缁
+缂
+缃
+缄
+缅
+缆
+缇
+缈
+缉
+缋
+缌
+缍
+缎
+缏
+缑
+缒
+缓
+缔
+缕
+编
+缗
+缘
+缙
+缚
+缛
+缜
+缝
+缟
+缠
+缡
+缢
+缣
+缤
+缥
+缦
+缧
+缨
+缩
+缪
+缫
+缬
+缭
+缮
+缯
+缰
+缱
+缲
+缳
+缴
+缵
+缶
+缸
+缺
+罂
+罄
+罅
+罐
+网
+罔
+罕
+罗
+罘
+罚
+罟
+罡
+罢
+罨
+罩
+罪
+置
+罱
+署
+罴
+罹
+罾
+羁
+羊
+羌
+美
+羔
+羚
+羝
+羞
+羟
+羡
+群
+羧
+羯
+羰
+羲
+羸
+羹
+羼
+羽
+羿
+翁
+翅
+翊
+翌
+翎
+翔
+翕
+翘
+翟
+翠
+翡
+翥
+翦
+翩
+翮
+翰
+翱
+翳
+翻
+翼
+耀
+老
+考
+耄
+者
+耆
+耋
+而
+耍
+耐
+耒
+耔
+耕
+耖
+耗
+耘
+耙
+耜
+耠
+耢
+耥
+耦
+耧
+耨
+耩
+耪
+耱
+耳
+耵
+耶
+耷
+耸
+耻
+耽
+耿
+聂
+聃
+聆
+聊
+聋
+职
+聍
+聒
+联
+聘
+聚
+聩
+聪
+聱
+聿
+肃
+肄
+肆
+肇
+肉
+肋
+肌
+肓
+肖
+肘
+肚
+肛
+肜
+肝
+肟
+肠
+股
+肢
+肤
+肥
+肩
+肪
+肫
+肭
+肮
+肯
+肱
+育
+肴
+肷
+肺
+肼
+肽
+肾
+肿
+胀
+胁
+胂
+胃
+胄
+胆
+背
+胍
+胎
+胖
+胗
+胙
+胚
+胛
+胜
+胝
+胞
+胡
+胤
+胥
+胧
+胨
+胩
+胪
+胫
+胬
+胭
+胯
+胰
+胱
+胲
+胳
+胴
+胶
+胸
+胺
+胼
+能
+脂
+脆
+脉
+脊
+脍
+脎
+脏
+脐
+脑
+脒
+脓
+脔
+脖
+脘
+脚
+脞
+脬
+脯
+脱
+脲
+脶
+脸
+脾
+腆
+腈
+腊
+腋
+腌
+腐
+腑
+腓
+腔
+腕
+腙
+腚
+腠
+腥
+腧
+腩
+腭
+腮
+腰
+腱
+腴
+腹
+腺
+腻
+腼
+腽
+腾
+腿
+膀
+膂
+膈
+膊
+膏
+膑
+膘
+膛
+膜
+膝
+膣
+膦
+膨
+膪
+膳
+膺
+膻
+臀
+臁
+臂
+臃
+臆
+臊
+臌
+臣
+臧
+自
+臬
+臭
+至
+致
+臻
+臼
+臾
+舀
+舁
+舂
+舄
+舅
+舆
+舌
+舍
+舐
+舒
+舔
+舛
+舜
+舞
+舟
+舡
+舢
+舣
+舨
+航
+舫
+般
+舭
+舯
+舰
+舱
+舳
+舴
+舵
+舶
+舷
+舸
+船
+舻
+舾
+艄
+艇
+艉
+艋
+艏
+艘
+艚
+艟
+艨
+艮
+良
+艰
+色
+艳
+艴
+艺
+艽
+艾
+艿
+节
+芄
+芈
+芊
+芋
+芍
+芎
+芏
+芑
+芒
+芗
+芘
+芙
+芜
+芝
+芟
+芡
+芤
+芥
+芦
+芨
+芩
+芪
+芫
+芬
+芭
+芮
+芯
+芰
+花
+芳
+芴
+芷
+芸
+芹
+芽
+芾
+苁
+苄
+苇
+苈
+苊
+苋
+苌
+苍
+苎
+苏
+苑
+苒
+苓
+苔
+苕
+苗
+苘
+苛
+苜
+苞
+苟
+苠
+苡
+苣
+苤
+若
+苦
+苫
+苯
+英
+苴
+苷
+苹
+苻
+茁
+茂
+范
+茄
+茅
+茆
+茇
+茈
+茉
+茌
+茎
+茏
+茑
+茔
+茕
+茗
+茚
+茛
+茜
+茧
+茨
+茫
+茬
+茭
+茯
+茱
+茳
+茴
+茵
+茶
+茸
+茹
+茺
+茼
+荀
+荃
+荆
+荇
+草
+荏
+荐
+荑
+荒
+荔
+荚
+荛
+荜
+荞
+荟
+荠
+荡
+荣
+荤
+荥
+荦
+荧
+荨
+荩
+荪
+荫
+荬
+荭
+荮
+药
+荷
+荸
+荻
+荼
+荽
+莅
+莆
+莉
+莎
+莒
+莓
+莘
+莛
+莜
+莞
+莠
+莨
+莩
+莪
+莫
+莰
+莱
+莲
+莳
+莴
+莶
+获
+莸
+莹
+莺
+莼
+莽
+菀
+菁
+菅
+菇
+菊
+菌
+菏
+菔
+菖
+菘
+菜
+菝
+菟
+菠
+菡
+菥
+菩
+菪
+菰
+菱
+菲
+菸
+菹
+菽
+萁
+萃
+萄
+萆
+萋
+萌
+萍
+萎
+萏
+萑
+萘
+萜
+萝
+萤
+营
+萦
+萧
+萨
+萱
+萸
+萼
+落
+葆
+葑
+著
+葙
+葚
+葛
+葜
+葡
+董
+葩
+葫
+葬
+葭
+葱
+葳
+葵
+葶
+葸
+葺
+蒂
+蒇
+蒈
+蒉
+蒋
+蒌
+蒎
+蒗
+蒙
+蒜
+蒡
+蒯
+蒲
+蒴
+蒸
+蒹
+蒺
+蒽
+蒿
+蓁
+蓄
+蓉
+蓊
+蓍
+蓐
+蓑
+蓓
+蓖
+蓝
+蓟
+蓠
+蓣
+蓥
+蓦
+蓬
+蓰
+蓼
+蓿
+蔌
+蔑
+蔓
+蔗
+蔚
+蔟
+蔡
+蔫
+蔬
+蔷
+蔸
+蔹
+蔺
+蔻
+蔼
+蔽
+蕃
+蕈
+蕉
+蕊
+蕖
+蕙
+蕞
+蕤
+蕨
+蕲
+蕴
+蕹
+蕺
+蕻
+蕾
+薄
+薅
+薇
+薏
+薛
+薜
+薤
+薨
+薪
+薮
+薯
+薰
+薷
+薹
+藁
+藉
+藏
+藐
+藓
+藕
+藜
+藤
+藩
+藻
+藿
+蘅
+蘑
+蘖
+蘧
+蘩
+蘸
+蘼
+虍
+虎
+虏
+虐
+虑
+虚
+虞
+虢
+虫
+虬
+虮
+虱
+虹
+虺
+虻
+虼
+虽
+虾
+虿
+蚀
+蚁
+蚂
+蚊
+蚋
+蚌
+蚍
+蚓
+蚕
+蚜
+蚝
+蚣
+蚤
+蚧
+蚨
+蚩
+蚪
+蚬
+蚯
+蚰
+蚱
+蚴
+蚵
+蚶
+蚺
+蛀
+蛄
+蛆
+蛇
+蛉
+蛊
+蛋
+蛎
+蛏
+蛐
+蛑
+蛔
+蛘
+蛙
+蛛
+蛞
+蛟
+蛤
+蛩
+蛭
+蛮
+蛰
+蛱
+蛲
+蛳
+蛴
+蛸
+蛹
+蛾
+蜀
+蜂
+蜃
+蜇
+蜈
+蜉
+蜊
+蜍
+蜒
+蜓
+蜕
+蜗
+蜘
+蜚
+蜜
+蜞
+蜡
+蜢
+蜣
+蜥
+蜩
+蜮
+蜱
+蜴
+蜷
+蜻
+蜾
+蜿
+蝇
+蝈
+蝉
+蝌
+蝎
+蝓
+蝗
+蝙
+蝠
+蝣
+蝤
+蝥
+蝮
+蝰
+蝴
+蝶
+蝻
+蝼
+蝽
+蝾
+螂
+螃
+螅
+螈
+螋
+融
+螓
+螗
+螟
+螨
+螫
+螬
+螭
+螯
+螳
+螵
+螺
+螽
+蟀
+蟆
+蟊
+蟋
+蟑
+蟒
+蟓
+蟛
+蟠
+蟥
+蟪
+蟮
+蟹
+蟾
+蠃
+蠊
+蠓
+蠕
+蠖
+蠛
+蠡
+蠢
+蠲
+蠹
+蠼
+血
+衄
+衅
+行
+衍
+衔
+街
+衙
+衡
+衢
+衣
+补
+表
+衩
+衫
+衬
+衮
+衰
+衲
+衷
+衽
+衾
+衿
+袁
+袂
+袄
+袅
+袈
+袋
+袍
+袒
+袖
+袜
+袢
+袤
+被
+袭
+袱
+袷
+袼
+裁
+裂
+装
+裆
+裉
+裎
+裒
+裔
+裕
+裘
+裙
+裟
+裢
+裣
+裤
+裥
+裨
+裰
+裱
+裳
+裴
+裸
+裹
+裼
+裾
+褂
+褊
+褐
+褒
+褓
+褙
+褚
+褛
+褡
+褥
+褪
+褫
+褰
+褴
+褶
+襁
+襄
+襞
+襟
+襦
+襻
+西
+要
+覃
+覆
+见
+观
+规
+觅
+视
+觇
+览
+觉
+觊
+觋
+觌
+觎
+觏
+觐
+觑
+角
+觖
+觚
+觜
+觞
+解
+觥
+触
+觫
+觯
+觳
+言
+訇
+訾
+詈
+詹
+誉
+誊
+誓
+謇
+謦
+警
+譬
+计
+订
+讣
+认
+讥
+讦
+讧
+讨
+让
+讪
+讫
+训
+议
+讯
+记
+讲
+讳
+讴
+讵
+讶
+讷
+许
+讹
+论
+讼
+讽
+设
+访
+诀
+证
+诂
+诃
+评
+诅
+识
+诈
+诉
+诊
+诋
+诌
+词
+诎
+诏
+译
+诒
+诓
+诔
+试
+诖
+诗
+诘
+诙
+诚
+诛
+诜
+话
+诞
+诟
+诠
+诡
+询
+诣
+诤
+该
+详
+诧
+诨
+诩
+诫
+诬
+语
+诮
+误
+诰
+诱
+诲
+诳
+说
+诵
+诶
+请
+诸
+诹
+诺
+读
+诼
+诽
+课
+诿
+谀
+谁
+谂
+调
+谄
+谅
+谆
+谇
+谈
+谊
+谋
+谌
+谍
+谎
+谏
+谐
+谑
+谒
+谓
+谔
+谕
+谖
+谗
+谘
+谙
+谚
+谛
+谜
+谝
+谟
+谠
+谡
+谢
+谣
+谤
+谥
+谦
+谧
+谨
+谩
+谪
+谫
+谬
+谭
+谮
+谯
+谰
+谱
+谲
+谳
+谴
+谵
+谶
+谷
+豁
+豆
+豇
+豉
+豌
+豕
+豚
+象
+豢
+豪
+豫
+豳
+豸
+豹
+豺
+貂
+貅
+貉
+貊
+貌
+貔
+貘
+贝
+贞
+负
+贡
+财
+责
+贤
+败
+账
+货
+质
+贩
+贪
+贫
+贬
+购
+贮
+贯
+贰
+贱
+贲
+贳
+贴
+贵
+贶
+贷
+贸
+费
+贺
+贻
+贼
+贽
+贾
+贿
+赀
+赁
+赂
+赃
+资
+赅
+赆
+赇
+赈
+赉
+赊
+赋
+赌
+赍
+赎
+赏
+赐
+赓
+赔
+赕
+赖
+赘
+赙
+赚
+赛
+赜
+赝
+赞
+赠
+赡
+赢
+赣
+赤
+赦
+赧
+赫
+赭
+走
+赳
+赴
+赵
+赶
+起
+趁
+趄
+超
+越
+趋
+趑
+趔
+趟
+趣
+趱
+足
+趴
+趵
+趸
+趺
+趼
+趾
+趿
+跃
+跄
+跆
+跋
+跌
+跎
+跏
+跑
+跖
+跗
+跚
+跛
+距
+跞
+跟
+跣
+跤
+跨
+跪
+跫
+跬
+路
+跳
+践
+跷
+跸
+跹
+跺
+跻
+跽
+踅
+踉
+踊
+踌
+踏
+踔
+踝
+踞
+踟
+踢
+踣
+踩
+踪
+踬
+踮
+踯
+踱
+踵
+踹
+踺
+踽
+蹀
+蹁
+蹂
+蹄
+蹇
+蹈
+蹉
+蹊
+蹋
+蹑
+蹒
+蹙
+蹦
+蹩
+蹬
+蹭
+蹯
+蹰
+蹲
+蹴
+蹶
+蹼
+蹿
+躁
+躅
+躇
+躏
+躐
+躔
+躜
+躞
+身
+躬
+躯
+躲
+躺
+軎
+车
+轧
+轨
+轩
+轫
+转
+轭
+轮
+软
+轰
+轱
+轲
+轳
+轴
+轵
+轶
+轷
+轸
+轹
+轺
+轻
+轼
+载
+轾
+轿
+辁
+辂
+较
+辄
+辅
+辆
+辇
+辈
+辉
+辊
+辋
+辍
+辎
+辏
+辐
+辑
+输
+辔
+辕
+辖
+辗
+辘
+辙
+辚
+辛
+辜
+辞
+辟
+辣
+辨
+辩
+辫
+辰
+辱
+边
+辽
+达
+迁
+迂
+迄
+迅
+过
+迈
+迎
+运
+近
+迓
+返
+迕
+还
+这
+进
+远
+违
+连
+迟
+迢
+迤
+迥
+迦
+迨
+迩
+迪
+迫
+迭
+迮
+述
+迳
+迷
+迸
+迹
+追
+退
+送
+适
+逃
+逄
+逅
+逆
+选
+逊
+逋
+逍
+透
+逐
+逑
+递
+途
+逖
+逗
+通
+逛
+逝
+逞
+速
+造
+逡
+逢
+逦
+逭
+逮
+逯
+逵
+逶
+逸
+逻
+逼
+逾
+遁
+遂
+遄
+遇
+遍
+遏
+遐
+遑
+遒
+道
+遗
+遘
+遛
+遢
+遣
+遥
+遨
+遭
+遮
+遴
+遵
+遽
+避
+邀
+邂
+邃
+邈
+邋
+邑
+邓
+邕
+邗
+邙
+邛
+邝
+邡
+邢
+那
+邦
+邪
+邬
+邮
+邯
+邰
+邱
+邳
+邴
+邵
+邶
+邸
+邹
+邺
+邻
+邾
+郁
+郄
+郅
+郇
+郊
+郎
+郏
+郐
+郑
+郓
+郗
+郛
+郜
+郝
+郡
+郢
+郦
+郧
+部
+郫
+郭
+郯
+郴
+郸
+都
+郾
+鄂
+鄄
+鄙
+鄞
+鄢
+鄣
+鄯
+鄱
+鄹
+酃
+酆
+酉
+酊
+酋
+酌
+配
+酎
+酏
+酐
+酒
+酗
+酚
+酝
+酞
+酡
+酢
+酣
+酤
+酥
+酩
+酪
+酬
+酮
+酯
+酰
+酱
+酲
+酴
+酵
+酶
+酷
+酸
+酹
+酽
+酾
+酿
+醅
+醇
+醉
+醋
+醌
+醍
+醐
+醑
+醒
+醚
+醛
+醢
+醣
+醪
+醭
+醮
+醯
+醴
+醵
+醺
+采
+釉
+释
+里
+重
+野
+量
+金
+釜
+鉴
+銎
+銮
+鋈
+錾
+鍪
+鎏
+鏊
+鏖
+鐾
+鑫
+钆
+钇
+针
+钉
+钊
+钋
+钌
+钍
+钎
+钏
+钐
+钒
+钓
+钔
+钕
+钗
+钙
+钚
+钛
+钜
+钝
+钞
+钟
+钠
+钡
+钢
+钣
+钤
+钥
+钦
+钧
+钨
+钩
+钪
+钫
+钬
+钭
+钮
+钯
+钰
+钱
+钲
+钳
+钴
+钵
+钶
+钷
+钸
+钹
+钺
+钻
+钼
+钽
+钾
+钿
+铀
+铁
+铂
+铃
+铄
+铅
+铆
+铈
+铉
+铊
+铋
+铌
+铍
+铎
+铐
+铑
+铒
+铕
+铖
+铗
+铘
+铙
+铛
+铜
+铝
+铞
+铟
+铠
+铡
+铢
+铣
+铤
+铥
+铧
+铨
+铩
+铪
+铫
+铬
+铭
+铮
+铯
+铰
+铱
+铲
+铳
+铴
+铵
+银
+铷
+铸
+铹
+铺
+铼
+铽
+链
+铿
+销
+锁
+锂
+锃
+锄
+锅
+锆
+锇
+锈
+锉
+锊
+锋
+锌
+锍
+锎
+锏
+锐
+锑
+锒
+锓
+锔
+锕
+锖
+锗
+锘
+错
+锚
+锛
+锝
+锞
+锟
+锡
+锢
+锣
+锤
+锥
+锦
+锨
+锩
+锪
+锫
+锬
+锭
+键
+锯
+锰
+锱
+锲
+锴
+锵
+锶
+锷
+锸
+锹
+锺
+锻
+锼
+锾
+锿
+镀
+镁
+镂
+镄
+镅
+镆
+镇
+镉
+镊
+镌
+镍
+镎
+镏
+镐
+镑
+镒
+镓
+镔
+镖
+镗
+镘
+镙
+镛
+镜
+镝
+镞
+镟
+镡
+镢
+镣
+镤
+镥
+镦
+镧
+镨
+镩
+镪
+镫
+镬
+镭
+镯
+镰
+镱
+镲
+镳
+镶
+长
+门
+闩
+闪
+闫
+闭
+问
+闯
+闰
+闱
+闲
+闳
+间
+闵
+闶
+闷
+闸
+闹
+闺
+闻
+闼
+闽
+闾
+阀
+阁
+阂
+阃
+阄
+阅
+阆
+阈
+阉
+阊
+阋
+阌
+阍
+阎
+阏
+阐
+阑
+阒
+阔
+阕
+阖
+阗
+阙
+阚
+阜
+队
+阡
+阢
+阪
+阮
+阱
+防
+阳
+阴
+阵
+阶
+阻
+阼
+阽
+阿
+陀
+陂
+附
+际
+陆
+陇
+陈
+陉
+陋
+陌
+降
+限
+陔
+陕
+陛
+陟
+陡
+院
+除
+陧
+陨
+险
+陪
+陬
+陲
+陴
+陵
+陶
+陷
+隅
+隆
+隈
+隋
+隍
+随
+隐
+隔
+隗
+隘
+隙
+障
+隧
+隰
+隳
+隶
+隹
+隼
+隽
+难
+雀
+雁
+雄
+雅
+集
+雇
+雉
+雌
+雍
+雎
+雏
+雒
+雕
+雠
+雨
+雩
+雪
+雯
+雳
+零
+雷
+雹
+雾
+需
+霁
+霄
+霆
+震
+霈
+霉
+霍
+霎
+霏
+霓
+霖
+霜
+霞
+霪
+霭
+霰
+露
+霸
+霹
+霾
+青
+靓
+靖
+静
+靛
+非
+靠
+靡
+面
+靥
+革
+靳
+靴
+靶
+靼
+鞅
+鞋
+鞍
+鞑
+鞒
+鞔
+鞘
+鞠
+鞣
+鞫
+鞭
+鞯
+鞲
+鞴
+韦
+韧
+韩
+韪
+韫
+韬
+韭
+音
+韵
+韶
+页
+顶
+顷
+顸
+项
+顺
+须
+顼
+顽
+顾
+顿
+颀
+颁
+颂
+颃
+预
+颅
+领
+颇
+颈
+颉
+颊
+颌
+颍
+颏
+颐
+频
+颓
+颔
+颖
+颗
+题
+颚
+颛
+颜
+额
+颞
+颟
+颠
+颡
+颢
+颤
+颥
+颦
+颧
+风
+飑
+飒
+飓
+飕
+飘
+飙
+飚
+飞
+食
+飧
+飨
+餍
+餐
+餮
+饔
+饕
+饥
+饧
+饨
+饩
+饪
+饫
+饬
+饭
+饮
+饯
+饰
+饱
+饲
+饴
+饵
+饶
+饷
+饺
+饼
+饽
+饿
+馀
+馁
+馄
+馅
+馆
+馇
+馈
+馊
+馋
+馍
+馏
+馐
+馑
+馒
+馓
+馔
+馕
+首
+馗
+馘
+香
+馥
+馨
+马
+驭
+驮
+驯
+驰
+驱
+驳
+驴
+驵
+驶
+驷
+驸
+驹
+驺
+驻
+驼
+驽
+驾
+驿
+骀
+骁
+骂
+骄
+骅
+骆
+骇
+骈
+骊
+骋
+验
+骏
+骐
+骑
+骒
+骓
+骖
+骗
+骘
+骚
+骛
+骜
+骝
+骞
+骟
+骠
+骡
+骢
+骣
+骤
+骥
+骧
+骨
+骰
+骱
+骶
+骷
+骸
+骺
+骼
+髀
+髁
+髂
+髅
+髋
+髌
+髑
+髓
+高
+髟
+髡
+髦
+髫
+髭
+髯
+髹
+髻
+鬃
+鬈
+鬏
+鬓
+鬟
+鬣
+鬯
+鬲
+鬻
+鬼
+魁
+魂
+魃
+魄
+魅
+魇
+魈
+魉
+魍
+魏
+魑
+魔
+鱼
+鱿
+鲁
+鲂
+鲅
+鲆
+鲇
+鲈
+鲋
+鲍
+鲎
+鲐
+鲑
+鲒
+鲔
+鲕
+鲚
+鲛
+鲜
+鲞
+鲟
+鲠
+鲡
+鲢
+鲣
+鲤
+鲥
+鲦
+鲧
+鲨
+鲩
+鲫
+鲭
+鲮
+鲰
+鲱
+鲲
+鲳
+鲴
+鲵
+鲶
+鲷
+鲸
+鲺
+鲻
+鲼
+鲽
+鳃
+鳄
+鳅
+鳆
+鳇
+鳊
+鳋
+鳌
+鳍
+鳎
+鳏
+鳐
+鳓
+鳔
+鳕
+鳖
+鳗
+鳘
+鳙
+鳜
+鳝
+鳞
+鳟
+鳢
+鸟
+鸠
+鸡
+鸢
+鸣
+鸥
+鸦
+鸨
+鸩
+鸪
+鸫
+鸬
+鸭
+鸯
+鸱
+鸲
+鸳
+鸵
+鸶
+鸷
+鸸
+鸹
+鸺
+鸽
+鸾
+鸿
+鹁
+鹂
+鹃
+鹄
+鹅
+鹆
+鹇
+鹈
+鹉
+鹊
+鹋
+鹌
+鹎
+鹏
+鹑
+鹕
+鹗
+鹘
+鹚
+鹛
+鹜
+鹞
+鹣
+鹤
+鹦
+鹧
+鹨
+鹩
+鹪
+鹫
+鹬
+鹭
+鹰
+鹱
+鹳
+鹾
+鹿
+麂
+麇
+麈
+麋
+麒
+麓
+麝
+麟
+麦
+麴
+麸
+麻
+麽
+麾
+黄
+黉
+黍
+黎
+黏
+黑
+黔
+默
+黛
+黜
+黝
+黟
+黠
+黢
+黥
+黧
+黩
+黪
+黯
+黹
+黻
+黼
+黾
+鼋
+鼍
+鼎
+鼐
+鼓
+鼗
+鼙
+鼠
+鼢
+鼬
+鼯
+鼷
+鼹
+鼻
+鼽
+鼾
+齄
+齐
+齑
+齿
+龀
+龃
+龄
+龅
+龆
+龇
+龈
+龉
+龊
+龋
+龌
+龙
+龚
+龛
+龟
+龠
\ No newline at end of file
diff --git a/kaiFontDatasetGeneration/kai_font_radicals_segment.py b/kaiFontDatasetGeneration/kai_font_radicals_segment.py
new file mode 100644
index 0000000..ecf2443
--- /dev/null
+++ b/kaiFontDatasetGeneration/kai_font_radicals_segment.py
@@ -0,0 +1,46 @@
+# coding: utf-8
+
+import os
+import cv2
+import numpy as np
+
+from utils.Functions import splitConnectedComponents
+
+
+def main():
+ charset_dir = "../simkaidataset6958"
+
+ char_list = []
+ char_names = []
+ for fl in os.listdir(charset_dir):
+ if ".jpg" in fl:
+ char_list.append(fl)
+ char_names.append(os.path.splitext(fl)[0])
+
+ print(char_list)
+ print(char_names)
+
+ radicals_path = charset_dir + "/radicals"
+ strokes_path = charset_dir + "/strokes"
+
+ for i in range(len(char_list)):
+ print("index: %d" % i)
+ f_path = os.path.join(charset_dir, char_list[i])
+ f_name = char_names[i]
+
+ img_gray = cv2.imread(f_path, 0)
+ _, img_gray = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)
+
+ boxes = splitConnectedComponents(img_gray)
+ print(len(boxes))
+
+ radical_path = str(os.path.join(radicals_path, f_name))
+ if not os.path.exists(radical_path):
+ os.mkdir(radical_path)
+ for bi in range(len(boxes)):
+ r_path = radical_path + "/radicals_" + str(bi) + ".jpg"
+ cv2.imwrite(r_path, boxes[bi])
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/kaiFontDatasetGeneration/kai_stroke_extract_test.py b/kaiFontDatasetGeneration/kai_stroke_extract_test.py
new file mode 100644
index 0000000..1144f98
--- /dev/null
+++ b/kaiFontDatasetGeneration/kai_stroke_extract_test.py
@@ -0,0 +1,70 @@
+# coding: utf-8
+import cv2
+import numpy as np
+from skimage.feature import match_template
+from matplotlib import pyplot as plt
+
+from utils.Functions import getContourOfImage, sortPointsOnContourOfImage, getSingleMaxBoundingBoxOfImage
+
+
+def main():
+ img_path = "0001ding.jpg"
+ template_path = "0001ding_stroke.jpg"
+
+ img = cv2.imread(img_path, 0)
+ temp_img = cv2.imread(template_path, 0)
+
+ _, img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
+ _, temp_img = cv2.threshold(temp_img, 127, 255, cv2.THRESH_BINARY)
+
+ img_rgb = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
+
+ temp_x, temp_y, temp_w, temp_h = getSingleMaxBoundingBoxOfImage(temp_img)
+
+ temp_img = temp_img[temp_x:temp_x+temp_w, temp_y:temp_y+temp_h]
+
+ # All the 6 methods for comparison in a list
+ methods = ['cv2.TM_CCOEFF', 'cv2.TM_CCOEFF_NORMED', 'cv2.TM_CCORR',
+ 'cv2.TM_CCORR_NORMED', 'cv2.TM_SQDIFF', 'cv2.TM_SQDIFF_NORMED']
+
+ for meth in methods:
+ img_ = img.copy()
+ method = eval(meth)
+
+ res = cv2.matchTemplate(img_, temp_img, method)
+
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
+
+ # If the method is TM_SQDIFF or TM_SQDIFF_NORMED, take minimum
+ if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
+ top_left = min_loc
+ else:
+ top_left = max_loc
+ bottom_right = (top_left[0] + temp_w, top_left[1] + temp_h)
+
+ cv2.rectangle(img_rgb, top_left, bottom_right, (0,0,255), 2)
+
+ plt.subplot(121), plt.imshow(res, cmap='gray')
+ plt.title('Matching Result'), plt.xticks([]), plt.yticks([])
+ plt.subplot(122), plt.imshow(img_rgb)
+ plt.title('Detected Point'), plt.xticks([]), plt.yticks([])
+ plt.suptitle(meth)
+
+ plt.show()
+
+ # contour = getContourOfImage(img)
+ #
+ # # sorted the contour with clockwise driection
+ # contour_sorted = sortPointsOnContourOfImage(contour)
+ # print(contour_sorted)
+
+
+ # cv2.imshow("src", img)
+ # # cv2.imshow("contour", contour)
+ #
+ # cv2.waitKey(0)
+ # cv2.destroyAllWindows()
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/kaiFontDatasetGeneration/simkai.ttf b/kaiFontDatasetGeneration/simkai.ttf
new file mode 100755
index 0000000..1aa018b
Binary files /dev/null and b/kaiFontDatasetGeneration/simkai.ttf differ
diff --git a/manuallyStrokeExtractingTool.py b/manuallyStrokeExtractingTool.py
deleted file mode 100644
index bb65847..0000000
--- a/manuallyStrokeExtractingTool.py
+++ /dev/null
@@ -1,191 +0,0 @@
-from PyQt5.QtCore import QDir, Qt, QPoint
-from PyQt5.QtGui import QImage, QPainter, QPalette, QPixmap
-from PyQt5.QtWidgets import (QAction, QApplication, QFileDialog, QLabel,
- QMainWindow, QMenu, QMessageBox, QScrollArea, QSizePolicy)
-from PyQt5.QtPrintSupport import QPrintDialog, QPrinter
-
-
-class ImageViewer(QMainWindow):
- def __init__(self):
- super(ImageViewer, self).__init__()
-
- self.printer = QPrinter()
- self.scaleFactor = 0.0
-
- self.lastPoint = QPoint()
- self.endPoint = QPoint()
-
- self.imagePix = QPixmap()
- self.imagePix.fill(Qt.white)
-
- self.imageLabel = QLabel()
- self.imageLabel.setBackgroundRole(QPalette.Base)
- self.imageLabel.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
- self.imageLabel.setScaledContents(True)
- self.imageLabel.setPixmap(self.imagePix)
- self.imageLabel.mousePressEvent = self.getPos
-
- self.scrollArea = QScrollArea()
- self.scrollArea.setBackgroundRole(QPalette.Dark)
- self.scrollArea.setWidget(self.imageLabel)
- self.setCentralWidget(self.scrollArea)
-
- self.createActions()
- self.createMenus()
-
- self.setWindowTitle("Image Viewer")
- self.resize(1000, 800)
-
- def mousePressEvent(self, event):
- if event.button() == Qt.LeftButton:
- self.lastPoint = event.pos()
- self.endPoint = self.lastPoint
- self.pos = event.pos()
-
- def paintEvent(self, event):
-
- painter = QPainter(self)
-
- def drawPoints(self, painter):
- painter.setPen(Qt.red)
- size = self.size()
-
-
- def getPos(self, event):
- x = event.pos().x()
- y = event.pos().y()
- print((x, y))
-
- def open(self):
- fileName, _ = QFileDialog.getOpenFileName(self, "Open File",
- QDir.currentPath())
- if fileName:
- image = QImage(fileName)
- if image.isNull():
- QMessageBox.information(self, "Image Viewer",
- "Cannot load %s." % fileName)
- return
-
- # self.imageLabel.setPixmap(QPixmap.fromImage(image))
- self.imagePix = QPixmap.fromImage(image)
- self.imageLabel.setPixmap(self.imagePix)
- self.scaleFactor = 1.0
-
- self.printAct.setEnabled(True)
- self.fitToWindowAct.setEnabled(True)
- self.updateActions()
-
- if not self.fitToWindowAct.isChecked():
- self.imageLabel.adjustSize()
-
- def print_(self):
- dialog = QPrintDialog(self.printer, self)
- if dialog.exec_():
- painter = QPainter(self.printer)
- rect = painter.viewport()
- size = self.imageLabel.pixmap().size()
- size.scale(rect.size(), Qt.KeepAspectRatio)
- painter.setViewport(rect.x(), rect.y(), size.width(), size.height())
- painter.setWindow(self.imageLabel.pixmap().rect())
- painter.drawPixmap(0, 0, self.imageLabel.pixmap())
-
- def zoomIn(self):
- self.scaleImage(1.25)
-
- def zoomOut(self):
- self.scaleImage(0.8)
-
- def normalSize(self):
- self.imageLabel.adjustSize()
- self.scaleFactor = 1.0
-
- def fitToWindow(self):
- fitToWindow = self.fitToWindowAct.isChecked()
- self.scrollArea.setWidgetResizable(fitToWindow)
- if not fitToWindow:
- self.normalSize()
-
- self.updateActions()
-
- def about(self):
- QMessageBox.about(self, "About Image Viewer",
- "This tool is used to stroke extracting manually!")
-
- def createActions(self):
- self.openAct = QAction("&Open...", self, shortcut="Ctrl+O",
- triggered=self.open)
-
- self.printAct = QAction("&Print...", self, shortcut="Ctrl+P",
- enabled=False, triggered=self.print_)
-
- self.exitAct = QAction("E&xit", self, shortcut="Ctrl+Q",
- triggered=self.close)
-
- self.zoomInAct = QAction("Zoom &In (25%)", self, shortcut="Ctrl++",
- enabled=False, triggered=self.zoomIn)
-
- self.zoomOutAct = QAction("Zoom &Out (25%)", self, shortcut="Ctrl+-",
- enabled=False, triggered=self.zoomOut)
-
- self.normalSizeAct = QAction("&Normal Size", self, shortcut="Ctrl+S",
- enabled=False, triggered=self.normalSize)
-
- self.fitToWindowAct = QAction("&Fit to Window", self, enabled=False,
- checkable=True, shortcut="Ctrl+F", triggered=self.fitToWindow)
-
- self.aboutAct = QAction("&About", self, triggered=self.about)
-
- self.aboutQtAct = QAction("About &Qt", self,
- triggered=QApplication.instance().aboutQt)
-
- def createMenus(self):
- self.fileMenu = QMenu("&File", self)
- self.fileMenu.addAction(self.openAct)
- self.fileMenu.addAction(self.printAct)
- self.fileMenu.addSeparator()
- self.fileMenu.addAction(self.exitAct)
-
- self.viewMenu = QMenu("&View", self)
- self.viewMenu.addAction(self.zoomInAct)
- self.viewMenu.addAction(self.zoomOutAct)
- self.viewMenu.addAction(self.normalSizeAct)
- self.viewMenu.addSeparator()
- self.viewMenu.addAction(self.fitToWindowAct)
-
- self.helpMenu = QMenu("&Help", self)
- self.helpMenu.addAction(self.aboutAct)
- self.helpMenu.addAction(self.aboutQtAct)
-
- self.menuBar().addMenu(self.fileMenu)
- self.menuBar().addMenu(self.viewMenu)
- self.menuBar().addMenu(self.helpMenu)
- self.menuBar().setNativeMenuBar(False)
-
- def updateActions(self):
- self.zoomInAct.setEnabled(not self.fitToWindowAct.isChecked())
- self.zoomOutAct.setEnabled(not self.fitToWindowAct.isChecked())
- self.normalSizeAct.setEnabled(not self.fitToWindowAct.isChecked())
-
- def scaleImage(self, factor):
- self.scaleFactor *= factor
- self.imageLabel.resize(self.scaleFactor * self.imageLabel.pixmap().size())
-
- self.adjustScrollBar(self.scrollArea.horizontalScrollBar(), factor)
- self.adjustScrollBar(self.scrollArea.verticalScrollBar(), factor)
-
- self.zoomInAct.setEnabled(self.scaleFactor < 3.0)
- self.zoomOutAct.setEnabled(self.scaleFactor > 0.333)
-
- def adjustScrollBar(self, scrollBar, factor):
- scrollBar.setValue(int(factor * scrollBar.value()
- + ((factor - 1) * scrollBar.pageStep()/2)))
-
-
-if __name__ == '__main__':
-
- import sys
-
- app = QApplication(sys.argv)
- imageViewer = ImageViewer()
- imageViewer.show()
- sys.exit(app.exec_())
\ No newline at end of file
diff --git a/strokeExtractionTool/__init__.py b/strokeExtractionTool/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/strokeExtractionTool/strokeExtractingMainwindow.py b/strokeExtractionTool/strokeExtractingMainwindow.py
new file mode 100644
index 0000000..6d4a76d
--- /dev/null
+++ b/strokeExtractionTool/strokeExtractingMainwindow.py
@@ -0,0 +1,98 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'strokeExtractingMainwindow.ui'
+#
+# Created by: PyQt5 UI code generator 5.10.1
+#
+# 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(1278, 667)
+ self.centralwidget = QtWidgets.QWidget(MainWindow)
+ self.centralwidget.setObjectName("centralwidget")
+ self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
+ self.verticalLayoutWidget.setGeometry(QtCore.QRect(10, 10, 171, 111))
+ self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
+ self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
+ self.verticalLayout.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.open_btn = QtWidgets.QPushButton(self.verticalLayoutWidget)
+ self.open_btn.setObjectName("open_btn")
+ self.verticalLayout.addWidget(self.open_btn)
+ self.clear_btn = QtWidgets.QPushButton(self.verticalLayoutWidget)
+ self.clear_btn.setObjectName("clear_btn")
+ self.verticalLayout.addWidget(self.clear_btn)
+ self.exit_btn = QtWidgets.QPushButton(self.verticalLayoutWidget)
+ self.exit_btn.setObjectName("exit_btn")
+ self.verticalLayout.addWidget(self.exit_btn)
+ self.image_view = QtWidgets.QGraphicsView(self.centralwidget)
+ self.image_view.setGeometry(QtCore.QRect(200, 10, 1061, 591))
+ self.image_view.setObjectName("image_view")
+ self.verticalLayoutWidget_2 = QtWidgets.QWidget(self.centralwidget)
+ self.verticalLayoutWidget_2.setGeometry(QtCore.QRect(10, 130, 171, 151))
+ self.verticalLayoutWidget_2.setObjectName("verticalLayoutWidget_2")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_2)
+ self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.radicalExtract_btn = QtWidgets.QPushButton(self.verticalLayoutWidget_2)
+ self.radicalExtract_btn.setObjectName("radicalExtract_btn")
+ self.verticalLayout_2.addWidget(self.radicalExtract_btn)
+ self.radical_listview = QtWidgets.QListView(self.verticalLayoutWidget_2)
+ self.radical_listview.setObjectName("radical_listview")
+ self.verticalLayout_2.addWidget(self.radical_listview)
+ self.verticalLayoutWidget_3 = QtWidgets.QWidget(self.centralwidget)
+ self.verticalLayoutWidget_3.setGeometry(QtCore.QRect(10, 287, 174, 351))
+ self.verticalLayoutWidget_3.setObjectName("verticalLayoutWidget_3")
+ self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_3)
+ self.verticalLayout_3.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_3.setObjectName("verticalLayout_3")
+ self.auto_extract_btn = QtWidgets.QPushButton(self.verticalLayoutWidget_3)
+ self.auto_extract_btn.setObjectName("auto_extract_btn")
+ self.verticalLayout_3.addWidget(self.auto_extract_btn)
+ self.extract_btn = QtWidgets.QPushButton(self.verticalLayoutWidget_3)
+ self.extract_btn.setObjectName("extract_btn")
+ self.verticalLayout_3.addWidget(self.extract_btn)
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.delete_stroke_btn = QtWidgets.QPushButton(self.verticalLayoutWidget_3)
+ self.delete_stroke_btn.setObjectName("delete_stroke_btn")
+ self.horizontalLayout.addWidget(self.delete_stroke_btn)
+ self.add_stroke_btn = QtWidgets.QPushButton(self.verticalLayoutWidget_3)
+ self.add_stroke_btn.setObjectName("add_stroke_btn")
+ self.horizontalLayout.addWidget(self.add_stroke_btn)
+ self.verticalLayout_3.addLayout(self.horizontalLayout)
+ self.stroke_listview = QtWidgets.QListView(self.verticalLayoutWidget_3)
+ self.stroke_listview.setObjectName("stroke_listview")
+ self.verticalLayout_3.addWidget(self.stroke_listview)
+ self.saveStroke_btn = QtWidgets.QPushButton(self.verticalLayoutWidget_3)
+ self.saveStroke_btn.setObjectName("saveStroke_btn")
+ self.verticalLayout_3.addWidget(self.saveStroke_btn)
+ MainWindow.setCentralWidget(self.centralwidget)
+ self.menubar = QtWidgets.QMenuBar(MainWindow)
+ self.menubar.setGeometry(QtCore.QRect(0, 0, 1278, 22))
+ self.menubar.setObjectName("menubar")
+ MainWindow.setMenuBar(self.menubar)
+ self.statusbar = QtWidgets.QStatusBar(MainWindow)
+ self.statusbar.setObjectName("statusbar")
+ MainWindow.setStatusBar(self.statusbar)
+
+ self.retranslateUi(MainWindow)
+ QtCore.QMetaObject.connectSlotsByName(MainWindow)
+
+ def retranslateUi(self, MainWindow):
+ _translate = QtCore.QCoreApplication.translate
+ MainWindow.setWindowTitle(_translate("MainWindow", "Strokes Extracting Tool"))
+ self.open_btn.setText(_translate("MainWindow", "Open"))
+ self.clear_btn.setText(_translate("MainWindow", "Clear"))
+ self.exit_btn.setText(_translate("MainWindow", "Exit"))
+ self.radicalExtract_btn.setText(_translate("MainWindow", "Radicals Extracting"))
+ self.auto_extract_btn.setText(_translate("MainWindow", "Auto-extracting"))
+ self.extract_btn.setText(_translate("MainWindow", "Extracting"))
+ self.delete_stroke_btn.setText(_translate("MainWindow", "DELETE"))
+ self.add_stroke_btn.setText(_translate("MainWindow", "ADD"))
+ self.saveStroke_btn.setText(_translate("MainWindow", "Save Strokes"))
+
diff --git a/strokeExtractionTool/strokeExtractingMainwindow.ui b/strokeExtractionTool/strokeExtractingMainwindow.ui
new file mode 100644
index 0000000..93d92ec
--- /dev/null
+++ b/strokeExtractionTool/strokeExtractingMainwindow.ui
@@ -0,0 +1,151 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 1278
+ 667
+
+
+
+ MainWindow
+
+
+
+
+
+ 10
+ 10
+ 171
+ 111
+
+
+
+ -
+
+
+ Open
+
+
+
+ -
+
+
+ Clear
+
+
+
+ -
+
+
+ Exit
+
+
+
+
+
+
+
+
+ 200
+ 10
+ 1061
+ 591
+
+
+
+
+
+
+ 10
+ 130
+ 171
+ 151
+
+
+
+ -
+
+
+ Radicals Extracting
+
+
+
+ -
+
+
+
+
+
+
+
+ 10
+ 287
+ 174
+ 351
+
+
+
+ -
+
+
+ Auto-extracting
+
+
+
+ -
+
+
+ Extracting
+
+
+
+ -
+
+
-
+
+
+ DELETE
+
+
+
+ -
+
+
+ ADD
+
+
+
+
+
+ -
+
+
+ -
+
+
+ Save Strokes
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/manuallyStrokeExtractToolMainWindow.py b/strokeExtractionToolGUI.py
similarity index 99%
rename from manuallyStrokeExtractToolMainWindow.py
rename to strokeExtractionToolGUI.py
index 3b92fee..d5405eb 100644
--- a/manuallyStrokeExtractToolMainWindow.py
+++ b/strokeExtractionToolGUI.py
@@ -7,7 +7,7 @@
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
-from strokeExtractingMainwindow import Ui_MainWindow
+from strokeExtractionTool.strokeExtractingMainwindow import Ui_MainWindow
from utils.Functions import splitConnectedComponents
diff --git a/test/PathFitter.py b/test/PathFitter.py
new file mode 100644
index 0000000..e2f292f
--- /dev/null
+++ b/test/PathFitter.py
@@ -0,0 +1,391 @@
+"""
+Ported from Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
+http://paperjs.org/
+Copyright (c) 2011 - 2014, Juerg Lehni & Jonathan Puckey
+http://scratchdisk.com/ & http://jonathanpuckey.com/
+Distributed under the MIT license. See LICENSE file for details.
+All rights reserved.
+An Algorithm for Automatically Fitting Digitized Curves
+by Philip J. Schneider
+from "Graphics Gems", Academic Press, 1990
+Modifications and optimisations of original algorithm by Juerg Lehni.
+Ported by Gumble, 2015.
+"""
+
+import math
+
+TOLERANCE = 10e-6
+EPSILON = 10e-12
+
+
+class Point:
+ __slots__ = ['x', 'y']
+
+ def __init__(self, x, y=None):
+ if y is None:
+ self.x, self.y = x[0], x[1]
+ else:
+ self.x, self.y = x, y
+
+ def __repr__(self):
+ return 'Point(%r, %r)' % (self.x, self.y)
+
+ def __str__(self):
+ return '%G,%G' % (self.x, self.y)
+
+ def __complex__(self):
+ return complex(self.x, self.y)
+
+ def __hash__(self):
+ return hash(self.__complex__())
+
+ def __bool__(self):
+ return bool(self.x or self.y)
+
+ def __add__(self, other):
+ if isinstance(other, Point):
+ return Point(self.x + other.x, self.y + other.y)
+ else:
+ return Point(self.x + other, self.y + other)
+
+ def __sub__(self, other):
+ if isinstance(other, Point):
+ return Point(self.x - other.x, self.y - other.y)
+ else:
+ return Point(self.x - other, self.y - other)
+
+ def __mul__(self, other):
+ if isinstance(other, Point):
+ return Point(self.x * other.x, self.y * other.y)
+ else:
+ return Point(self.x * other, self.y * other)
+
+ def __truediv__(self, other):
+ if isinstance(other, Point):
+ return Point(self.x / other.x, self.y / other.y)
+ else:
+ return Point(self.x / other, self.y / other)
+
+ def __neg__(self):
+ return Point(-self.x, -self.y)
+
+ def __len__(self):
+ return math.hypot(self.x, self.y)
+
+ def __eq__(self, other):
+ try:
+ return self.x == other.x and self.y == other.y
+ except Exception:
+ return False
+
+ def __ne__(self, other):
+ try:
+ return self.x != other.x or self.y != other.y
+ except Exception:
+ return True
+
+ add = __add__
+ subtract = __sub__
+ multiply = __mul__
+ divide = __truediv__
+ negate = __neg__
+ getLength = __len__
+ equals = __eq__
+
+ def copy(self):
+ return Point(self.x, self.y)
+
+ def dot(self, other):
+ return self.x * other.x + self.y * other.y
+
+ def normalize(self, length=1):
+ current = self.__len__()
+ scale = length / current if current != 0 else 0
+ return Point(self.x * scale, self.y * scale)
+
+ def getDistance(self, other):
+ return math.hypot(self.x - other.x, self.y - other.y)
+
+
+class Segment:
+
+ def __init__(self, *args):
+ self.point = Point(0, 0)
+ self.handleIn = Point(0, 0)
+ self.handleOut = Point(0, 0)
+ if len(args) == 1:
+ if isinstance(args[0], Segment):
+ self.point = args[0].point
+ self.handleIn = args[0].handleIn
+ self.handleOut = args[0].handleOut
+ else:
+ self.point = args[0]
+ elif len(args) == 2 and isinstance(args[0], (int, float)):
+ self.point = Point(*args)
+ elif len(args) == 2:
+ self.point = args[0]
+ self.handleIn = args[1]
+ elif len(args) == 3:
+ self.point = args[0]
+ self.handleIn = args[1]
+ self.handleOut = args[2]
+ else:
+ self.point = Point(args[0], args[1])
+ self.handleIn = Point(args[2], args[3])
+ self.handleOut = Point(args[4], args[5])
+
+ def __repr__(self):
+ return 'Segment(%r, %r, %r)' % (self.point, self.handleIn, self.handleOut)
+
+ def __hash__(self):
+ return hash((self.point, self.handleIn, self.handleOut))
+
+ def __bool__(self):
+ return bool(self.point or self.handleIn or self.handleOut)
+
+ def getPoint(self):
+ return self.point
+
+ def setPoint(self, other):
+ self.point = other
+
+ def getHandleIn(self):
+ return self.handleIn
+
+ def setHandleIn(self, other):
+ self.handleIn = other
+
+ def getHandleOut(self):
+ return self.handleOut
+
+ def setHandleOut(self, other):
+ self.handleOut = other
+
+
+class PathFitter:
+
+ def __init__(self, segments, error=2.5):
+ self.points = []
+ # Copy over points from path and filter out adjacent duplicates.
+ l = len(segments)
+ prev = None
+ for i in range(l):
+ point = segments[i].point.copy()
+ if prev != point:
+ self.points.append(point)
+ prev = point
+ self.error = error
+
+ def fit(self):
+ points = self.points
+ length = len(points)
+ self.segments = [Segment(points[0])] if length > 0 else []
+ if length > 1:
+ self.fitCubic(0, length - 1,
+ # Left Tangent
+ points[1].subtract(points[0]).normalize(),
+ # Right Tangent
+ points[length - 2].subtract(points[length - 1]).normalize())
+ return self.segments
+
+ # Fit a Bezier curve to a (sub)set of digitized points
+ def fitCubic(self, first, last, tan1, tan2):
+ # Use heuristic if region only has two points in it
+ if last - first == 1:
+ pt1 = self.points[first]
+ pt2 = self.points[last]
+ dist = pt1.getDistance(pt2) / 3
+ self.addCurve([pt1, pt1 + tan1.normalize(dist),
+ pt2 + tan2.normalize(dist), pt2])
+ return
+ # Parameterize points, and attempt to fit curve
+ uPrime = self.chordLengthParameterize(first, last)
+ maxError = max(self.error, self.error * self.error)
+ # Try 4 iterations
+ for i in range(5):
+ curve = self.generateBezier(first, last, uPrime, tan1, tan2)
+ # Find max deviation of points to fitted curve
+ maxerr, maxind = self.findMaxError(first, last, curve, uPrime)
+ if maxerr < self.error:
+ self.addCurve(curve)
+ return
+ split = maxind
+ # If error not too large, try reparameterization and iteration
+ if maxerr >= maxError:
+ break
+ self.reparameterize(first, last, uPrime, curve)
+ maxError = maxerr
+ # Fitting failed -- split at max error point and fit recursively
+ V1 = self.points[split - 1].subtract(self.points[split])
+ V2 = self.points[split] - self.points[split + 1]
+ tanCenter = V1.add(V2).divide(2).normalize()
+ self.fitCubic(first, split, tan1, tanCenter)
+ self.fitCubic(split, last, tanCenter.negate(), tan2)
+
+ def addCurve(self, curve):
+ prev = self.segments[len(self.segments) - 1]
+ prev.setHandleOut(curve[1].subtract(curve[0]))
+ self.segments.append(
+ Segment(curve[3], curve[2].subtract(curve[3])))
+
+ # Use least-squares method to find Bezier control points for region.
+ def generateBezier(self, first, last, uPrime, tan1, tan2):
+ epsilon = 1e-11
+ pt1 = self.points[first]
+ pt2 = self.points[last]
+ # Create the C and X matrices
+ C = [[0, 0], [0, 0]]
+ X = [0, 0]
+
+ l = last - first + 1
+
+ for i in range(l):
+ u = uPrime[i]
+ t = 1 - u
+ b = 3 * u * t
+ b0 = t * t * t
+ b1 = b * t
+ b2 = b * u
+ b3 = u * u * u
+ a1 = tan1.normalize(b1)
+ a2 = tan2.normalize(b2)
+ tmp = (self.points[first + i]
+ - pt1.multiply(b0 + b1)
+ - pt2.multiply(b2 + b3))
+ C[0][0] += a1.dot(a1)
+ C[0][1] += a1.dot(a2)
+ # C[1][0] += a1.dot(a2)
+ C[1][0] = C[0][1]
+ C[1][1] += a2.dot(a2)
+ X[0] += a1.dot(tmp)
+ X[1] += a2.dot(tmp)
+
+ # Compute the determinants of C and X
+ detC0C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1]
+ if abs(detC0C1) > epsilon:
+ # Kramer's rule
+ detC0X = C[0][0] * X[1] - C[1][0] * X[0]
+ detXC1 = X[0] * C[1][1] - X[1] * C[0][1]
+ # Derive alpha values
+ alpha1 = detXC1 / detC0C1
+ alpha2 = detC0X / detC0C1
+ else:
+ # Matrix is under-determined, try assuming alpha1 == alpha2
+ c0 = C[0][0] + C[0][1]
+ c1 = C[1][0] + C[1][1]
+ if abs(c0) > epsilon:
+ alpha1 = alpha2 = X[0] / c0
+ elif abs(c1) > epsilon:
+ alpha1 = alpha2 = X[1] / c1
+ else:
+ # Handle below
+ alpha1 = alpha2 = 0
+
+ # If alpha negative, use the Wu/Barsky heuristic (see text)
+ # (if alpha is 0, you get coincident control points that lead to
+ # divide by zero in any subsequent NewtonRaphsonRootFind() call.
+ segLength = pt2.getDistance(pt1)
+ epsilon *= segLength
+ if alpha1 < epsilon or alpha2 < epsilon:
+ # fall back on standard (probably inaccurate) formula,
+ # and subdivide further if needed.
+ alpha1 = alpha2 = segLength / 3
+
+ # First and last control points of the Bezier curve are
+ # positioned exactly at the first and last data points
+ # Control points 1 and 2 are positioned an alpha distance out
+ # on the tangent vectors, left and right, respectively
+ return [pt1, pt1.add(tan1.normalize(alpha1)),
+ pt2.add(tan2.normalize(alpha2)), pt2]
+
+ # Given set of points and their parameterization, try to find
+ # a better parameterization.
+ def reparameterize(self, first, last, u, curve):
+ for i in range(first, last + 1):
+ u[i - first] = self.findRoot(curve, self.points[i], u[i - first])
+
+ # Use Newton-Raphson iteration to find better root.
+ def findRoot(self, curve, point, u):
+ # Generate control vertices for Q'
+ curve1 = [
+ curve[i + 1].subtract(curve[i]).multiply(3) for i in range(3)]
+ # Generate control vertices for Q''
+ curve2 = [
+ curve1[i + 1].subtract(curve1[i]).multiply(2) for i in range(2)]
+ # Compute Q(u), Q'(u) and Q''(u)
+ pt = self.evaluate(3, curve, u)
+ pt1 = self.evaluate(2, curve1, u)
+ pt2 = self.evaluate(1, curve2, u)
+ diff = pt - point
+ df = pt1.dot(pt1) + diff.dot(pt2)
+ # Compute f(u) / f'(u)
+ if abs(df) < TOLERANCE:
+ return u
+ # u = u - f(u) / f'(u)
+ return u - diff.dot(pt1) / df
+
+ # Evaluate a bezier curve at a particular parameter value
+ def evaluate(self, degree, curve, t):
+ # Copy array
+ tmp = curve[:]
+ # Triangle computation
+ for i in range(1, degree + 1):
+ for j in range(degree - i + 1):
+ tmp[j] = tmp[j].multiply(1 - t) + tmp[j + 1].multiply(t)
+ return tmp[0]
+
+ # Assign parameter values to digitized points
+ # using relative distances between points.
+ def chordLengthParameterize(self, first, last):
+ u = {0: 0}
+ print(first, last)
+ for i in range(first + 1, last + 1):
+ u[i - first] = u[i - first - 1] + \
+ self.points[i].getDistance(self.points[i - 1])
+ m = last - first
+ for i in range(1, m + 1):
+ u[i] /= u[m]
+ return u
+
+ # Find the maximum squared distance of digitized points to fitted curve.
+ def findMaxError(self, first, last, curve, u):
+ index = math.floor((last - first + 1) / 2)
+ maxDist = 0
+ for i in range(first + 1, last):
+ P = self.evaluate(3, curve, u[i - first])
+ v = P.subtract(self.points[i])
+ dist = v.x * v.x + v.y * v.y # squared
+ if dist >= maxDist:
+ maxDist = dist
+ index = i
+ return maxDist, index
+
+
+def fitpath(pointlist, error):
+ return PathFitter(list(map(Segment, map(Point, pointlist))), error).fit()
+
+
+def fitpathsvg(pointlist, error):
+ return pathtosvg(PathFitter(list(map(Segment, map(Point, pointlist))), error).fit())
+
+
+def pathtosvg(path):
+ segs = ['M', str(path[0].point)]
+ last = path[0]
+ for seg in path[1:]:
+ segs.append('C')
+ segs.append(str(last.point + last.handleOut))
+ segs.append(str(seg.point + seg.handleIn))
+ segs.append(str(seg.point))
+ last = seg
+ return ' '.join(segs)
+
+
+if __name__ == '__main__':
+ p = [(88, 151), (90, 151), (98, 151), (105, 151), (112, 151), (121, 151), (141, 151), (153, 150), (165, 150), (203, 150), (224, 151),
+ (268, 154), (282, 155), (331, 156), (340, 156), (353, 156), (358, 156), (361, 156), (362, 156), (365, 156), (366, 156), (372, 156), (373, 156)]
+ pf = fitpath(p, error=2.5)
+ print(pf)
+ print("----")
+ sp = pathtosvg(pf)
+ print(sp)
\ No newline at end of file
diff --git a/test/autoStrokeExtracting.py b/test/autoStrokeExtracting.py
new file mode 100644
index 0000000..da70c80
--- /dev/null
+++ b/test/autoStrokeExtracting.py
@@ -0,0 +1,284 @@
+# coding: utf-8
+import cv2
+import numpy as np
+import math
+
+from utils.Functions import getConnectedComponents, getContourOfImage, getSkeletonOfImage, removeBreakPointsOfContour, \
+ removeBranchOfSkeletonLine, removeExtraBranchesOfSkeleton, getEndPointsOfSkeletonLine, \
+ getCrossPointsOfSkeletonLine, sortPointsOnContourOfImage, min_distance_point2pointlist, \
+ getNumberOfValidPixels, segmentContourBasedOnCornerPoints, createBlankGrayscaleImage, \
+ getLinePoints, getBreakPointsFromContour, merge_corner_lines_to_point, getCropLines, \
+ getCornerPointsOfImage, getClusterOfCornerPoints, getCropLinesPoints, \
+ getConnectedComponentsOfGrayScale, getAllMiniBoundingBoxesOfImage, getCornersPoints, \
+ getContourImage, getValidCornersPoints, getDistanceBetweenPointAndComponent, \
+ isIndependentCropLines, mergeBkAndComponent, isValidComponent
+
+
+def autoStrokeExtracting(index, image, threshold_value=200):
+ """
+ Automatic strokes extracting
+ :param image: grayscale image
+ :return: strokes images with same size
+ """
+ strokes = []
+ if image is None:
+ return strokes
+
+ # get connected components from the grayscale image, not for the binary image.
+ contour_img = getContourImage(image)
+ contours = getConnectedComponents(contour_img) # no holes, num=1, holes exist, num >= 2
+ print("contours num: %d" % len(contours))
+
+ corners_points_sorted = []
+ for ct in contours:
+ points = sortPointsOnContourOfImage(ct)
+ corners_points_sorted.append(points)
+ if len(corners_points_sorted) == 1:
+ print("No holes exist!")
+ elif len(corners_points_sorted) >= 2:
+ print("Holes exist!")
+
+ # grayscale image to binary image
+ _, img_bit = cv2.threshold(image, threshold_value, 255, cv2.THRESH_BINARY)
+
+ # skeleton image of width 1 pixel of binray image
+ skeleton_img = getSkeletonOfImage(img_bit)
+ skeleton_img = removeExtraBranchesOfSkeleton(skeleton_img)
+ end_points = getEndPointsOfSkeletonLine(skeleton_img) # end points
+ cross_points = getCrossPointsOfSkeletonLine(skeleton_img) # croiss points
+
+ print("end points num: %d" % len(end_points))
+ print("cross points num: %d" % len(cross_points))
+
+ if len(cross_points) == 0:
+ print("no cross points!")
+ strokes.append(image)
+ return strokes
+
+ # corner area points
+ corners_all_points = getCornersPoints(image.copy(), contour_img, blockSize=3, ksize=3, k=0.04)
+ corners_points = getValidCornersPoints(corners_all_points, cross_points, end_points, distance_threshold=30)
+ print("corners points num: %d" % len(corners_points))
+
+ if len(corners_points) == 0:
+ print("no corner point")
+ strokes.append(image)
+ return strokes
+
+ contour_rgb = cv2.cvtColor(contour_img, cv2.COLOR_GRAY2RGB)
+ contour_gray = cv2.cvtColor(contour_rgb, cv2.COLOR_RGB2GRAY)
+ _, contour_gray = cv2.threshold(contour_gray, 200, 255, cv2.THRESH_BINARY)
+
+ for pt in corners_points:
+ contour_rgb[pt[1]][pt[0]] = (0, 0, 255)
+
+ # cluster corners points based on the cross point
+ corner_points_cluster = getClusterOfCornerPoints(corners_points, cross_points)
+
+ # cropping lines based on the corner points
+ crop_lines = getCropLines(corner_points_cluster)
+
+ for line in crop_lines:
+ cv2.line(contour_rgb, line[0], line[1], (0, 255, 0), 1)
+ cv2.line(contour_gray, line[0], line[1], 0, 1)
+
+ # split contour to components
+ ret, labels = cv2.connectedComponents(contour_gray, connectivity=4)
+ components = []
+ for r in range(1, ret):
+ img_ = createBlankGrayscaleImage(contour_gray)
+ for y in range(contour_gray.shape[0]):
+ for x in range(contour_gray.shape[1]):
+ if labels[y][x] == r:
+ img_[y][x] = 0.0
+ if img_[0][0] != 0.0 and isValidComponent(img_, img_bit):
+ components.append(img_)
+
+ print("components num : %d" % len(components))
+ used_components = []
+ component_line_relation = {} # {component_id: [line_id1, line_id2]}
+
+ # merge contour to components
+ for i in range(len(components)):
+ merge_points = []
+ for y in range(1, contour_gray.shape[0]-1):
+ for x in range(1, contour_gray.shape[1]-1):
+ if contour_gray[y][x] == 0.0:
+ # 4 nearby pixels should be black in components
+ if components[i][y-1][x] == 0.0 or components[i][y][x+1] == 0.0 or components[i][y+1][x] == 0.0 or \
+ components[i][y][x-1] == 0.0:
+ merge_points.append((x, y))
+ for pt in merge_points:
+ components[i][pt[1]][pt[0]] = 0.0
+ # merge cropping lines on the components
+ for i in range(len(crop_lines)):
+ components_id = []
+ line = crop_lines[i]
+ for j in range(len(components)):
+ dist_startpt = getDistanceBetweenPointAndComponent(line[0], components[j])
+ # print("dist startpt:%f" % dist_startpt)
+ dist_endpt = getDistanceBetweenPointAndComponent(line[1], components[j])
+ # print("dist end pt: %f" % dist_endpt)
+
+ if dist_startpt < 3 and dist_endpt < 3:
+ cv2.line(components[j], line[0], line[1], 0, 1)
+ components_id.append(j)
+
+ if len(components_id) >= 2:
+ break
+
+ # find overlap region components
+ overlap_components = []
+ for i in range(len(components)):
+ part = components[i]
+ part_lines = []
+ part_lines_id = []
+ for j in range(len(crop_lines)):
+ line = crop_lines[j]
+ if part[line[0][1]][line[0][0]] == 0.0 and part[line[1][1]][line[1][0]] == 0.0:
+ part_lines.append(line)
+ part_lines_id.append(j)
+
+ # check number of lines == 4 and cross each other
+ if len(part_lines) == 4:
+ points_set = set()
+ for line in part_lines:
+ points_set.add(line[0])
+ points_set.add(line[1])
+ if len(points_set) == 4:
+ # only 4 points
+ overlap_components.append(part)
+ used_components.append(i)
+ component_line_relation[i] = part_lines_id
+
+ print("overlap components num: %d" % len(overlap_components))
+ print(component_line_relation)
+
+ # cluster components based on the cropping lines
+ for i in range(len(components)):
+ part = components[i]
+ part_lines = [] # used to detect overlap region components.
+
+ # find single part is stroke
+ is_single = True
+ for line in crop_lines:
+ x1 = line[0][0]; y1 = line[0][1]
+ x2 = line[1][0]; y2 = line[1][1]
+ if part[y1][x1] == 0.0 and part[y2][x2] != 0.0 or part[y1][x1] != 0.0 and part[y2][x2] == 0.0:
+ is_single = False
+ break
+ if part[y1][x1] == 0.0 and part[y2][x2] == 0.0:
+ part_lines.append(line)
+
+ if is_single and isIndependentCropLines(part_lines):
+ strokes.append(part)
+ used_components.append(i)
+
+ print("single stroke num: %d" % len(strokes))
+ print("used components num: %d" % len(used_components))
+
+ # cluster components based on the cropping lines
+ for i in range(len(components)):
+ if i in used_components:
+ continue
+ # find corresponding crop lines
+ lines_id = []
+ for j in range(len(crop_lines)):
+ line = crop_lines[j]
+ if components[i][line[0][1]][line[0][0]] == 0.0 and components[i][line[1][1]][line[1][0]] != 0.0:
+ lines_id.append(j)
+ if components[i][line[0][1]][line[0][0]] != 0.0 and components[i][line[1][1]][line[1][0]] == 0.0:
+ lines_id.append(j)
+
+ if len(lines_id) == 0:
+ continue
+ component_line_relation[i] = lines_id
+ used_components.append(i)
+
+ # cluster components based on the relations and merge those related components
+ clusters = []
+ for k1, v1 in component_line_relation.items():
+
+ cluster = [k1]; value_sets = [set(v1)]
+
+ for k2, v2 in component_line_relation.items():
+ is_related = True
+ for value in value_sets:
+ if not value.intersection(set(v2)):
+ is_related = False
+ break
+ if is_related and k2 not in cluster:
+ cluster.append(k2)
+ value_sets.append(set(v2))
+ cluster = sorted(cluster)
+ if cluster not in clusters:
+ clusters.append(cluster)
+
+ print(clusters)
+
+ # merge components based on the cluster
+ for i in range(len(clusters)):
+ cluster = clusters[i]
+ bk = createBlankGrayscaleImage(image)
+
+ for clt in cluster:
+ bk = mergeBkAndComponent(bk, components[clt])
+
+ # add to strokes
+ strokes.append(bk)
+
+ # check the stroke is valid
+ for i in range(len(strokes)):
+ stroke = strokes[i]
+
+
+
+ cv2.imshow("radical_%d" % index, contour_rgb)
+ cv2.imshow("radical_gray_%d" % index, contour_gray)
+
+ return strokes
+
+
+def main():
+ # 1133壬 2252支 0631叟 0633口 0242俄 0195佛 0860善 0059乘 0098亩 0034串
+ path = "0034串.jpg"
+
+ img_rgb = cv2.imread(path)
+ img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY)
+ _, img_bit = cv2.threshold(img_gray, 200, 255, cv2.THRESH_BINARY)
+
+ # grayscale threshold filter pixel > 200 = 255
+ for y in range(img_gray.shape[0]):
+ for x in range(img_gray.shape[1]):
+ if img_gray[y][x] > 200:
+ img_gray[y][x] = 255
+
+ # get components
+ components = getConnectedComponentsOfGrayScale(img_gray, threshold_value=200)
+
+ if components is None or len(components) == 0:
+ print("components num is 0")
+ return
+
+ total_strokes = []
+
+ for i in range(len(components)):
+ radical = components[i]
+
+ radical_strokes = autoStrokeExtracting(i, radical)
+ if radical_strokes is None or len(radical_strokes) == 0:
+ print("radiacal is None")
+ else:
+ total_strokes += radical_strokes
+
+ # display strokes
+ for i in range(len(total_strokes)):
+ cv2.imshow("stroke_%d"%i, total_strokes[i])
+
+ cv2.waitKey(0)
+ cv2.destroyAllWindows()
+
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/test/background_remove.py b/test/background_remove.py
new file mode 100644
index 0000000..dfad139
--- /dev/null
+++ b/test/background_remove.py
@@ -0,0 +1,15 @@
+import numpy as np
+import cv2
+
+path = "seg_test.jpg"
+
+img = cv2.imread(path)
+
+fgbg = cv2.createBackgroundSubtractorMOG2()
+
+fgmask = fgbg.apply(img)
+
+cv2.imshow("mask", fgmask)
+
+cv2.waitKey(0)
+cv2.destroyAllWindows()
\ No newline at end of file
diff --git a/test/calligraphySegmentationTool.py b/test/calligraphySegmentationTool.py
new file mode 100644
index 0000000..32f33f6
--- /dev/null
+++ b/test/calligraphySegmentationTool.py
@@ -0,0 +1,236 @@
+import cv2
+import numpy as np
+from utils.Functions import getAllMiniBoundingBoxesOfImage
+import math
+
+def main():
+ scale_ratio = 0.4
+ # src_path = '../calligraphys/yaomengqi1.jpg'
+ # src_path = '../calligraphys/ouyangxun1.jpg'
+ src_path = '../calligraphys/hurulin1.jpg'
+
+ img = cv2.imread(src_path, 0)
+
+ img_rgb = cv2.imread(src_path)
+
+ img = cv2.resize(img, (0, 0), fx=scale_ratio, fy=scale_ratio)
+ if len(img_rgb.shape) == 2:
+ img_rgb = cv2.cvtColor(img_rgb, cv2.COLOR_GRAY2RGB)
+
+ img_rgb = cv2.resize(img_rgb, (0,0), fx=scale_ratio, fy=scale_ratio)
+
+ # image processing
+
+ # image binary
+ _, img_bit = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
+ print(img_bit.shape)
+
+ # inverting color
+ # img_bit = 255 - img_bit
+
+ # get all bounding boxes
+ boxes = getAllMiniBoundingBoxesOfImage(img_bit)
+ print(len(boxes))
+
+ boxes_ = []
+
+ for box in boxes:
+ if box[2] < 30*scale_ratio or box[3] < 30*scale_ratio:
+ continue
+
+ if box[2] >= img_bit.shape[0] or box[3] >= img_bit.shape[1]:
+ continue
+
+ boxes_.append(box)
+
+ print(len(boxes_))
+
+ # remove inside rectangles
+ inside_id = []
+ for i in range(len(boxes_)):
+ ri_x = boxes_[i][0]
+ ri_y = boxes_[i][1]
+ ri_w = boxes_[i][2]
+ ri_h = boxes_[i][3]
+
+ for j in range(len(boxes_)):
+ if i == j or j in inside_id:
+ continue
+ rj_x = boxes_[j][0]
+ rj_y = boxes_[j][1]
+ rj_w = boxes_[j][2]
+ rj_h = boxes_[j][3]
+
+ # rect_j inside rect_i
+ if ri_x <= rj_x and ri_y <= rj_y and ri_x+ri_w >= rj_x+rj_w and ri_y+ri_h >= rj_y+rj_h:
+ if j not in inside_id:
+ inside_id.append(j)
+ elif rj_x <= ri_x and rj_y <= ri_y and rj_x+rj_w >= ri_x+ri_w and rj_y+rj_h >= ri_y+ri_h:
+ if i not in inside_id:
+ inside_id.append(i)
+
+ print("inside id len: %d" % len(inside_id))
+
+ boxes_noinsde = []
+ for i in range(len(boxes_)):
+ if i in inside_id:
+ continue
+ boxes_noinsde.append(boxes_[i])
+ print("no inside box len: %d" % len(boxes_noinsde))
+
+ # add rectangles to original images
+ for box in boxes_noinsde:
+ img_rgb = cv2.rectangle(img_rgb, (box[0], box[1]), (box[0] + box[2], box[1] + box[3]), (255, 0, 0), 1)
+
+ # handle the intersect rectangles
+
+ # add line of center of rectangles
+ dist_threshod = 70 * scale_ratio
+ for i in range(len(boxes_noinsde)):
+ ct_rect_i = getCenterOfRectangles(boxes_noinsde[i])
+
+ for j in range(len(boxes_noinsde)):
+ if i == j:
+ continue
+ ct_rect_j = getCenterOfRectangles(boxes_noinsde[j])
+
+ dist = math.sqrt((ct_rect_j[0] - ct_rect_i[0]) * (ct_rect_j[0] - ct_rect_i[0]) + (ct_rect_j[1] - ct_rect_i[1]) * (ct_rect_j[1] - ct_rect_i[1]))
+
+ if dist > dist_threshod:
+ continue
+ img_rgb = cv2.line(img_rgb, (ct_rect_i[0], ct_rect_i[1]), (ct_rect_j[0], ct_rect_j[1]), (0, 255, 0), 1)
+
+ # cluster rectangles based on the distance
+ rect_clustor = []
+ for i in range(len(boxes_noinsde)):
+ rect_item = []
+ rect_item.append(i)
+
+ ct_rect_i = getCenterOfRectangles(boxes_noinsde[i])
+
+ for j in range(len(boxes_noinsde)):
+ ct_rect_j = getCenterOfRectangles(boxes_noinsde[j])
+
+ dist = math.sqrt( (ct_rect_j[0] - ct_rect_i[0]) * (ct_rect_j[0] - ct_rect_i[0]) + (ct_rect_j[1] - ct_rect_i[1]) * (
+ ct_rect_j[1] - ct_rect_i[1]))
+ if dist <= dist_threshod and j not in rect_item:
+ rect_item.append(j)
+ rect_clustor.append(rect_item)
+
+ print(rect_clustor)
+
+ # merge the clustor
+ final_clustor = []
+ used_index = []
+ for i in range(len(rect_clustor)):
+ if i in used_index:
+ continue
+ new_clustor = rect_clustor[i]
+ # merge
+ for j in range(i+1, len(rect_clustor)):
+ if len(set(new_clustor).intersection(set(rect_clustor[j]))) == 0:
+ continue
+ new_clustor = list(set(new_clustor).union(set(rect_clustor[j])))
+ used_index.append(j)
+ final_clustor.append(new_clustor)
+
+ print(final_clustor)
+
+ for i in range(len(final_clustor)):
+
+ new_rect = combineRectangles(boxes_noinsde, final_clustor[i])
+ img_rgb = cv2.rectangle(img_rgb, (new_rect[0], new_rect[1]), (new_rect[0] + new_rect[2], new_rect[1] + new_rect[3]), (0, 0, 255), 1)
+
+
+
+
+
+
+ # the projecting histogram on X-axis (columns)
+ # x_sum = np.zeros(img_bit.shape[1])
+ # for j in range(img_bit.shape[0]):
+ # x_sum += 255 - img_bit[j, :]
+ # x_sum = x_sum / 255
+ # print(x_sum)
+ #
+ # plt.subplot(121)
+ # plt.imshow(img_bit, cmap='gray')
+ # plt.subplot(122)
+ # plt.plot(x_sum)
+ # plt.show()
+
+
+
+ cv2.imshow("source", img_rgb)
+
+ cv2.waitKey(0)
+ cv2.destroyAllWindows()
+
+
+# Check the valid pixels (black pixels) ratio in rectangule
+def checkRectangle(rect, threshold):
+ if rect is None or threshold == 0.0:
+ return False
+ area = rect.shape[0] * rect.shape[1]
+ if area == 0.0:
+ return False
+
+ # white background (255)
+ rect = 255.0 - rect
+ valid = np.sum(np.array(rect) / 255)
+ ratio = valid / area
+
+ if ratio >= threshold:
+ return True
+ else:
+ return False
+
+
+def getCenterOfRectangles(rect):
+ """
+ Get the coordinate (x, y) of the center if rectangle.
+ :param rect: rectangle (x, y, w, h)
+ :return: (x, y) of center of rectangle.
+ """
+ if rect is None:
+ return None
+ cx = rect[0] + int(rect[2]/2)
+ cy = rect[1] + int(rect[3]/2)
+
+ return (cx, cy)
+
+
+def combineRectangles(rectangles, rect_list):
+ """
+ Combining rectangles together.
+ :param rectangles: list of rectangles.
+ :param rect_list: list of index of rectangles in rectangles list.
+ :return: new combined rectangle.
+ """
+ if rectangles is None:
+ return None
+ if len(rect_list) == 1:
+ return rectangles[rect_list[0]]
+
+ new_rect_x = rectangles[rect_list[0]][0]
+ new_rect_y = rectangles[rect_list[0]][1]
+ new_rect_w = rectangles[rect_list[0]][2]
+ new_rect_h = rectangles[rect_list[0]][3]
+
+ for id in range(1, len(rect_list)):
+ rect_x = rectangles[rect_list[id]][0]
+ rect_y = rectangles[rect_list[id]][1]
+ rect_w = rectangles[rect_list[id]][2]
+ rect_h = rectangles[rect_list[id]][3]
+
+ new_rect_x = min(new_rect_x, rect_x)
+ new_rect_y = min(new_rect_y, rect_y)
+
+ new_rect_w = max(new_rect_x+new_rect_w, rect_x+rect_w) - new_rect_x
+ new_rect_h = max(new_rect_y+new_rect_h, rect_y+rect_h) - new_rect_y
+
+ return new_rect_x, new_rect_y, new_rect_w, new_rect_h
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/test/centerofgravitytool.py b/test/centerofgravitytool.py
new file mode 100644
index 0000000..358ecf9
--- /dev/null
+++ b/test/centerofgravitytool.py
@@ -0,0 +1,40 @@
+import numpy as np
+import cv2
+from utils.Functions import getCenterOfGravity, addIntersectedFig
+
+
+def main():
+ src_path = "../chars/src_dan_svg_simple_resized.png"
+ tag_path = "../chars/tag_dan_svg_simple_resized.png"
+
+ src_img = cv2.imread(src_path, 0)
+ tag_img = cv2.imread(tag_path, 0)
+
+ _, src_img = cv2.threshold(src_img, 127, 255, cv2.THRESH_BINARY)
+ _, tag_img = cv2.threshold(tag_img, 127, 255, cv2.THRESH_BINARY)
+
+ src_img_rgb = cv2.cvtColor(src_img, cv2.COLOR_GRAY2RGB)
+ tag_img_rgb = cv2.cvtColor(tag_img, cv2.COLOR_GRAY2RGB)
+
+ # center of gravity of source
+ src_cog_x, src_cog_y = getCenterOfGravity(src_img)
+ src_img_rgb = cv2.circle(src_img_rgb, (src_cog_y, src_cog_x), 4, (0, 0, 255), -1)
+ print("src : (%d, %d)" % (src_cog_x, src_cog_y))
+
+ # center of gravity of target
+ tag_cog_x, tag_cog_y = getCenterOfGravity(tag_img)
+ tag_img_rgb = cv2.circle(tag_img_rgb, (tag_cog_y, tag_cog_x), 4, (0, 0, 255), -1)
+ print("tag : (%d, %d)" % (tag_cog_x, tag_cog_y))
+
+ # add intersect lines
+ src_img_rgb_ = addIntersectedFig(src_img_rgb)
+ tag_img_rgb_ = addIntersectedFig(tag_img_rgb)
+
+ cv2.imshow("src", src_img_rgb_)
+ cv2.imshow("tag", tag_img_rgb_)
+ cv2.waitKey(0)
+ cv2.destroyAllWindows()
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/test/componentslayouttool.py b/test/componentslayouttool.py
new file mode 100644
index 0000000..c4216fe
--- /dev/null
+++ b/test/componentslayouttool.py
@@ -0,0 +1,92 @@
+import numpy as np
+import cv2
+
+
+def main():
+ src_path = "../chars/src_dan_svg_simple_resized.png"
+ tag_path = "../chars/tag_dan_svg_simple_resized.png"
+
+ src_img = cv2.imread(src_path, 0)
+ tag_img = cv2.imread(tag_path, 0)
+
+ _, src_img = cv2.threshold(src_img, 127, 255, cv2.THRESH_BINARY)
+ _, tag_img = cv2.threshold(tag_img, 127, 255, cv2.THRESH_BINARY)
+
+ src_img_rgb = cv2.cvtColor(src_img, cv2.COLOR_GRAY2RGB)
+ tag_img_rgb = cv2.cvtColor(tag_img, cv2.COLOR_GRAY2RGB)
+
+ # src component rects
+ _, src_contours, _ = cv2.findContours(src_img, 1, 2)
+ # for src_contour in src_contours:
+ # x, y, w, h = cv2.boundingRect(src_contour)
+ # if w > 0.95 * src_img.shape[0] and h > 0.95 * src_img.shape[1]:
+ # continue
+ #
+ # src_img_rgb = cv2.rectangle(src_img_rgb, (x,y), (x+w, y+h), (0, 255, 0), 2)
+
+ x, y, w, h = cv2.boundingRect(src_contours[0])
+ src_img_rgb = cv2.rectangle(src_img_rgb, (x, y), (x + w, y + h), (0, 255, 0), 2)
+ c0_x = x + int(w / 2)
+ c0_y = y + int(h / 2)
+ # x, y, w, h = cv2.boundingRect(src_contours[1])
+ # src_img_rgb = cv2.rectangle(src_img_rgb, (x, y), (x + w, y + h), (0, 255, 0), 2)
+ x, y, w, h = cv2.boundingRect(src_contours[2])
+ src_img_rgb = cv2.rectangle(src_img_rgb, (x, y), (x + w, y + h), (0, 255, 0), 2)
+ c2_x = x + int(w/2)
+ c2_y = y + int(h/2)
+
+ x, y, w, h = cv2.boundingRect(src_contours[3])
+ src_img_rgb = cv2.rectangle(src_img_rgb, (x, y), (x + w, y + h), (0, 255, 0), 2)
+ c3_x = x + int(w/2)
+ c3_y = y + int(h/2)
+
+ src_img_rgb = cv2.line(src_img_rgb, (c0_x, c0_y), (c2_x, c2_y), (0, 0, 255), 2)
+ src_img_rgb = cv2.line(src_img_rgb, (c0_x, c0_y), (c3_x, c3_y), (0, 0, 255), 2)
+ src_img_rgb = cv2.line(src_img_rgb, (c3_x, c3_y), (c2_x, c2_y), (0, 0, 255), 2)
+
+
+
+ # tag component rects
+ _, tag_contours, _ = cv2.findContours(tag_img, 1, 2)
+ # for tag_contour in tag_contours:
+ # x, y, w, h = cv2.boundingRect(tag_contour)
+ # if w > 0.95 * tag_img.shape[0] and h > 0.95 * tag_img.shape[1]:
+ # continue
+ #
+ # tag_img_rgb = cv2.rectangle(tag_img_rgb, (x,y), (x+w, y+h), (0, 255, 0), 2)
+ x, y, w, h = cv2.boundingRect(tag_contours[0])
+ tag_img_rgb = cv2.rectangle(tag_img_rgb, (x, y), (x + w, y + h), (0, 255, 0), 2)
+ c0_x = x+ int(w/2)
+ c0_y = y + int(h/2)
+ x1, y1, w1, h1 = cv2.boundingRect(tag_contours[1])
+ # tag_img_rgb = cv2.rectangle(tag_img_rgb, (x1, y1), (x1 + w1, y1 + h1), (0, 255, 0), 2)
+ # x, y, w, h = cv2.boundingRect(tag_contours[2])
+ # tag_img_rgb = cv2.rectangle(tag_img_rgb, (x, y), (x + w, y + h), (0, 255, 0), 2)
+ x3, y3, w3, h3 = cv2.boundingRect(tag_contours[3])
+ tag_img_rgb = cv2.rectangle(tag_img_rgb, (x3, y3), (x3 + w3, y3 + h3), (0, 255, 0), 2)
+ c3_x = x3 + int(w3/2)
+ c3_y = y3 + int(h3/2)
+ x4, y4, w4, h4 = cv2.boundingRect(tag_contours[4])
+ # tag_img_rgb = cv2.rectangle(tag_img_rgb, (x4, y4), (x4 + w4, y4 + h4), (0, 255, 0), 2)
+
+ new_x = min(x1, x4)
+ new_y = min(y1, y4)
+ new_w = max(x1+w1, x4+w4) - new_x
+ new_h = max(y1+h1, y4+h4) - new_y
+ tag_img_rgb = cv2.rectangle(tag_img_rgb, (new_x, new_y), (new_x + new_w, new_y + new_h), (0, 255, 0), 2)
+ cnew_x = new_x + int(new_w/2)
+ cnew_y = new_y + int(new_h/2)
+
+ tag_img_rgb = cv2.line(tag_img_rgb, (c0_x, c0_y), (c3_x, c3_y), (0, 0, 255), 2)
+ tag_img_rgb = cv2.line(tag_img_rgb, (c3_x, c3_y), (cnew_x, cnew_y), (0, 0, 255), 2)
+ tag_img_rgb = cv2.line(tag_img_rgb, (c0_x, c0_y), (cnew_x, cnew_y), (0, 0, 255), 2)
+
+
+ cv2.imshow("src", src_img_rgb)
+ cv2.imshow("tag", tag_img_rgb)
+ cv2.waitKey(0)
+ cv2.destroyAllWindows()
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/test/contourSmoothTool.py b/test/contourSmoothTool.py
new file mode 100644
index 0000000..e2e0d9c
--- /dev/null
+++ b/test/contourSmoothTool.py
@@ -0,0 +1,889 @@
+import cv2
+import math
+import numpy as np
+from utils.Functions import getContourOfImage, sortPointsOnContourOfImage
+import matplotlib.pyplot as plt
+from scipy.misc import comb
+
+from scipy import interpolate
+from utils.Functions import getNumberOfValidPixels
+
+def main():
+
+ # load image
+ img_path = "../templates/stroke_dan.png"
+
+ img = cv2.imread(img_path, 0)
+ _, img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
+
+ img_rgb = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
+
+ # get contour of image
+ contour = getContourOfImage(img)
+ contour_rgb = cv2.cvtColor(contour, cv2.COLOR_GRAY2RGB)
+
+ # fix breaking points on the contour
+ break_points = []
+ for y in range(1, contour.shape[0]-1):
+ for x in range(1, contour.shape[1]-1):
+ if contour[y][x] == 0.0:
+ num_ = getNumberOfValidPixels(contour, x, y)
+ if num_ == 1:
+ print((x, y))
+ break_points.append((x, y))
+ if len(break_points) != 0:
+ contour = cv2.line(contour, break_points[0], break_points[1], color=0, thickness=1)
+ cv2.imshow("c", contour)
+
+ # order the contour points
+ contour_points_ordered = sortPointsOnContourOfImage(contour)
+ # contour_points_counter_clockwise = order_points(contour, isClockwise=False)
+ print("number of points in ordered contour: %d" % len(contour_points_ordered))
+ # print("counter clock: %d" % len(contour_points_counter_clockwise))
+
+ contour_rgb_clock = contour_rgb.copy()
+ contour_smooth_rgb_clock = contour_rgb.copy()
+ # contour_rgb_counter_clock = contour_rgb.copy()
+
+ # get key points on contour
+ corners = cv2.goodFeaturesToTrack(contour, 6, 0.01, 10)
+ corners = np.int0(corners)
+ print("number of key points on contour: %d" % len(corners))
+
+ index = 0
+ corner_points_ = []
+ for i in corners:
+ MAX_DIST = 10000
+ x,y = i.ravel()
+ pt_ = None
+ if (x, y-1) in contour_points_ordered:
+ pt_ = (x, y-1)
+ elif (x+1, y-1) in contour_points_ordered:
+ pt_ = (x+1, y-1)
+ elif (x+1, y) in contour_points_ordered:
+ pt_ = (x+1, y)
+ elif (x+1, y+1) in contour_points_ordered:
+ pt_ = (x+1, y+1)
+ elif (x, y+1) in contour_points_ordered:
+ pt_ = (x, y+1)
+ elif (x-1, y+1) in contour_points_ordered:
+ pt_ = (x-1, y+1)
+ elif (x-1, y) in contour_points_ordered:
+ pt_ = (x-1, y)
+ elif (x-1, y-1) in contour_points_ordered:
+ pt_ = (x-1, y-1)
+ else:
+ # find the nearest point on the contour
+ minx = 0
+ miny = 0
+ for cp in contour_points_ordered:
+ dist = math.sqrt((x-cp[0])**2 + (y-cp[1])**2)
+ if dist < MAX_DIST:
+ MAX_DIST = dist
+ minx = cp[0]
+ miny = cp[1]
+ pt_ = (minx, miny)
+ corner_points_.append(pt_)
+ cv2.circle(contour_rgb, (pt_[0], pt_[1]), 1, (0, 0, 255), -1)
+ cv2.putText(contour_rgb, str(index), (pt_[0], pt_[1]), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,0,0),2,cv2.LINE_AA)
+ index += 1
+ print("orignal corner points number: %d" % len(corner_points_))
+ # order the corner points in the clockwise direction
+ corner_points = []
+ index = 0
+ for pt in contour_points_ordered:
+ if pt in corner_points_:
+ corner_points.append(pt)
+ cv2.circle(contour_rgb_clock, (pt[0], pt[1]), 3, (255, 0, 0), -1)
+ cv2.putText(contour_rgb_clock, str(index), (pt[0], pt[1]), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2,
+ cv2.LINE_AA)
+ index += 1
+ print("corner points len: %d" % len(corner_points))
+ # contour segmentation based on the corner points
+ contour_lines = []
+ for id in range(len(corner_points)):
+ start_point = corner_points[id]
+ end_point = start_point
+ if id == len(corner_points) - 1:
+ end_point = corner_points[0]
+ else:
+ end_point = corner_points[id+1]
+ # contour segmentation
+ contour_segmentation = []
+ start_index = contour_points_ordered.index(start_point)
+ end_index = contour_points_ordered.index(end_point)
+
+ if start_index <= end_index:
+ # normal index
+ contour_segmentation = contour_points_ordered[start_index: end_index+1]
+ else:
+ # end is at
+ contour_segmentation = contour_points_ordered[start_index: len(contour_points_ordered)] + \
+ contour_points_ordered[0: end_index+1]
+ contour_lines.append(contour_segmentation)
+
+ print("number of contour segmentation: %d" % len(contour_lines))
+
+ # use different color to show the contour segmentation
+ for id in range(len(contour_lines)):
+ if id % 3 == 0:
+ # red lines
+ for pt in contour_lines[id]:
+ contour_rgb_clock[pt[1]][pt[0]] = (0, 0, 255)
+ elif id % 3 == 1:
+ # blue line
+ for pt in contour_lines[id]:
+ contour_rgb_clock[pt[1]][pt[0]] = (255, 0, 0)
+ elif id % 3 == 2:
+ # green line
+ for pt in contour_lines[id]:
+ contour_rgb_clock[pt[1]][pt[0]] = (0, 255, 0)
+
+ # original and smooth contour
+ smoothed_contour_points = []
+ for id in range(len(contour_lines)):
+ print("line index: %d" % id)
+
+ # original contour
+ for pt in contour_lines[id]:
+ contour_smooth_rgb_clock[pt[1]][pt[0]] = (0, 0, 255)
+
+ # smooth contour
+ li_points = np.array(contour_lines[id])
+
+ beziers = fitCurve(li_points, maxError=30)
+ print("len bezier: %d" % len(beziers))
+ # # print(beziers)
+ for bez in beziers:
+ print(len(bez))
+ bezier_points = draw_cubic_bezier(bez[0], bez[1], bez[2], bez[3])
+ for id in range(len(bezier_points) - 1):
+ start_pt = bezier_points[id]
+ end_pt = bezier_points[id + 1]
+ cv2.line(contour_smooth_rgb_clock, start_pt, end_pt, (255, 0, 0))
+ smoothed_contour_points += bezier_points
+
+ # fill color in contour with sorted smooth contour points
+ print(len(smoothed_contour_points))
+ smoothed_contour_points = np.array([smoothed_contour_points], "int32")
+ fill_contour_smooth = np.ones(img.shape) * 255
+ fill_contour_smooth = np.array(fill_contour_smooth, dtype=np.uint8)
+ fill_contour_smooth = cv2.fillPoly(fill_contour_smooth, smoothed_contour_points, 0)
+
+ cv2.imshow("src", img)
+ cv2.imshow("contour", contour)
+ # cv2.imshow("corners", contour_rgb)
+ cv2.imshow("contour clock", contour_rgb_clock)
+ cv2.imshow("smooth contour clock", contour_smooth_rgb_clock)
+ # cv2.imshow("contour counter clock", contour_rgb_counter_clock)
+ cv2.imshow("fill contour", fill_contour_smooth)
+
+ cv2.waitKey(0)
+ cv2.destroyAllWindows()
+
+
+def point_inside_polygon(x, y, poly, include_edges=True):
+ '''
+ Test if point (x,y) is inside polygon poly.
+
+ poly is N-vertices polygon defined as
+ [(x1,y1),...,(xN,yN)] or [(x1,y1),...,(xN,yN),(x1,y1)]
+ (function works fine in both cases)
+
+ Geometrical idea: point is inside polygon if horisontal beam
+ to the right from point crosses polygon even number of times.
+ Works fine for non-convex polygons.
+ '''
+ n = len(poly)
+ inside = False
+
+ p1x, p1y = poly[0]
+ for i in range(1, n + 1):
+ p2x, p2y = poly[i % n]
+ if p1y == p2y:
+ if y == p1y:
+ if min(p1x, p2x) <= x <= max(p1x, p2x):
+ # point is on horisontal edge
+ inside = include_edges
+ break
+ elif x < min(p1x, p2x): # point is to the left from current edge
+ inside = not inside
+ else: # p1y!= p2y
+ if min(p1y, p2y) <= y <= max(p1y, p2y):
+ xinters = (y - p1y) * (p2x - p1x) / float(p2y - p1y) + p1x
+
+ if x == xinters: # point is right on the edge
+ inside = include_edges
+ break
+
+ if x < xinters: # point is to the left from current edge
+ inside = not inside
+
+ p1x, p1y = p2x, p2y
+
+ return inside
+
+
+def bernstein_poly(i, n, t):
+ """
+ The Bernstein polynomial of n, i as a function of t
+ """
+
+ return comb(n, i) * ( t**(n-i) ) * (1 - t)**i
+
+
+def bezier_curve(points, nTimes=1000):
+ """
+ Given a set of control points, return the
+ bezier curve defined by the control points.
+
+ points should be a list of lists, or list of tuples
+ such as [ [1,1],
+ [2,3],
+ [4,5], ..[Xn, Yn] ]
+ nTimes is the number of time steps, defaults to 1000
+
+ See http://processingjs.nihongoresources.com/bezierinfo/
+ """
+
+ nPoints = len(points)
+ xPoints = np.array([p[0] for p in points])
+ yPoints = np.array([p[1] for p in points])
+
+ t = np.linspace(0.0, 1.0, nTimes)
+
+ polynomial_array = np.array([bernstein_poly(i, nPoints-1, t) for i in range(0, nPoints)])
+
+ xvals = np.dot(xPoints, polynomial_array)
+ yvals = np.dot(yPoints, polynomial_array)
+
+ return xvals, yvals
+
+def cubic_bezier_sum(t, w):
+ t2 = t * t
+ t3 = t2 * t
+ mt = 1 - t
+ mt2 = mt * mt
+ mt3 = mt2 * mt
+
+ return w[0] * mt3 + 3 * w[1] * mt2 * t + 3 * w[2] * mt * t2 + w[3] * t3
+
+
+def draw_cubic_bezier(p1, p2, p3, p4):
+ points = []
+ t = 0
+ while t < 1:
+ x = int(cubic_bezier_sum(t, (p1[0], p2[0], p3[0], p4[0])))
+ y = int(cubic_bezier_sum(t, (p1[1], p2[1], p3[1], p4[1])))
+
+ points.append((x, y))
+
+ t += 0.01
+ return points
+
+
+def binomial(i, n):
+ """
+ Binomal coefficient
+ :param i:
+ :param n:
+ :return:
+ """
+ return math.factorial(n) / float(
+ math.factorial(i) * math.factorial(n - i)
+ )
+
+
+def bernstein(t, i, n):
+ """
+ Bernstein polymn
+ :param t:
+ :param i:
+ :param n:
+ :return:
+ """
+ return binomial(i, n) * (t ** i) * ((1-t) * (n-i))
+
+
+def bezier(t, points):
+ """
+ Calculate coordinate of a point in the bezier curve
+ :param t:
+ :param points:
+ :return:
+ """
+ n = len(points) - 1
+ x = y = 0
+ for i, pos in enumerate(points):
+ bern = bernstein(t, i, n)
+ x += pos[0] * bern
+ y += pos[1] * bern
+ return x, y
+
+
+def bezier_curve_range(n, points):
+ """
+ Range of points in a curve bezier
+ :param n:
+ :param points:
+ :return:
+ """
+ for i in range(n):
+ t = i / float(n - 1)
+ yield bezier(t, points)
+
+
+def cubic_bezier_sum(t, w):
+ t2 = t * t
+ t3 = t2 * t
+ mt = 1 - t
+ mt2 = mt * mt
+ mt3 = mt2 * mt
+
+ return w[0]*mt3 + 3*w[1]*mt2*t + 3*w[2]*mt*t2 + w[3]*t3
+
+
+def draw_cubic_bezier(p1, p2, p3, p4):
+ points = []
+ t = 0
+ while t < 1:
+ x = int(cubic_bezier_sum(t, (p1[0], p2[0], p3[0], p4[0])))
+ y = int(cubic_bezier_sum(t, (p1[1], p2[1], p3[1], p4[1])))
+
+ points.append((x, y))
+
+ t += 0.01
+ return points
+
+
+def fitCurve(points, maxError):
+ leftTangent = normalize(points[1] - points[0])
+ rightTanget = normalize(points[-2] - points[-1])
+ return fitCubic(points, leftTangent, rightTanget, maxError)
+
+
+def fitCubic(points, leftTangent, rightTangent, error):
+ # Use heuristic if region only has two points in it
+ if len(points) == 2:
+ dist = np.linalg.norm(points[0] - points[1]) / 3.
+ bezCurve = [points[0], points[0]+leftTangent*dist, points[1]+rightTangent*dist, points[1]]
+ return [bezCurve]
+
+ # Parameterize points, and attempt to fit curve
+ u = chordLengthParameterize(points)
+ bezCurve = generateBezier(points, u, leftTangent, rightTangent)
+ # Find max deviation of points to fitted curve
+ maxError, splitPoint = computeMaxError(points, bezCurve, u)
+ if maxError < error:
+ return [bezCurve]
+
+ # If error not too large, try some reparameterization and iteration
+ if maxError < error**2:
+ for i in range(20):
+ uPrime = reparameterize(bezCurve, points, u)
+ bezCurve = generateBezier(points, uPrime, leftTangent, rightTangent)
+ maxError, splitPoint = computeMaxError(points, bezCurve, uPrime)
+ if maxError < error:
+ return [bezCurve]
+ u = uPrime
+
+ # Fitting failed -- split at max error point and fit recursively
+ beziers = []
+ centerTangent = normalize(points[splitPoint-1] - points[splitPoint+1])
+ beziers += fitCubic(points[:splitPoint+1], leftTangent, centerTangent, error)
+ beziers += fitCubic(points[splitPoint:], -centerTangent, rightTangent, error)
+
+ return beziers
+
+
+def generateBezier(points, parameters, leftTangent, rightTangent):
+ bezCurve = [points[0], None, None, points[-1]]
+
+ # compute the A's
+ A = np.zeros((len(parameters), 2, 2))
+ for i, u in enumerate(parameters):
+ A[i][0] = leftTangent * 3 * (1-u)**2 *u
+ A[i][1] = rightTangent * 3 * (1-u) * u**2
+
+ # Create the C and X matrics
+ C = np.zeros((2, 2))
+ X = np.zeros(2)
+
+ for i, (point, u) in enumerate(zip(points, parameters)):
+ C[0][0] += np.dot(A[i][0], A[i][0])
+ C[0][1] += np.dot(A[i][0], A[i][1])
+ C[1][0] += np.dot(A[i][0], A[i][1])
+ C[1][1] += np.dot(A[i][1], A[i][1])
+
+ tmp = point - q([points[0], points[0], points[-1], points[-1]], u)
+ X[0] += np.dot(A[i][0], tmp)
+ X[1] += np.dot(A[i][1], tmp)
+
+ # compute the determinants of C and X
+ det_C0_C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1]
+ det_C0_X = C[0][0] * X[1] - C[1][0] * X[0]
+ det_X_C1 = X[0] * C[1][1] - X[1] * C[0][1]
+
+ # Finally, derive alpha values
+ alpha_l = 0.0 if det_C0_C1 == 0 else det_X_C1 / det_C0_C1
+ alpha_r = 0.0 if det_C0_C1 == 0 else det_C0_X / det_C0_C1
+
+ # If alpha negative, use the Wu/Barsky heuristic (see text) */
+ # (if alpha is 0, you get coincident control points that lead to
+ # divide by zero in any subsequent NewtonRaphsonRootFind() call. */
+ segLength = np.linalg.norm(points[0] - points[-1])
+ epsilon = 1.0e-6 * segLength
+ if alpha_l < epsilon or alpha_r < epsilon:
+ # fall back on standard (probably inaccurate) formula, and subdivide further if needed.
+ bezCurve[1] = bezCurve[0] + leftTangent * (segLength / 3.)
+ bezCurve[2] = bezCurve[3] + rightTangent * (segLength / 3.)
+ else:
+ # First and last control points of the Bezier curve are
+ # positioned exactly at the first and last data points
+ # Control points 1 and 2 are positioned an alpha distance out
+ # on the tangent vectors, left and right, respectively
+ bezCurve[1] = bezCurve[0] + leftTangent * alpha_l
+ bezCurve[2] = bezCurve[3] + rightTangent * alpha_r
+
+ return bezCurve
+
+
+def reparameterize(bezier, points, parameters):
+ return [newtonRaphsonRootFind(bezier, point, u) for point, u in zip(points, parameters)]
+
+
+def newtonRaphsonRootFind(bez, point, u):
+ """
+ Newton's root finding algorithm calculates f(x)=0 by reiterating
+ x_n+1 = x_n - f(x_n)/f'(x_n)
+ We are trying to find curve parameter u for some point p that minimizes
+ the distance from that point to the curve. Distance point to curve is d=q(u)-p.
+ At minimum distance the point is perpendicular to the curve.
+ We are solving
+ f = q(u)-p * q'(u) = 0
+ with
+ f' = q'(u) * q'(u) + q(u)-p * q''(u)
+ gives
+ u_n+1 = u_n - |q(u_n)-p * q'(u_n)| / |q'(u_n)**2 + q(u_n)-p * q''(u_n)|
+ :param bez:
+ :param point:
+ :param u:
+ :return:
+ """
+ d = q(bez, u) - point
+ numerator = (d * qprime(bez, u)).sum()
+ denominator = (qprime(bez, u)**2 + d * qprimeprime(bez, u)).sum()
+ if denominator == 0.0:
+ return u
+ else:
+ return u - numerator / denominator
+
+def chordLengthParameterize(points):
+ u = [0.0]
+ for i in range(1, len(points)):
+ u.append(u[i-1] + np.linalg.norm(points[i] - points[i-1]))
+
+ for i, _ in enumerate(u):
+ u[i] = u[i] / u[-1]
+ return u
+
+def computeMaxError(points, bez, parameters):
+ maxDist = 0.0
+ splitPoint = len(points) / 2
+ for i, (point, u) in enumerate(zip(points, parameters)):
+ dist = np.linalg.norm(q(bez, u)-point)**2
+ if dist > maxDist:
+ maxDist = dist
+ splitPoint = i
+
+ return maxDist, splitPoint
+
+
+def normalize(v):
+ return v / np.linalg.norm(v)
+
+
+# evaluates cubic bezier at t, return point
+def q(ctrlPoly, t):
+ return (1.0-t)**3 * ctrlPoly[0] + 3*(1.0-t)**2 * t * ctrlPoly[1] + 3*(1.0-t)* t**2 * ctrlPoly[2] + t**3 * ctrlPoly[3]
+
+
+# evaluates cubic bezier first derivative at t, return point
+def qprime(ctrlPoly, t):
+ return 3*(1.0-t)**2 * (ctrlPoly[1]-ctrlPoly[0]) + 6*(1.0-t) * t * (ctrlPoly[2]-ctrlPoly[1]) + 3*t**2 * (ctrlPoly[3]-ctrlPoly[2])
+
+
+# evaluates cubic bezier second derivative at t, return point
+def qprimeprime(ctrlPoly, t):
+ return 6*(1.0-t) * (ctrlPoly[2]-2*ctrlPoly[1]+ctrlPoly[0]) + 6*(t) * (ctrlPoly[3]-2*ctrlPoly[2]+ctrlPoly[1])
+
+
+TOLERANCE = 10e-6
+EPSILON = 10e-12
+
+
+class Point:
+ __slots__ = ['x', 'y']
+
+ def __init__(self, x, y=None):
+ if y is None:
+ self.x, self.y = x[0], x[1]
+ else:
+ self.x, self.y = x, y
+
+ def __repr__(self):
+ return 'Point(%r, %r)' % (self.x, self.y)
+
+ def __str__(self):
+ return '%G,%G' % (self.x, self.y)
+
+ def __complex__(self):
+ return complex(self.x, self.y)
+
+ def __hash__(self):
+ return hash(self.__complex__())
+
+ def __bool__(self):
+ return bool(self.x or self.y)
+
+ def __add__(self, other):
+ if isinstance(other, Point):
+ return Point(self.x + other.x, self.y + other.y)
+ else:
+ return Point(self.x + other, self.y + other)
+
+ def __sub__(self, other):
+ if isinstance(other, Point):
+ return Point(self.x - other.x, self.y - other.y)
+ else:
+ return Point(self.x - other, self.y - other)
+
+ def __mul__(self, other):
+ if isinstance(other, Point):
+ return Point(self.x * other.x, self.y * other.y)
+ else:
+ return Point(self.x * other, self.y * other)
+
+ def __truediv__(self, other):
+ if isinstance(other, Point):
+ return Point(self.x / other.x, self.y / other.y)
+ else:
+ return Point(self.x / other, self.y / other)
+
+ def __neg__(self):
+ return Point(-self.x, -self.y)
+
+ def __len__(self):
+ return math.hypot(self.x, self.y)
+
+ def __eq__(self, other):
+ try:
+ return self.x == other.x and self.y == other.y
+ except Exception:
+ return False
+
+ def __ne__(self, other):
+ try:
+ return self.x != other.x or self.y != other.y
+ except Exception:
+ return True
+
+ add = __add__
+ subtract = __sub__
+ multiply = __mul__
+ divide = __truediv__
+ negate = __neg__
+ getLength = __len__
+ equals = __eq__
+
+ def copy(self):
+ return Point(self.x, self.y)
+
+ def dot(self, other):
+ return self.x * other.x + self.y * other.y
+
+ def normalize(self, length=1):
+ current = self.__len__()
+ scale = length / current if current != 0 else 0
+ return Point(self.x * scale, self.y * scale)
+
+ def getDistance(self, other):
+ return math.hypot(self.x - other.x, self.y - other.y)
+
+
+class Segment:
+
+ def __init__(self, *args):
+ self.point = Point(0, 0)
+ self.handleIn = Point(0, 0)
+ self.handleOut = Point(0, 0)
+ if len(args) == 1:
+ if isinstance(args[0], Segment):
+ self.point = args[0].point
+ self.handleIn = args[0].handleIn
+ self.handleOut = args[0].handleOut
+ else:
+ self.point = args[0]
+ elif len(args) == 2 and isinstance(args[0], (int, float)):
+ self.point = Point(*args)
+ elif len(args) == 2:
+ self.point = args[0]
+ self.handleIn = args[1]
+ elif len(args) == 3:
+ self.point = args[0]
+ self.handleIn = args[1]
+ self.handleOut = args[2]
+ else:
+ self.point = Point(args[0], args[1])
+ self.handleIn = Point(args[2], args[3])
+ self.handleOut = Point(args[4], args[5])
+
+ def __repr__(self):
+ return 'Segment(%r, %r, %r)' % (self.point, self.handleIn, self.handleOut)
+
+ def __hash__(self):
+ return hash((self.point, self.handleIn, self.handleOut))
+
+ def __bool__(self):
+ return bool(self.point or self.handleIn or self.handleOut)
+
+ def getPoint(self):
+ return self.point
+
+ def setPoint(self, other):
+ self.point = other
+
+ def getHandleIn(self):
+ return self.handleIn
+
+ def setHandleIn(self, other):
+ self.handleIn = other
+
+ def getHandleOut(self):
+ return self.handleOut
+
+ def setHandleOut(self, other):
+ self.handleOut = other
+
+
+class PathFitter:
+
+ def __init__(self, segments, error=2.5):
+ self.points = []
+ # Copy over points from path and filter out adjacent duplicates.
+ l = len(segments)
+ prev = None
+ for i in range(l):
+ point = segments[i].point.copy()
+ if prev != point:
+ self.points.append(point)
+ prev = point
+ self.error = error
+
+ def fit(self):
+ points = self.points
+ length = len(points)
+ self.segments = [Segment(points[0])] if length > 0 else []
+ if length > 1:
+ self.fitCubic(0, length - 1,
+ # Left Tangent
+ points[1].subtract(points[0]).normalize(),
+ # Right Tangent
+ points[length - 2].subtract(points[length - 1]).normalize())
+ return self.segments
+
+ # Fit a Bezier curve to a (sub)set of digitized points
+ def fitCubic(self, first, last, tan1, tan2):
+ # Use heuristic if region only has two points in it
+ if last - first == 1:
+ pt1 = self.points[first]
+ pt2 = self.points[last]
+ dist = pt1.getDistance(pt2) / 3
+ self.addCurve([pt1, pt1 + tan1.normalize(dist),
+ pt2 + tan2.normalize(dist), pt2])
+ return
+ # Parameterize points, and attempt to fit curve
+ uPrime = self.chordLengthParameterize(first, last)
+ maxError = max(self.error, self.error * self.error)
+ # Try 4 iterations
+ for i in range(5):
+ curve = self.generateBezier(first, last, uPrime, tan1, tan2)
+ # Find max deviation of points to fitted curve
+ maxerr, maxind = self.findMaxError(first, last, curve, uPrime)
+ if maxerr < self.error:
+ self.addCurve(curve)
+ return
+ split = maxind
+ # If error not too large, try reparameterization and iteration
+ if maxerr >= maxError:
+ break
+ self.reparameterize(first, last, uPrime, curve)
+ maxError = maxerr
+ # Fitting failed -- split at max error point and fit recursively
+ V1 = self.points[split - 1].subtract(self.points[split])
+ V2 = self.points[split] - self.points[split + 1]
+ tanCenter = V1.add(V2).divide(2).normalize()
+ self.fitCubic(first, split, tan1, tanCenter)
+ self.fitCubic(split, last, tanCenter.negate(), tan2)
+
+ def addCurve(self, curve):
+ prev = self.segments[len(self.segments) - 1]
+ prev.setHandleOut(curve[1].subtract(curve[0]))
+ self.segments.append(
+ Segment(curve[3], curve[2].subtract(curve[3])))
+
+ # Use least-squares method to find Bezier control points for region.
+ def generateBezier(self, first, last, uPrime, tan1, tan2):
+ epsilon = 1e-11
+ pt1 = self.points[first]
+ pt2 = self.points[last]
+ # Create the C and X matrices
+ C = [[0, 0], [0, 0]]
+ X = [0, 0]
+
+ l = last - first + 1
+
+ for i in range(l):
+ u = uPrime[i]
+ t = 1 - u
+ b = 3 * u * t
+ b0 = t * t * t
+ b1 = b * t
+ b2 = b * u
+ b3 = u * u * u
+ a1 = tan1.normalize(b1)
+ a2 = tan2.normalize(b2)
+ tmp = (self.points[first + i]
+ - pt1.multiply(b0 + b1)
+ - pt2.multiply(b2 + b3))
+ C[0][0] += a1.dot(a1)
+ C[0][1] += a1.dot(a2)
+ # C[1][0] += a1.dot(a2)
+ C[1][0] = C[0][1]
+ C[1][1] += a2.dot(a2)
+ X[0] += a1.dot(tmp)
+ X[1] += a2.dot(tmp)
+
+ # Compute the determinants of C and X
+ detC0C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1]
+ if abs(detC0C1) > epsilon:
+ # Kramer's rule
+ detC0X = C[0][0] * X[1] - C[1][0] * X[0]
+ detXC1 = X[0] * C[1][1] - X[1] * C[0][1]
+ # Derive alpha values
+ alpha1 = detXC1 / detC0C1
+ alpha2 = detC0X / detC0C1
+ else:
+ # Matrix is under-determined, try assuming alpha1 == alpha2
+ c0 = C[0][0] + C[0][1]
+ c1 = C[1][0] + C[1][1]
+ if abs(c0) > epsilon:
+ alpha1 = alpha2 = X[0] / c0
+ elif abs(c1) > epsilon:
+ alpha1 = alpha2 = X[1] / c1
+ else:
+ # Handle below
+ alpha1 = alpha2 = 0
+
+ # If alpha negative, use the Wu/Barsky heuristic (see text)
+ # (if alpha is 0, you get coincident control points that lead to
+ # divide by zero in any subsequent NewtonRaphsonRootFind() call.
+ segLength = pt2.getDistance(pt1)
+ epsilon *= segLength
+ if alpha1 < epsilon or alpha2 < epsilon:
+ # fall back on standard (probably inaccurate) formula,
+ # and subdivide further if needed.
+ alpha1 = alpha2 = segLength / 3
+
+ # First and last control points of the Bezier curve are
+ # positioned exactly at the first and last data points
+ # Control points 1 and 2 are positioned an alpha distance out
+ # on the tangent vectors, left and right, respectively
+ return [pt1, pt1.add(tan1.normalize(alpha1)),
+ pt2.add(tan2.normalize(alpha2)), pt2]
+
+ # Given set of points and their parameterization, try to find
+ # a better parameterization.
+ def reparameterize(self, first, last, u, curve):
+ for i in range(first, last + 1):
+ u[i - first] = self.findRoot(curve, self.points[i], u[i - first])
+
+ # Use Newton-Raphson iteration to find better root.
+ def findRoot(self, curve, point, u):
+ # Generate control vertices for Q'
+ curve1 = [
+ curve[i + 1].subtract(curve[i]).multiply(3) for i in range(3)]
+ # Generate control vertices for Q''
+ curve2 = [
+ curve1[i + 1].subtract(curve1[i]).multiply(2) for i in range(2)]
+ # Compute Q(u), Q'(u) and Q''(u)
+ pt = self.evaluate(3, curve, u)
+ pt1 = self.evaluate(2, curve1, u)
+ pt2 = self.evaluate(1, curve2, u)
+ diff = pt - point
+ df = pt1.dot(pt1) + diff.dot(pt2)
+ # Compute f(u) / f'(u)
+ if abs(df) < TOLERANCE:
+ return u
+ # u = u - f(u) / f'(u)
+ return u - diff.dot(pt1) / df
+
+ # Evaluate a bezier curve at a particular parameter value
+ def evaluate(self, degree, curve, t):
+ # Copy array
+ tmp = curve[:]
+ # Triangle computation
+ for i in range(1, degree + 1):
+ for j in range(degree - i + 1):
+ tmp[j] = tmp[j].multiply(1 - t) + tmp[j + 1].multiply(t)
+ return tmp[0]
+
+ # Assign parameter values to digitized points
+ # using relative distances between points.
+ def chordLengthParameterize(self, first, last):
+ u = {0: 0}
+ print(first, last)
+ for i in range(first + 1, last + 1):
+ u[i - first] = u[i - first - 1] + \
+ self.points[i].getDistance(self.points[i - 1])
+ m = last - first
+ for i in range(1, m + 1):
+ u[i] /= u[m]
+ return u
+
+ # Find the maximum squared distance of digitized points to fitted curve.
+ def findMaxError(self, first, last, curve, u):
+ index = math.floor((last - first + 1) / 2)
+ maxDist = 0
+ for i in range(first + 1, last):
+ P = self.evaluate(3, curve, u[i - first])
+ v = P.subtract(self.points[i])
+ dist = v.x * v.x + v.y * v.y # squared
+ if dist >= maxDist:
+ maxDist = dist
+ index = i
+ return maxDist, index
+
+
+def fitpath(pointlist, error):
+ return PathFitter(list(map(Segment, map(Point, pointlist))), error).fit()
+
+
+def fitpathsvg(pointlist, error):
+ return pathtosvg(PathFitter(list(map(Segment, map(Point, pointlist))), error).fit())
+
+
+def pathtosvg(path):
+ segs = ['M', str(path[0].point)]
+ last = path[0]
+ for seg in path[1:]:
+ segs.append('C')
+ segs.append(str(last.point + last.handleOut))
+ segs.append(str(seg.point + seg.handleIn))
+ segs.append(str(seg.point))
+ last = seg
+ return ' '.join(segs)
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/test/convexHullTool.py b/test/convexHullTool.py
new file mode 100644
index 0000000..f56d768
--- /dev/null
+++ b/test/convexHullTool.py
@@ -0,0 +1,65 @@
+import cv2
+import numpy as np
+
+from utils.Functions import getConvexHullOfImage, calculatePolygonArea, calculateValidPixelsArea, addIntersectedFig
+
+
+def main():
+ src_path = "../chars/src_dan_svg_simple_resized.png"
+ tag_path = "../chars/tag_dan_svg_simple_resized.png"
+
+ src_img = cv2.imread(src_path, 0)
+ tag_img = cv2.imread(tag_path, 0)
+
+ _, src_img = cv2.threshold(src_img, 127, 255, cv2.THRESH_BINARY)
+ _, tag_img = cv2.threshold(tag_img, 127, 255, cv2.THRESH_BINARY)
+
+ src_l = getConvexHullOfImage(src_img)
+ tag_l = getConvexHullOfImage(tag_img)
+
+ print("src L len: %d" % len(src_l))
+ print("tag L len: %d" % len(tag_l))
+
+ src_img_rgb = cv2.cvtColor(src_img, cv2.COLOR_GRAY2RGB)
+ tag_img_rgb = cv2.cvtColor(tag_img, cv2.COLOR_GRAY2RGB)
+
+ # Source convex hull
+ for idx in range(len(src_l)):
+ if idx+1 == len(src_l):
+ src_img_rgb = cv2.line(src_img_rgb, (src_l[idx][0], src_l[idx][1]), (src_l[0][0], src_l[0][1]), (0, 0, 255), 2)
+ else:
+ src_img_rgb = cv2.line(src_img_rgb, (src_l[idx][0], src_l[idx][1]), (src_l[idx+1][0], src_l[idx+1][1]), (0, 0, 255), 2)
+ src_convexhull_area = calculatePolygonArea(src_l)
+ src_valid_area = calculateValidPixelsArea(src_img)
+ src_area_ratio = src_convexhull_area / (src_img.shape[0] * src_img.shape[1]) * 100
+ src_valid_ratio = src_valid_area / src_convexhull_area * 100
+ print("src area: %0.3f (%0.3f)" % (src_convexhull_area, src_area_ratio))
+ print("src valid area: %0.3f (%0.3f)" % (src_valid_area, src_valid_ratio))
+
+
+ # Target convex hull
+ for idx in range(len(tag_l)):
+ if idx+1 == len(tag_l):
+ tag_img_rgb = cv2.line(tag_img_rgb, (tag_l[idx][0], tag_l[idx][1]), (tag_l[0][0], tag_l[0][1]), (0, 0, 255), 2)
+ else:
+ tag_img_rgb = cv2.line(tag_img_rgb, (tag_l[idx][0], tag_l[idx][1]), (tag_l[idx+1][0], tag_l[idx+1][1]), (0, 0, 255), 2)
+ tag_convexhull_area = calculatePolygonArea(tag_l)
+ tag_valid_area = calculateValidPixelsArea(tag_img)
+ tag_area_ratio = tag_convexhull_area / (tag_img.shape[0] * tag_img.shape[1]) * 100
+ tag_valid_ratio = tag_valid_area / tag_convexhull_area * 100
+ print("tag area: %0.3f (%0.3f)" % (tag_convexhull_area, tag_area_ratio))
+ print("tag valid area: %0.3f (%0.3f)" % (tag_valid_area, tag_valid_ratio))
+
+ #
+ src_img_rgb_ = addIntersectedFig(src_img_rgb)
+ tag_img_rgb_ = addIntersectedFig(tag_img_rgb)
+
+ cv2.imshow("src", src_img_rgb_)
+ cv2.imshow("tag", tag_img_rgb_)
+
+ cv2.waitKey(0)
+ cv2.destroyAllWindows()
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/test/coverageratetool.py b/test/coverageratetool.py
new file mode 100644
index 0000000..e1c4973
--- /dev/null
+++ b/test/coverageratetool.py
@@ -0,0 +1,57 @@
+import cv2
+from utils.Functions import resizeImages, coverTwoImages, shiftImageWithMaxCR, calculateCoverageRate, addIntersectedFig, addSquaredFig
+
+
+def main():
+ # src_path = "../chars/src_dan_svg_simple_resized.png"
+ # tag_path = "../chars/tag_dan_svg_simple_resized.png"
+ src_path = "../strokes/src_strokes4.png"
+ tag_path = "../strokes/tag_strokes4.png"
+
+ # src_path = "../strokes/src_strokes1.png"
+ # tag_path = "../strokes/tag_strokes1.png"
+
+ src_img = cv2.imread(src_path, 0)
+ tag_img = cv2.imread(tag_path, 0)
+
+ ret, src_img = cv2.threshold(src_img, 127, 255, cv2.THRESH_BINARY)
+ ret, tag_img = cv2.threshold(tag_img, 127, 255, cv2.THRESH_BINARY)
+
+ # resize
+ src_img, tag_img = resizeImages(src_img, tag_img)
+
+ # Threshold
+ ret, src_img = cv2.threshold(src_img, 127, 255, cv2.THRESH_BINARY)
+ ret, tag_img = cv2.threshold(tag_img, 127, 255, cv2.THRESH_BINARY)
+
+ # cv2.imwrite("src_resize.png", src_img)
+ # cv2.imwrite("tag_resize.png", tag_img)
+
+ # Cover Images
+
+ coverage_img = coverTwoImages(src_img, tag_img)
+ cr = calculateCoverageRate(src_img, tag_img)
+ print("No shifting cr: %f" % cr)
+
+ # coverage_img = addIntersectedFig(coverage_img)
+ # coverage_img = addSquaredFig(coverage_img)
+
+ # Shift images with max CR
+ new_tag_img = shiftImageWithMaxCR(src_img, tag_img)
+
+ # Cover images
+ coverage_img1 = coverTwoImages(src_img, new_tag_img)
+ cr = calculateCoverageRate(src_img, new_tag_img)
+ print("Shifting cr: %f" % cr)
+
+ coverage_img_ = addIntersectedFig(coverage_img)
+ coverage_img1_ = addIntersectedFig(coverage_img1)
+
+ cv2.imshow("coverage img", coverage_img_)
+ cv2.imshow("new coverage img", coverage_img1_)
+ cv2.waitKey(0)
+ cv2.destroyAllWindows()
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/test/creatSimSunFontImages.py b/test/creatSimSunFontImages.py
new file mode 100644
index 0000000..f886f9a
--- /dev/null
+++ b/test/creatSimSunFontImages.py
@@ -0,0 +1,66 @@
+# coding: utf-8
+
+from __future__ import print_function
+from __future__ import absolute_import
+
+import argparse
+import numpy as np
+import os
+
+from PIL import Image, ImageDraw, ImageFont
+
+import collections
+
+
+def load_charset(char_dir):
+ with open(char_dir, 'r') as f:
+ charset = f.readlines()
+ charset = [char.strip() for char in charset]
+ return charset
+
+def draw_single_char(ch, font, canvas_size, x_offset, y_offset):
+ img = Image.new("L", (canvas_size, canvas_size), 255)
+ draw = ImageDraw.Draw(img)
+ draw.text((x_offset, y_offset), ch, 0, font=font)
+ return img
+
+
+def draw_example(ch, font, canvas_size, x_offset, y_offset):
+ img = draw_single_char(ch, font, canvas_size, x_offset, y_offset)
+
+
+def font2image(font_dir, charset, char_size, canvas_size, x_offset, y_offset, sample_dir):
+ font = ImageFont.truetype(font_dir, size=char_size)
+
+ count = 0
+ for char in charset:
+ e = draw_single_char(char, font, canvas_size, x_offset, y_offset)
+ if e:
+ e.save(os.path.join(sample_dir, ("%04d" + char + ".jpg") % count))
+ count += 1
+ if count % 100 == 0:
+ print("process %d chars" % count)
+
+
+
+
+parser = argparse.ArgumentParser(description="Covert SimSun font to images")
+parser.add_argument("--char_dir", dest="char_dir", required=True, help="path of the characters")
+parser.add_argument("--font_dir", dest="font_dir", required=True, help="path of the source font")
+
+parser.add_argument("--char_size", dest="char_size", type=int, default=512, help="character size")
+parser.add_argument("--canvas_size", dest="canvas_size", type=int, default=512, help="canvas size")
+parser.add_argument("--x_offset", dest="x_offset", type=int, default=0, help="x offset")
+parser.add_argument("--y_offset", dest="y_offset", type=int, default=0, help="y offset")
+parser.add_argument("--sample_dir", dest="sample_dir", help="directory to save sample")
+
+args = parser.parse_args()
+
+if __name__ == '__main__':
+ if not os.path.exists(args.sample_dir):
+ os.mkdir(args.sample_dir)
+
+ charset = load_charset(args.char_dir)
+
+ font2image(args.font_dir, charset, args.char_size, args.canvas_size, args.x_offset, args.y_offset,
+ args.sample_dir)
diff --git a/test/cropTemplates.py b/test/cropTemplates.py
new file mode 100644
index 0000000..9a655d3
--- /dev/null
+++ b/test/cropTemplates.py
@@ -0,0 +1,99 @@
+import os
+import cv2
+import numpy as np
+from utils.Functions import getSingleMaxBoundingBoxOfImage
+
+
+def main():
+ base_path = "../templates/templates_comparison"
+
+ temp_roots = [f for f in os.listdir(base_path) if "." not in f]
+
+ for temp_rt in temp_roots:
+ temp_path = base_path + "/" + temp_rt + "/char/" + temp_rt + ".png"
+ print(temp_path)
+
+ if not os.path.exists(temp_path):
+ continue
+
+ temp_img = cv2.imread(temp_path, 0)
+ _, temp_img = cv2.threshold(temp_img, 127, 255, cv2.THRESH_BINARY)
+
+ x, y, w, h = getSingleMaxBoundingBoxOfImage(temp_img)
+
+ crop_img = temp_img[y: y+h, x: x+w]
+
+ crop_path = base_path + "/" + temp_rt + "/char/" + temp_rt + "_crop.png"
+ cv2.imwrite(crop_path, crop_img)
+
+ c0_x = int(crop_img.shape[1] / 2)
+ c0_y = int(crop_img.shape[0] / 2)
+
+ new_w = max(w, h) + int(0.1 * max(w, h))
+ new_h = new_w
+
+ c1_x = int(new_w / 2)
+ c1_y = int(new_h / 2)
+
+ # offset
+ offset_x = c1_x - c0_x
+ offset_y = c1_y - c0_y
+
+ new_img = np.ones((new_w, new_h)) * 255
+ new_img = np.array(new_img, dtype=np.uint8)
+
+ for y in range(crop_img.shape[0]):
+ for x in range(crop_img.shape[1]):
+ new_img[y+offset_y][x+offset_x] = crop_img[y][x]
+
+ resize_path = base_path + "/" + temp_rt + "/char/" + temp_rt + "_resize.png"
+
+ cv2.imwrite(resize_path, new_img)
+
+
+if __name__ == '__main__':
+ main()
+
+ # strokes_path = "../templates/templates/ben/strokes"
+ #
+ # files = [f for f in os.listdir(strokes_path) if '.png' in f]
+ #
+ # print(files)
+ #
+ # for file in files:
+ # file_path = strokes_path + "/" + file
+ #
+ # img = cv2.imread(file_path, 0)
+ #
+ # _, img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
+ #
+ # h, w = img.shape[0], img.shape[1]
+ #
+ # c0_x = int(w / 2)
+ # c0_y = int(h / 2)
+ #
+ # new_w = max(w, h) + int(0.1 * max(w, h))
+ # new_h = new_w
+ #
+ # c1_x = int(new_w / 2)
+ # c1_y = int(new_h / 2)
+ #
+ # # offset
+ # offset_x = c1_x - c0_x
+ # offset_y = c1_y - c0_y
+ #
+ # new_img = np.ones((new_w, new_h)) * 255
+ # new_img = np.array(new_img, dtype=np.uint8)
+ #
+ # for y in range(h):
+ # for x in range(w):
+ # new_img[y+offset_y][x+offset_x] = img[y][x]
+ #
+ # save_path = strokes_path + "/_" + file
+ # cv2.imwrite(save_path, new_img)
+
+
+
+
+
+
diff --git a/test/edgeandskeletontool.py b/test/edgeandskeletontool.py
new file mode 100644
index 0000000..26ef54c
--- /dev/null
+++ b/test/edgeandskeletontool.py
@@ -0,0 +1,66 @@
+import numpy as np
+import cv2
+
+
+def main():
+ src_path = "../strokes/src_strokes7.png"
+ tag_path = "../strokes/tag_strokes7.png"
+
+ src_img = cv2.imread(src_path, 0)
+ tag_img = cv2.imread(tag_path, 0)
+
+ _, src_img = cv2.threshold(src_img, 127, 255, cv2.THRESH_BINARY)
+ _, tag_img = cv2.threshold(tag_img, 127, 255, cv2.THRESH_BINARY)
+ src_img_ = 255 - src_img
+ tag_img_ = 255 - tag_img
+
+ src_edges = cv2.Canny(src_img, 100, 200)
+
+ tag_edges = cv2.Canny(tag_img, 100, 200)
+
+
+ # skeleton
+ src_skel = np.zeros(src_img.shape, np.uint8)
+ size = np.size(src_img)
+ element = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3))
+ done = False
+
+ while (not done):
+ eroded = cv2.erode(src_img_, element)
+ temp = cv2.dilate(eroded, element)
+ temp = cv2.subtract(src_img_, temp)
+ src_skel = cv2.bitwise_or(src_skel, temp)
+ src_img_ = eroded.copy()
+
+ zeros = size - cv2.countNonZero(src_img_)
+ if zeros == size:
+ done = True
+
+ tag_skel = np.zeros(tag_img.shape, np.uint8)
+ size = np.size(tag_img)
+ element = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3))
+ done = False
+
+ while (not done):
+ eroded = cv2.erode(tag_img_, element)
+ temp = cv2.dilate(eroded, element)
+ temp = cv2.subtract(tag_img_, temp)
+ tag_skel = cv2.bitwise_or(tag_skel, temp)
+ tag_img_ = eroded.copy()
+
+ zeros = size - cv2.countNonZero(tag_img_)
+ if zeros == size:
+ done = True
+
+ cv2.imshow("src edge", src_edges)
+ cv2.imshow("tag edge", tag_edges)
+
+ cv2.imshow("src skel", src_skel)
+ cv2.imshow("tag skel", tag_skel)
+
+ cv2.waitKey(0)
+ cv2.destroyAllWindows()
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/test/fun_test.py b/test/fun_test.py
new file mode 100644
index 0000000..35f20a7
--- /dev/null
+++ b/test/fun_test.py
@@ -0,0 +1,155 @@
+# from scipy.spatial import ConvexHull
+# import numpy as np
+# import matplotlib.pyplot as plt
+#
+#
+# def getTrangleArea(x0,y0,x1,y1,x2,y2):
+# area = 0.0
+#
+# return area
+#
+#
+# def getPolygonArea(points):
+# if points is None:
+# return 0.0
+# area = 0.0
+# i = j = len(points)-1
+#
+# for i in range(len(points)):
+# area += (points[j][1] + points[i][1]) * (points[j][0] - points[i][0])
+# j = i
+#
+# return area * 0.5
+#
+# points = [(0,0),(1,0),(1,1),(2,2),(0,2),(0,1)]
+# area = getPolygonArea(points)
+# print("area: %f" % area)
+#!/usr/bin/env python
+
+# embedding_in_qt5.py --- Simple Qt4 application embedding matplotlib canvases
+#
+# Copyright (C) 2005 Florent Rougon
+# 2006 Darren Dale
+# with Updates from @boxcontrol
+#
+#
+#
+# This file is an example program for matplotlib. It may be used and
+# modified with no restriction; raw copies as well as modified versions
+# may be distributed without limitation.
+
+import sys
+import random
+import matplotlib
+matplotlib.use("Qt5Agg")
+from PyQt5 import QtCore
+from PyQt5.QtWidgets import QApplication, QMainWindow, QMenu, QVBoxLayout, QSizePolicy, QMessageBox, QWidget
+from numpy import arange, sin, pi
+from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
+from matplotlib.figure import Figure
+
+
+class MyMplCanvas(FigureCanvas):
+ """Ultimately, this is a QWidget (as well as a FigureCanvasAgg, etc.)."""
+ def __init__(self, parent=None, width=5, height=4, dpi=100):
+ fig = Figure(figsize=(width, height), dpi=dpi)
+ self.axes = fig.add_subplot(111)
+ # We want the axes cleared every time plot() is called
+ self.axes.hold(False)
+
+ self.compute_initial_figure()
+
+ #
+ FigureCanvas.__init__(self, fig)
+ self.setParent(parent)
+
+ FigureCanvas.setSizePolicy(self,
+ QSizePolicy.Expanding,
+ QSizePolicy.Expanding)
+ FigureCanvas.updateGeometry(self)
+
+ def compute_initial_figure(self):
+ pass
+
+class MyStaticMplCanvas(MyMplCanvas):
+ """Simple canvas with a sine plot."""
+ def compute_initial_figure(self):
+ t = arange(0.0, 3.0, 0.01)
+ s = sin(2*pi*t)
+ self.axes.plot(t, s)
+
+
+class MyDynamicMplCanvas(MyMplCanvas):
+ """A canvas that updates itself every second with a new plot."""
+ def __init__(self, *args, **kwargs):
+ MyMplCanvas.__init__(self, *args, **kwargs)
+ timer = QtCore.QTimer(self)
+ timer.timeout.connect(self.update_figure)
+ timer.start(1000)
+
+ def compute_initial_figure(self):
+ self.axes.plot([0, 1, 2, 3], [1, 2, 0, 4], 'r')
+
+ def update_figure(self):
+ # Build a list of 4 random integers between 0 and 10 (both inclusive)
+ l = [random.randint(0, 10) for i in range(4)]
+
+ self.axes.plot([0, 1, 2, 3], l, 'r')
+ self.draw()
+
+class ApplicationWindow(QMainWindow):
+ def __init__(self):
+ QMainWindow.__init__(self)
+ self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
+ self.setWindowTitle("application main window")
+
+ self.file_menu = QMenu('&File', self)
+ self.file_menu.addAction('&Quit', self.fileQuit,
+ QtCore.Qt.CTRL + QtCore.Qt.Key_Q)
+ self.menuBar().addMenu(self.file_menu)
+
+ self.help_menu = QMenu('&Help', self)
+ self.menuBar().addSeparator()
+ self.menuBar().addMenu(self.help_menu)
+
+ self.help_menu.addAction('&About', self.about)
+
+ self.main_widget = QWidget(self)
+
+ l = QVBoxLayout(self.main_widget)
+ sc = MyStaticMplCanvas(self.main_widget, width=5, height=4, dpi=100)
+ dc = MyDynamicMplCanvas(self.main_widget, width=5, height=4, dpi=100)
+ l.addWidget(sc)
+ l.addWidget(dc)
+
+ self.main_widget.setFocus()
+ self.setCentralWidget(self.main_widget)
+
+ self.statusBar().showMessage("All hail matplotlib!", 2000)
+
+ def fileQuit(self):
+ self.close()
+
+ def closeEvent(self, ce):
+ self.fileQuit()
+
+ def about(self):
+ QMessageBox.about(self, "About",
+ """embedding_in_qt5.py example
+ Copyright 2015 BoxControL
+ This program is a simple example of a Qt5 application embedding matplotlib
+ canvases. It is base on example from matplolib documentation, and initially was
+ developed from Florent Rougon and Darren Dale.
+ http://matplotlib.org/examples/user_interfaces/embedding_in_qt4.html
+ It may be used and modified with no restriction; raw copies as well as
+ modified versions may be distributed without limitation."""
+ )
+
+if __name__ == '__main__':
+ app = QApplication(sys.argv)
+
+ aw = ApplicationWindow()
+ aw.setWindowTitle("PyQt5 Matplot Example")
+ aw.show()
+ #sys.exit(qApp.exec_())
+ app.exec_()
\ No newline at end of file
diff --git a/test/json_load_test.py b/test/json_load_test.py
new file mode 100644
index 0000000..8e72eb6
--- /dev/null
+++ b/test/json_load_test.py
@@ -0,0 +1,19 @@
+import json
+
+
+def main():
+ data_path = "../ProgrammWebScrapy.json"
+
+ with open(data_path, 'rb') as f:
+ lines = f.read()
+
+ for line in lines:
+ if "\n" in line:
+ print(repr(line))
+
+
+
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/main.py b/test/main.py
similarity index 100%
rename from main.py
rename to test/main.py
diff --git a/test/mainwindow.py b/test/mainwindow.py
new file mode 100644
index 0000000..d874ddc
--- /dev/null
+++ b/test/mainwindow.py
@@ -0,0 +1,115 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'mainwindow.ui'
+#
+# 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(1139, 766)
+ self.centralwidget = QtWidgets.QWidget(MainWindow)
+ self.centralwidget.setObjectName("centralwidget")
+ self.tabWidget = QtWidgets.QTabWidget(self.centralwidget)
+ self.tabWidget.setGeometry(QtCore.QRect(0, 30, 1131, 681))
+ self.tabWidget.setLayoutDirection(QtCore.Qt.LeftToRight)
+ self.tabWidget.setTabPosition(QtWidgets.QTabWidget.North)
+ self.tabWidget.setTabShape(QtWidgets.QTabWidget.Triangular)
+ self.tabWidget.setObjectName("tabWidget")
+ self.tab = QtWidgets.QWidget()
+ self.tab.setObjectName("tab")
+ self.groupBox = QtWidgets.QGroupBox(self.tab)
+ self.groupBox.setGeometry(QtCore.QRect(0, 0, 271, 671))
+ self.groupBox.setObjectName("groupBox")
+ self.pushButton = QtWidgets.QPushButton(self.groupBox)
+ self.pushButton.setGeometry(QtCore.QRect(10, 30, 113, 32))
+ self.pushButton.setObjectName("pushButton")
+ self.pushButton_2 = QtWidgets.QPushButton(self.groupBox)
+ self.pushButton_2.setGeometry(QtCore.QRect(10, 60, 113, 32))
+ self.pushButton_2.setObjectName("pushButton_2")
+ self.pushButton_3 = QtWidgets.QPushButton(self.groupBox)
+ self.pushButton_3.setGeometry(QtCore.QRect(10, 90, 113, 32))
+ self.pushButton_3.setObjectName("pushButton_3")
+ self.checkBox = QtWidgets.QCheckBox(self.groupBox)
+ self.checkBox.setGeometry(QtCore.QRect(170, 530, 91, 20))
+ self.checkBox.setObjectName("checkBox")
+ self.checkBox_2 = QtWidgets.QCheckBox(self.groupBox)
+ self.checkBox_2.setGeometry(QtCore.QRect(170, 550, 87, 20))
+ self.checkBox_2.setObjectName("checkBox_2")
+ self.pushButton_4 = QtWidgets.QPushButton(self.groupBox)
+ self.pushButton_4.setGeometry(QtCore.QRect(10, 130, 141, 32))
+ self.pushButton_4.setObjectName("pushButton_4")
+ self.pushButton_5 = QtWidgets.QPushButton(self.groupBox)
+ self.pushButton_5.setGeometry(QtCore.QRect(10, 160, 141, 32))
+ self.pushButton_5.setObjectName("pushButton_5")
+ self.pushButton_6 = QtWidgets.QPushButton(self.groupBox)
+ self.pushButton_6.setGeometry(QtCore.QRect(10, 190, 141, 32))
+ self.pushButton_6.setObjectName("pushButton_6")
+ self.graphicsView = QtWidgets.QGraphicsView(self.tab)
+ self.graphicsView.setGeometry(QtCore.QRect(290, 20, 381, 271))
+ self.graphicsView.setObjectName("graphicsView")
+ self.graphicsView_2 = QtWidgets.QGraphicsView(self.tab)
+ self.graphicsView_2.setGeometry(QtCore.QRect(710, 20, 381, 271))
+ self.graphicsView_2.setObjectName("graphicsView_2")
+ self.graphicsView_3 = QtWidgets.QGraphicsView(self.tab)
+ self.graphicsView_3.setGeometry(QtCore.QRect(290, 310, 381, 271))
+ self.graphicsView_3.setObjectName("graphicsView_3")
+ self.graphicsView_4 = QtWidgets.QGraphicsView(self.tab)
+ self.graphicsView_4.setGeometry(QtCore.QRect(710, 310, 381, 271))
+ self.graphicsView_4.setObjectName("graphicsView_4")
+ self.groupBox_2 = QtWidgets.QGroupBox(self.tab)
+ self.groupBox_2.setGeometry(QtCore.QRect(290, 590, 801, 80))
+ self.groupBox_2.setObjectName("groupBox_2")
+ self.tabWidget.addTab(self.tab, "")
+ self.tab_2 = QtWidgets.QWidget()
+ self.tab_2.setObjectName("tab_2")
+ self.tabWidget.addTab(self.tab_2, "")
+ MainWindow.setCentralWidget(self.centralwidget)
+ self.menubar = QtWidgets.QMenuBar(MainWindow)
+ self.menubar.setGeometry(QtCore.QRect(0, 0, 1139, 22))
+ self.menubar.setObjectName("menubar")
+ self.menuFile = QtWidgets.QMenu(self.menubar)
+ self.menuFile.setObjectName("menuFile")
+ self.menuExit = QtWidgets.QMenu(self.menubar)
+ self.menuExit.setObjectName("menuExit")
+ MainWindow.setMenuBar(self.menubar)
+ self.statusbar = QtWidgets.QStatusBar(MainWindow)
+ self.statusbar.setObjectName("statusbar")
+ MainWindow.setStatusBar(self.statusbar)
+ self.actionOpen = QtWidgets.QAction(MainWindow)
+ self.actionOpen.setObjectName("actionOpen")
+ self.actionExit = QtWidgets.QAction(MainWindow)
+ self.actionExit.setObjectName("actionExit")
+ self.menuFile.addAction(self.actionOpen)
+ self.menuExit.addAction(self.actionExit)
+ self.menubar.addAction(self.menuFile.menuAction())
+ self.menubar.addAction(self.menuExit.menuAction())
+
+ self.retranslateUi(MainWindow)
+ self.tabWidget.setCurrentIndex(0)
+ QtCore.QMetaObject.connectSlotsByName(MainWindow)
+
+ def retranslateUi(self, MainWindow):
+ _translate = QtCore.QCoreApplication.translate
+ MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
+ self.groupBox.setTitle(_translate("MainWindow", "Functions"))
+ self.pushButton.setText(_translate("MainWindow", "Pre-process"))
+ self.pushButton_2.setText(_translate("MainWindow", "Coverage Rate"))
+ self.pushButton_3.setText(_translate("MainWindow", "Gravity"))
+ self.checkBox.setText(_translate("MainWindow", "Intersected"))
+ self.checkBox_2.setText(_translate("MainWindow", "Squared"))
+ self.pushButton_4.setText(_translate("MainWindow", "Component Layout"))
+ self.pushButton_5.setText(_translate("MainWindow", "Convex hull"))
+ self.pushButton_6.setText(_translate("MainWindow", "Slope intersection"))
+ self.groupBox_2.setTitle(_translate("MainWindow", "Info"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "Golbal Features"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("MainWindow", "Strokes Features"))
+ self.menuFile.setTitle(_translate("MainWindow", "File"))
+ self.menuExit.setTitle(_translate("MainWindow", "Exit"))
+ self.actionOpen.setText(_translate("MainWindow", "Open"))
+ self.actionExit.setText(_translate("MainWindow", "Exit"))
+
diff --git a/test/mainwindow.ui b/test/mainwindow.ui
new file mode 100644
index 0000000..7568405
--- /dev/null
+++ b/test/mainwindow.ui
@@ -0,0 +1,98 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 1278
+ 667
+
+
+
+ MainWindow
+
+
+
+
+
+ 190
+ 10
+ 1031
+ 601
+
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 1029
+ 599
+
+
+
+
+
+ 0
+ 0
+ 1031
+ 601
+
+
+
+
+
+
+
+
+ 10
+ 10
+ 171
+ 221
+
+
+
+ -
+
+
+ Open
+
+
+
+ -
+
+
+ Extracting
+
+
+
+ -
+
+
+ Exit
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/mainwindow_stroke_extrcting.py b/test/mainwindow_stroke_extrcting.py
new file mode 100644
index 0000000..d36e7f4
--- /dev/null
+++ b/test/mainwindow_stroke_extrcting.py
@@ -0,0 +1,83 @@
+import sys
+from PyQt5.QtWidgets import QApplication, QWidget
+from PyQt5.QtGui import QPainter, QPixmap, QImage
+from PyQt5.QtCore import Qt, QPoint
+
+
+class Winform(QWidget):
+ def __init__(self, parent=None):
+ super(Winform, self).__init__(parent)
+ self.setWindowTitle("绘图例子")
+
+
+ self.pix = QPixmap()
+
+ self.lastPoint = QPoint()
+ self.endPoint = QPoint()
+ self.points = []
+
+ self.initUi()
+
+ def initUi(self):
+ image = QImage("ben.png")
+ # 窗口大小设置为600*500
+ self.resize(600, 500)
+ # 画布大小为400*400,背景为白色
+ # self.pix = QPixmap(400, 400)
+ # self.pix.fill(Qt.white)
+ self.pix = QPixmap.fromImage(image)
+
+ def paintEvent(self, event):
+ pp = QPainter(self.pix)
+
+ # Draw circle in end point
+ pp.drawEllipse(self.endPoint, 2, 2)
+
+ # 根据鼠标指针前后两个位置绘制直线
+ pp.drawLine(self.lastPoint, self.endPoint)
+ # 让前一个坐标值等于后一个坐标值,
+ # 这样就能实现画出连续的线
+ self.lastPoint = self.endPoint
+ painter = QPainter(self)
+ painter.drawPixmap(0, 0, self.pix)
+
+ print("len: %d" % len(self.points))
+
+ def mousePressEvent(self, event):
+ # 鼠标左键按下
+ if event.button() == Qt.LeftButton:
+ if self.lastPoint == None and self.endPoint == None:
+ self.lastPoint = event.pos()
+ self.endPoint = event.pos()
+ x = event.pos().x()
+ y = event.pos().y()
+
+ self.points.append((x, y))
+ # self.update()
+ # self.lastPoint = event.pos()
+ # self.endPoint = self.lastPoint
+
+ def mouseMoveEvent(self, event):
+ # 鼠标左键按下的同时移动鼠标
+ pass
+ # if event.buttons() and Qt.LeftButton:
+ # self.endPoint = event.pos()
+ # # 进行重新绘制
+ # self.update()
+
+ def mouseReleaseEvent(self, event):
+ # 鼠标左键释放
+ if event.button() == Qt.LeftButton:
+ self.endPoint = event.pos()
+ # 进行重新绘制
+ self.update()
+ # self.lastPoint = self.endPoint
+
+
+
+
+if __name__ == "__main__":
+ app = QApplication(sys.argv)
+ form = Winform()
+ form.show()
+ sys.exit(app.exec_())
\ No newline at end of file
diff --git a/test/polynomicalPredictionOfDegree1.py b/test/polynomicalPredictionOfDegree1.py
new file mode 100644
index 0000000..34d3c32
--- /dev/null
+++ b/test/polynomicalPredictionOfDegree1.py
@@ -0,0 +1,92 @@
+import numpy as np
+import cv2
+from matplotlib import pyplot as plt
+
+
+def main():
+ src_path = "src_resize.png"
+ tag_path = "tag_resize.png"
+
+ src_img = cv2.imread(src_path, 0)
+ tag_img = cv2.imread(tag_path, 0)
+
+ _, src_img = cv2.threshold(src_img, 127, 255, cv2.THRESH_BINARY)
+ _, tag_img = cv2.threshold(tag_img, 127, 255, cv2.THRESH_BINARY)
+
+ src_img_rgb = cv2.cvtColor(src_img, cv2.COLOR_GRAY2RGB)
+ tag_img_rgb = cv2.cvtColor(tag_img, cv2.COLOR_GRAY2RGB)
+
+ # src image polynomical prediction of degree 1
+ src_x_list = []
+ src_y_list = []
+ for y in range(src_img.shape[0]):
+ for x in range(src_img.shape[1]):
+ if src_img[y][x] == 0.0:
+ src_x_list.append(x)
+ src_y_list.append(y)
+
+ # z[0] = k, z[1] = b
+ z = np.polyfit(src_x_list, src_y_list, 1)
+ print(z)
+
+ # horizon or vertical
+ k = z[0]
+ b = z[1]
+ if k >= -1 and k <= 1:
+ # horizon
+ x0 = 0; xn = src_img.shape[1] - 1
+ y0 = int(k * x0 + b)
+ yn = int(k * xn + b)
+ src_img_rgb = cv2.line(src_img_rgb, (y0, x0), (yn, xn), (0, 0, 255), 2)
+ elif k < -1 or k > 1:
+ # vertical
+ y0 = 0; yn = src_img.shape[0] - 1
+ x0 = int(k * y0 + b)
+ xn = int(k * yn + b)
+ src_img_rgb = cv2.line(src_img_rgb, (y0, x0), (yn, xn), (0, 0, 255), 2)
+
+ # target image polynomical prediction of degree 1
+ tag_x_list = []
+ tag_y_list = []
+ for y in range(tag_img.shape[0]):
+ for x in range(tag_img.shape[1]):
+ if tag_img[y][x] == 0.0:
+ tag_x_list.append(x)
+ tag_y_list.append(y)
+
+ # z[0] = k, z[1] = b
+ z = np.polyfit(tag_x_list, tag_y_list, 1)
+ print(z)
+
+ # horizon or vertical
+ k = z[0]
+ b = z[1]
+ if k >= -1 and k <= 1:
+ # horizon
+ x0 = 0;
+ xn = tag_img.shape[1] - 1
+ y0 = int(k * x0 + b)
+ yn = int(k * xn + b)
+ tag_img_rgb = cv2.line(tag_img_rgb, (y0, x0), (yn, xn), (0, 0, 255), 2)
+ elif k < -1 or k > 1:
+ # vertical
+ y0 = 0;
+ yn = tag_img.shape[0] - 1
+ x0 = int(k * y0 + b)
+ xn = int(k * yn + b)
+ tag_img_rgb = cv2.line(tag_img_rgb, (y0, x0), (yn, xn), (0, 0, 255), 2)
+
+ cv2.imshow("src", src_img_rgb)
+ cv2.imshow("tag", tag_img_rgb)
+ cv2.waitKey(0)
+ cv2.destroyAllWindows()
+
+
+
+
+
+
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/test/radicalsExtractingTool.py b/test/radicalsExtractingTool.py
new file mode 100644
index 0000000..2a40694
--- /dev/null
+++ b/test/radicalsExtractingTool.py
@@ -0,0 +1,28 @@
+import cv2
+import numpy as np
+
+from utils.Functions import splitConnectedComponents
+
+def main():
+ img_path = "ben.png"
+
+ img = cv2.imread(img_path, 0)
+ _, img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
+
+ # extract radicals of character
+ radicals = splitConnectedComponents(img)
+
+ print("radicals len: %d" % len(radicals))
+
+ for id, rd in enumerate(radicals):
+ cv2.imshow("id:"+str(id), rd)
+
+
+ cv2.imshow("src", img)
+
+ cv2.waitKey(0)
+ cv2.destroyAllWindows()
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/test/resize_proceduretool.py b/test/resize_proceduretool.py
new file mode 100644
index 0000000..1e06c23
--- /dev/null
+++ b/test/resize_proceduretool.py
@@ -0,0 +1,38 @@
+import cv2
+from utils.Functions import resizeImages, addMinBoundingBox
+
+
+def main():
+
+ src_path = "../characters/src_dan_processed.png"
+ tag_path = "../characters/tag_dan_processed.png"
+
+ src_img = cv2.imread(src_path, 0)
+ tag_img = cv2.imread(tag_path, 0)
+
+ _, src_img = cv2.threshold(src_img, 127, 255, cv2.THRESH_BINARY)
+ _, tag_img = cv2.threshold(tag_img, 127, 255, cv2.THRESH_BINARY)
+
+ cv2.imshow("source", src_img)
+ cv2.imshow("target", tag_img)
+
+ src_img_box = addMinBoundingBox(src_img)
+ tag_img_box = addMinBoundingBox(tag_img)
+
+ cv2.imshow("source box", src_img_box)
+ cv2.imshow("target box", tag_img_box)
+
+ src_img, tag_img = resizeImages(src_img, tag_img)
+
+ print(src_img.shape)
+ print(tag_img.shape)
+
+ cv2.imshow("resize source", src_img)
+ cv2.imshow("resize target", tag_img)
+
+ cv2.waitKey(0)
+ cv2.destroyAllWindows()
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/test/resizetools.py b/test/resizetools.py
new file mode 100644
index 0000000..a3b070d
--- /dev/null
+++ b/test/resizetools.py
@@ -0,0 +1,34 @@
+import cv2
+from utils.Functions import resizeImages
+
+
+def main():
+
+ src_path = "../chars/src_dan_svg_simple.png"
+ tag_path = "../chars/tag_dan_svg_simple.png"
+
+ src_img = cv2.imread(src_path, 0)
+ tag_img = cv2.imread(tag_path, 0)
+
+ # rgt to grayscale
+
+ _, src_img = cv2.threshold(src_img, 127, 255, cv2.THRESH_BINARY)
+ _, tag_img = cv2.threshold(tag_img, 127, 255, cv2.THRESH_BINARY)
+
+ src_img, tag_img = resizeImages(src_img, tag_img)
+
+ print(src_img.shape)
+ print(tag_img.shape)
+
+ # cv2.imwrite('../chars/src_dan_svg_simple_resized.png', src_img)
+ # cv2.imwrite('../chars/tag_dan_svg_simple_resized.png', tag_img)
+
+ cv2.imshow("source", src_img)
+ cv2.imshow("target", tag_img)
+
+ cv2.waitKey(0)
+ cv2.destroyAllWindows()
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/test/rotatetool.py b/test/rotatetool.py
new file mode 100644
index 0000000..6030e70
--- /dev/null
+++ b/test/rotatetool.py
@@ -0,0 +1,30 @@
+import cv2
+import numpy as np
+from utils.Functions import rotateImage, resizeImages, getSingleMaxBoundingBoxOfImage, rotate_character
+
+
+def main():
+ src_path = "../chars/src_dan_svg_simple.png"
+ tag_path = "../chars/tag_dan_svg_simple.png"
+
+ src_img = cv2.imread(src_path, 0)
+ tag_img = cv2.imread(tag_path, 0)
+
+ # rgt to grayscale
+
+ _, src_img = cv2.threshold(src_img, 127, 255, cv2.THRESH_BINARY)
+ _, tag_img = cv2.threshold(tag_img, 127, 255, cv2.THRESH_BINARY)
+
+ src_img, tag_img = resizeImages(src_img, tag_img)
+
+ dst = rotate_character(src_img, 90)
+
+ cv2.imshow("src img", src_img)
+ cv2.imshow("rotate img", dst)
+ cv2.waitKey(0)
+ cv2.destroyAllWindows()
+
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/test/segmentationTool.py b/test/segmentationTool.py
new file mode 100644
index 0000000..486d14f
--- /dev/null
+++ b/test/segmentationTool.py
@@ -0,0 +1,197 @@
+import cv2
+import numpy as np
+from utils.Functions import getAllMiniBoundingBoxesOfImage
+import copy
+
+WIDTH_THRESHOLD = 15
+HEIGHT_THRESHOLD = 15
+SCALE_RATIO = 0.4
+
+
+def main():
+ src_path = '../calligraphys/hurulin1.jpg'
+
+ img = cv2.imread(src_path, 0)
+
+ img_rgb = cv2.imread(src_path)
+
+ img = cv2.resize(img, (0, 0), fx=SCALE_RATIO, fy=SCALE_RATIO)
+ if len(img_rgb.shape) == 2:
+ img_rgb = cv2.cvtColor(img_rgb, cv2.COLOR_GRAY2RGB)
+
+ img_rgb = cv2.resize(img_rgb, (0, 0), fx=SCALE_RATIO, fy=SCALE_RATIO)
+
+ img_rgb_noinside = copy.deepcopy(img_rgb)
+ img_rgb_nointersected = copy.deepcopy(img_rgb)
+
+ # image processing
+
+ # image binary
+ _, img_bit = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
+ print(img_bit.shape)
+
+ # inverting color
+ # img_bit = 255 - img_bit
+
+ # get all bounding boxes
+ boxes = getAllMiniBoundingBoxesOfImage(img_bit)
+ print("boxes len:%d" % len(boxes))
+
+ # filter the boxes, and remove the biggest and too small boxes
+ boxes_filtered = []
+ for box in boxes:
+ if box[2] < WIDTH_THRESHOLD * SCALE_RATIO or box[3] < HEIGHT_THRESHOLD * SCALE_RATIO:
+ continue
+
+ if box[2] >= img_bit.shape[0] or box[3] >= img_bit.shape[1]:
+ continue
+
+ boxes_filtered.append(box)
+ print("after filtered boxes len: %d" % len(boxes_filtered))
+
+ # removed contained rectangles
+ inside_id = []
+ for i in range(len(boxes_filtered)):
+ ri_x = boxes_filtered[i][0]
+ ri_y = boxes_filtered[i][1]
+ ri_w = boxes_filtered[i][2]
+ ri_h = boxes_filtered[i][3]
+
+ for j in range(len(boxes_filtered)):
+ if i == j or j in inside_id:
+ continue
+ rj_x = boxes_filtered[j][0]
+ rj_y = boxes_filtered[j][1]
+ rj_w = boxes_filtered[j][2]
+ rj_h = boxes_filtered[j][3]
+
+ # rect_j inside rect_i
+ if ri_x <= rj_x and ri_y <= rj_y and ri_x + ri_w >= rj_x + rj_w and ri_y + ri_h >= rj_y + rj_h:
+ if j not in inside_id:
+ inside_id.append(j)
+ elif rj_x <= ri_x and rj_y <= ri_y and rj_x + rj_w >= ri_x + ri_w and rj_y + rj_h >= ri_y + ri_h:
+ if i not in inside_id:
+ inside_id.append(i)
+ print("insie id len: %d " % len(inside_id))
+
+ boxes_noinside = []
+ for i in range(len(boxes_filtered)):
+ if i in inside_id:
+ continue
+ boxes_noinside.append(boxes_filtered[i])
+ print("no inside box len: %d" % len(boxes_noinside))
+
+ # add rectangles to original images
+
+ for box in boxes_noinside:
+ img_rgb_noinside = cv2.rectangle(img_rgb_noinside, (box[0], box[1]), (box[0] + box[2], box[1] + box[3]), (255, 0, 0), 1)
+
+ # merge the intersected rectangles
+ intersected_id_list = []
+ for i in range(len(boxes_noinside)):
+ intersected_item = []
+ rect_i = boxes_noinside[i]
+ intersected_item.append(i)
+ for j in range(len(boxes_noinside)):
+ if i == j:
+ continue
+ rect_j = boxes_noinside[j]
+ if isIntersectedOfTwoRectangles(rect_i, rect_j):
+ intersected_item.append(j)
+ intersected_id_list.append(intersected_item)
+ print("Before merge the id list")
+ print(intersected_id_list)
+
+ # merge the id list
+ intersected_id_list_merged = []
+ used_id = []
+ for i in range(len(intersected_id_list)):
+ if i in used_id:
+ continue
+ # no intersected with others
+ # if len(intersected_id_list[i]) == 1:
+ # intersected_id_list_merged.append(intersected_id_list[i])
+ # continue
+
+ new_id = intersected_id_list[i]
+ # intersected with others
+ for j in range(i+1, len(intersected_id_list)):
+ if len(set(intersected_id_list[i]).intersection(set(intersected_id_list[j]))) == 0:
+ continue
+ # intersected
+ new_id = list(set(new_id).union(set(intersected_id_list[j])))
+ used_id.append(j)
+ intersected_id_list_merged.append(new_id)
+
+ print("After merge")
+ print(intersected_id_list_merged)
+
+ boxes_nointersected = []
+
+ for item in intersected_id_list_merged:
+ rt_x, rt_y, rt_w, rt_h = combineRectangles(boxes_noinside, item)
+ boxes_nointersected.append([rt_x, rt_y, rt_w, rt_h])
+
+ print(len(boxes_nointersected))
+
+ for box in boxes_nointersected:
+ img_rgb_nointersected = cv2.rectangle(img_rgb_nointersected, (box[0], box[1]), (box[0] + box[2], box[1] + box[3]), (0, 0, 255), 1)
+
+ cv2.imshow("noinside", img_rgb_noinside)
+ cv2.imshow("nointersected", img_rgb_nointersected)
+
+ cv2.waitKey(0)
+ cv2.destroyAllWindows()
+
+
+def isIntersectedOfTwoRectangles(rect1, rect2):
+ """
+ Detect whether two rectangles are intersected with each other or not.
+ :param rect1: rectangle 1
+ :param rect2: rectangle 2
+ :return: intersected or not.
+ """
+ if rect1 is None or rect2 is None:
+ return False
+ if rect1[0] < rect2[0]+rect2[2] and rect1[0]+rect1[2] > rect2[0] and rect1[1] < rect2[1]+rect2[3] and rect1[1]+rect1[3] > rect2[1]:
+ return True
+ return False
+
+
+def combineRectangles(rectangles, rect_list):
+ """
+ Combining rectangles together.
+ :param rectangles: list of rectangles.
+ :param rect_list: list of index of rectangles in rectangles list.
+ :return: new combined rectangle.
+ """
+ if rectangles is None or len(rect_list) == 0:
+ return None
+
+ new_rect_x1 = rectangles[rect_list[0]][0]
+ new_rect_y1 = rectangles[rect_list[0]][1]
+ new_rect_x2 = rectangles[rect_list[0]][2] + rectangles[rect_list[0]][0]
+ new_rect_y2 = rectangles[rect_list[0]][3] + rectangles[rect_list[0]][1]
+
+ for id in range(len(rect_list)):
+ rect_x1 = rectangles[rect_list[id]][0]
+ rect_y1 = rectangles[rect_list[id]][1]
+ rect_x2 = rectangles[rect_list[id]][2] + rectangles[rect_list[id]][0]
+ rect_y2 = rectangles[rect_list[id]][3] + rectangles[rect_list[id]][1]
+
+ x1 = min(new_rect_x1, rect_x1)
+ y1 = min(new_rect_y1, rect_y1)
+
+ x2 = max(new_rect_x2, rect_x2)
+ y2 = max(new_rect_y2, rect_y2)
+
+ new_rect_x1 = x1
+ new_rect_y1 = y1
+ new_rect_x2 = x2
+ new_rect_y2 = y2
+
+ return new_rect_x1, new_rect_y1, new_rect_x2 - new_rect_x1, new_rect_y2 - new_rect_y1
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/test/shapesimilarity.py b/test/shapesimilarity.py
new file mode 100644
index 0000000..7733bf9
--- /dev/null
+++ b/test/shapesimilarity.py
@@ -0,0 +1,133 @@
+import cv2
+import numpy as np
+
+from utils.Functions import getSingleMaxBoundingBoxOfImage, resizeImages
+
+
+def main():
+ temp_path = "/Users/liupeng/Documents/PythonProjects/templates/templates/ben/char/ben.png"
+ targ_path = "/Users/liupeng/Documents/PythonProjects/templates/templates_comparison/ben/char/ben.png"
+
+ temp_img = cv2.imread(temp_path, 0)
+ targ_img = cv2.imread(targ_path, 0)
+
+ _, temp_img = cv2.threshold(temp_img, 127, 255, cv2.THRESH_BINARY)
+ _, targ_img = cv2.threshold(targ_img, 127, 255, cv2.THRESH_BINARY)
+
+ # resize two images of template and target.
+ temp_img, targ_img = resizeImages(temp_img, targ_img)
+
+ temp_img = np.array(temp_img, dtype=np.uint8)
+ targ_img = np.array(targ_img, dtype=np.uint8)
+
+ # bounding box of template and target images
+ temp_x, temp_y, temp_w, temp_h = getSingleMaxBoundingBoxOfImage(temp_img)
+ targ_x, targ_y, targ_w, targ_h = getSingleMaxBoundingBoxOfImage(targ_img)
+
+ temp_ct_x = temp_x + int(temp_w / 2.)
+ temp_ct_y = temp_y + int(temp_h / 2.)
+
+ targ_ct_x = targ_x + int(targ_w / 2.)
+ targ_ct_y = targ_y + int(targ_h / 2.)
+
+ # new square width
+ square_width = max(temp_w, temp_h, targ_w, targ_h)
+
+ # using new square to crop all effective area in template and target images
+ # template image
+ if temp_ct_x - int(square_width / 2.) <= 0:
+ temp_x = 0
+ else:
+ temp_x = temp_ct_x - int(square_width / 2.)
+
+ if temp_ct_y - int(square_width / 2,) <= 0:
+ temp_y = 0
+ else:
+ temp_y = temp_ct_y - int(square_width / 2,)
+
+ if temp_ct_x + int(square_width / 2.) >= temp_img.shape[1]:
+ temp_w = temp_img.shape[1] - temp_x
+ else:
+ temp_w = square_width
+
+ if temp_ct_y + int(square_width / 2.) >= temp_img.shape[0]:
+ temp_h = temp_img.shape[0] - temp_y
+ else:
+ temp_h = square_width
+ # target image
+ if targ_ct_x - int(square_width / 2.) <= 0:
+ targ_x = 0
+ else:
+ targ_x = targ_ct_x - int(square_width / 2.)
+
+ if targ_ct_x - int(square_width / 2, ) <= 0:
+ targ_y = 0
+ else:
+ targ_y = targ_ct_x - int(square_width / 2, )
+
+ if targ_ct_x + int(square_width / 2.) >= targ_img.shape[1]:
+ targ_w = targ_img.shape[1] - targ_x
+ else:
+ targ_w = square_width
+
+ if targ_ct_x + int(square_width / 2.) >= targ_img.shape[0]:
+ targ_h = targ_img.shape[0] - targ_y
+ else:
+ targ_h = square_width
+
+ # crop effective areas of the template and target images
+ temp_reg = temp_img[temp_y:temp_y+temp_h, temp_x:temp_x+temp_w]
+ targ_reg = targ_img[targ_y:targ_y+targ_h, targ_x:targ_x+targ_w]
+
+ shape_similarity = calculateShapeSimilarity(temp_reg, targ_reg)
+ simi = calculateShapeSimilarity(temp_reg, temp_reg)
+
+ print("Shape similarity: %f" % shape_similarity)
+ print("Same image similarity: %f" % simi)
+
+ cv2.imshow("temp", temp_reg)
+ cv2.imshow("targ", targ_reg)
+
+ cv2.waitKey(0)
+ cv2.destroyAllWindows()
+
+def calculateShapeSimilarity(square1, square2):
+ if square1 is None or square2 is None:
+ return 0.0
+ # same shape of two squares
+ if square1.shape != square2.shape:
+ print("Two image shape are different!")
+ return 0.0
+ #
+
+ h = square1.shape[0]; w = square1.shape[1]
+
+ square1_mat = np.zeros_like(square1)
+ square2_mat = np.zeros_like(square2)
+
+ for y in range(h):
+ for x in range(w):
+ if square1[y][x] == 0.0:
+ square1_mat[y][x] = 1.
+ if square2[y][x] == 0.0:
+ square2_mat[y][x] = 1.
+
+ #
+ numerator = np.sum(square1_mat * square2_mat)
+ abs_ = np.sum(np.abs(square1_mat - square2_mat))
+
+ similarity = numerator / (numerator + abs_)
+
+ return similarity
+
+
+if __name__ == '__main__':
+ main()
+ # a = [[1,1,1], [0,0,0], [1,0,1]]
+ # b = [[0,1,0],[1,1,0],[0,0,1]]
+ #
+ # a = np.array(a)
+ # b = np.array(b)
+ #
+ # si = calculateShapeSimilarity(a, b)
+ # print(si)
\ No newline at end of file
diff --git a/test/skeletonSegmentationTool.py b/test/skeletonSegmentationTool.py
new file mode 100644
index 0000000..102fb0d
--- /dev/null
+++ b/test/skeletonSegmentationTool.py
@@ -0,0 +1,96 @@
+import cv2
+import numpy as np
+from utils.Functions import resizeImages, getEndPointsOfSkeletonLine, getCrossPointsOfSkeletonLine, removeBranchOfSkeletonLine
+from skimage.morphology import skeletonize
+
+DIST_THRESHOLD = 10
+
+
+def main():
+ # src_path = "../strokes/src_strokes4.png"
+ src_path = "../chars/src_dan_svg_simple_resized.png"
+ tag_path = "../strokes/tag_strokes4.png"
+
+ src_img = cv2.imread(src_path, 0)
+ tag_img = cv2.imread(tag_path, 0)
+
+ ret, src_img = cv2.threshold(src_img, 127, 255, cv2.THRESH_BINARY)
+ ret, tag_img = cv2.threshold(tag_img, 127, 255, cv2.THRESH_BINARY)
+
+ # resize
+ src_img, tag_img = resizeImages(src_img, tag_img)
+
+ ret, src_img = cv2.threshold(src_img, 127, 255, cv2.THRESH_BINARY)
+ ret, tag_img = cv2.threshold(tag_img, 127, 255, cv2.THRESH_BINARY)
+
+ # obtain the skeleton of strokes
+ src_img_ = src_img != 255
+ tag_img_ = tag_img != 255
+
+ src_skel = skeletonize(src_img_)
+ tag_skel = skeletonize(tag_img_)
+
+ src_skel = (1 - src_skel) * 255
+ tag_skel = (1 - tag_skel) * 255
+
+ src_skel = np.array(src_skel, dtype=np.uint8)
+ tag_skel = np.array(tag_skel, dtype=np.uint8)
+
+ src_skel_rgb = cv2.cvtColor(src_skel, cv2.COLOR_GRAY2BGR)
+ tag_skel_rgb = cv2.cvtColor(tag_skel, cv2.COLOR_GRAY2BGR)
+
+ src_end_points = getEndPointsOfSkeletonLine(src_skel)
+ tag_end_points = getEndPointsOfSkeletonLine(tag_skel)
+
+ for (x, y) in src_end_points:
+ src_skel_rgb[y][x] = (0, 0, 255)
+ for (x, y) in tag_end_points:
+ tag_skel_rgb[y][x] = (0, 0, 255)
+
+ print("src end points len: %d" % len(src_end_points))
+ print("tag end points len: %d" % len(tag_end_points))
+
+ if len(src_end_points) > 2:
+ print("src skeleton line has branch")
+ if len(tag_end_points) > 2:
+ print("tag skeleton line has branch")
+ src_cross_points = getCrossPointsOfSkeletonLine(src_skel)
+ tag_cross_points = getCrossPointsOfSkeletonLine(tag_skel)
+
+ for (x, y) in src_cross_points:
+ src_skel_rgb[y][x] = (255, 0, 0)
+ for (x, y) in tag_cross_points:
+ tag_skel_rgb[y][x] = (255, 0, 0)
+
+ print("src cross len: %d" % len(src_cross_points))
+ print("tag cross len: %d" % len(tag_cross_points))
+
+ if len(src_cross_points) > 0:
+ # exist branches
+ src_skel = removeBranchOfSkeletonLine(src_skel, src_end_points, src_cross_points, )
+
+ if len(tag_cross_points) > 0:
+ # exist branches
+ tag_skel = removeBranchOfSkeletonLine(tag_skel, tag_end_points, tag_cross_points, )
+
+ # src_skel_no_branch = removeBranchOfSkeletonLine(src_skel, src_end_points, src_cross_points)
+ # tag_skel_no_btranch = removeBranchOfSkeletonLine(tag_skel, tag_end_points, tag_cross_points)
+
+ cv2.imshow("coverage img", src_img)
+ cv2.imshow("new coverage img", tag_img)
+
+ cv2.imshow("src rgb", src_skel_rgb)
+ cv2.imshow("tag rgb", tag_skel_rgb)
+
+ cv2.imshow("src skeleton img", src_skel)
+ cv2.imshow("tag skeleton img", tag_skel)
+
+ # cv2.imshow("src skeleton img no branch", src_skel_no_branch)
+ # cv2.imshow("tag skeleton img no branch", tag_skel_no_branch)
+
+ cv2.waitKey(0)
+ cv2.destroyAllWindows()
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/test/strokeComparisonTool.py b/test/strokeComparisonTool.py
new file mode 100644
index 0000000..191aa9f
--- /dev/null
+++ b/test/strokeComparisonTool.py
@@ -0,0 +1,124 @@
+import cv2
+import numpy as np
+from utils.Functions import getSingleMaxBoundingBoxOfImage, getContourOfImage, \
+ getCrossPointsOfSkeletonLine, getEndPointsOfSkeletonLine, removeBranchOfSkeletonLine
+from skimage.morphology import skeletonize
+
+
+def main():
+ src_path = "../strokes/src_strokes7.png"
+ tag_path = "../strokes/tag_strokes7.png"
+
+ src_img = cv2.imread(src_path, 0)
+ tag_img = cv2.imread(tag_path, 0)
+
+ # threshold
+ _, src_img = cv2.threshold(src_img, 127, 255, cv2.THRESH_BINARY)
+ _, tag_img = cv2.threshold(tag_img, 127, 255, cv2.THRESH_BINARY)
+
+ # get the minimum bounding boxs
+ src_box = getSingleMaxBoundingBoxOfImage(src_img)
+ tag_box = getSingleMaxBoundingBoxOfImage(tag_img)
+
+ # get the region of strokes
+ src_region = src_img[src_box[1]-5:src_box[1]+src_box[3]+5, src_box[0]-5: src_box[0]+src_box[2]+5]
+ tag_region = tag_img[tag_box[1]-5:tag_box[1]+tag_box[3]+5, tag_box[0]-5: tag_box[0]+tag_box[2]+5]
+
+
+ # get the contour of storkes based on the Canny algorithm
+
+ src_edge = getContourOfImage(src_region)
+ tag_edge = getContourOfImage(tag_region)
+
+ cv2.imshow("src edge", src_edge)
+ cv2.imshow("tag edge", tag_edge)
+
+ # get the skeletons of strokes based on the thinning algorithm
+ src_img_ = src_region != 255
+ tag_img_ = tag_region != 255
+
+ src_skel = skeletonize(src_img_)
+ tag_skel = skeletonize(tag_img_)
+
+ src_skel = (1 - src_skel) * 255
+ tag_skel = (1 - tag_skel) * 255
+
+ src_skel = np.array(src_skel, dtype=np.uint8)
+ tag_skel = np.array(tag_skel, dtype=np.uint8)
+
+ src_end_points = getEndPointsOfSkeletonLine(src_skel)
+ tag_end_points = getEndPointsOfSkeletonLine(tag_skel)
+
+ src_cross_points = getCrossPointsOfSkeletonLine(src_skel)
+ tag_cross_points = getCrossPointsOfSkeletonLine(tag_skel)
+
+ # if len(src_cross_points) > 0:
+ # # exist branches
+ # src_skel = removeBranchOfSkeletonLine(src_skel, src_end_points, src_cross_points)
+ #
+ # if len(tag_cross_points) > 0:
+ # # exist branches
+ # tag_skel = removeBranchOfSkeletonLine(tag_skel, tag_end_points, tag_cross_points)
+
+ cv2.imshow("src skel", src_skel)
+ cv2.imshow("tag skel", tag_skel)
+
+ # split the strokes based on the rule: begin, middle and the end parts.
+ src_regions = splitStrokes(src_region, type="LongHeng")
+ tag_regions = splitStrokes(tag_region, type="LongHeng")
+
+ print('len src regions: %d' % len(src_regions))
+ print('len tag regions: %d' % len(tag_regions))
+
+ cv2.imshow('src begin', src_regions[0])
+ cv2.imshow('src middle', src_regions[1])
+ cv2.imshow('src end', src_regions[2])
+
+ cv2.imshow("src", src_region )
+ cv2.imshow("tag", tag_region)
+
+ cv2.waitKey(0)
+ cv2.destroyAllWindows()
+
+
+'''
+ Split the strokes based on the type of strokes.
+ Type 1: Long_Heng, crop to three parts: begin(1/5), middle (3/5) and end (1/5)
+'''
+def splitStrokes(image, type):
+ if image is None:
+ return None
+ result = []
+ # Type 1: LongHeng, the 1/5 part is the begining, the last 1/5 part is the ending, and the rest part is the middle.
+ if type == 'LongHeng':
+ range = max(image.shape[0], image.shape[1])
+
+ # Begining part
+ begin_region = image[:, 0:int(range * 1/5)]
+
+ # Middle part
+ middle_region = image[:, int(range * 1/5): int(range * 4/5)]
+
+ # Ending part
+ end_region = image[:, int(range * 4/5): range]
+
+ result.append(begin_region)
+ result.append(middle_region)
+ result.append(end_region)
+
+ return result
+
+ # begin= 1/5 * length
+
+ # Type 2: MiddleHeng, the 1/4 pare is the begining, the last 1/4 part is the ending, and the rest part is the middle.
+ if type == 'MiddleHeng':
+ pass
+
+
+ # Type 3: ShortHeng, the 1/2 part is the begining, the rest 1/2 part is the ending, and no middle part
+ if type == 'ShortHeng':
+ pass
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test/strokeExtractTool.py b/test/strokeExtractTool.py
new file mode 100644
index 0000000..f7c1aed
--- /dev/null
+++ b/test/strokeExtractTool.py
@@ -0,0 +1,237 @@
+import cv2
+import numpy as np
+from collections import namedtuple
+from skimage.morphology import skeletonize
+from utils.Functions import getSkeletonOfImage, getEndPointsOfSkeletonLine, getCrossAreaPointsOfSkeletionLine, \
+ removeBranchOfSkeletonLine, getAllMiniBoundingBoxesOfImage, getCrossPointsOfSkeletonLine, splitConnectedComponents
+
+
+def main():
+ # Point
+ Point = namedtuple("Point", ["x", "y"])
+
+ # target image
+ target_path = "../templates/ben.png"
+ target_img = cv2.imread(target_path, 0)
+ _, target_img = cv2.threshold(target_img, 127, 255, cv2.THRESH_BINARY)
+ print(target_img.shape)
+
+ target_img_rgb = cv2.cvtColor(target_img, cv2.COLOR_GRAY2RGB)
+
+ # connected components with labeling algorithm
+ partial_parts = splitConnectedComponents(target_img)
+ print("number of parts: %d" % len(partial_parts))
+ # part 1
+ part_1 = partial_parts[0]
+
+ # skeleton line
+ part_1_ = part_1 != 255
+
+ part_skel = skeletonize(part_1_)
+ part_skel = (1 - part_skel) * 255
+
+ part_skel = np.array(part_skel, dtype=np.uint8)
+
+ for y in range(part_skel.shape[0]):
+ for x in range(part_skel.shape[1]):
+ if part_skel[y][x] == 0.0:
+ target_img_rgb[y][x] = (0, 255, 0)
+
+ # remove extra branch
+ end_points = getEndPointsOfSkeletonLine(part_skel)
+ cross_points = getCrossPointsOfSkeletonLine(part_skel)
+
+ part_skel = removeBranchOfSkeletonLine(part_skel, end_points, cross_points, )
+
+ part_1_rgb_no_branch = cv2.cvtColor(part_1, cv2.COLOR_GRAY2RGB)
+
+ for y in range(part_skel.shape[0]):
+ for x in range(part_skel.shape[1]):
+ if part_skel[y][x] == 0.0:
+ part_1_rgb_no_branch[y][x] = (0, 255, 0)
+
+ # new end points and cross points without extra branches
+ end_points = getEndPointsOfSkeletonLine(part_skel)
+ print("number of end points: %d" % len(end_points))
+ cross_points = getCrossPointsOfSkeletonLine(part_skel)
+ print("number of cross points: %d" % len(cross_points))
+
+ # add end points to image with blue color
+ for (x, y) in end_points:
+ part_1_rgb_no_branch[y][x] = (255, 0, 0)
+
+ # add cross points to image with red color
+ for (x, y) in cross_points:
+ part_1_rgb_no_branch[y][x] = (0, 0, 255)
+
+ # Contour of character
+ part_1_edges = cv2.Canny(part_1, 100, 200)
+ part_1_edges = 255 - part_1_edges
+
+ # Order the points on contours of character
+ print(part_1_edges.shape)
+ begin_point = None
+
+ # find the begin point
+ for y in range(part_1_edges.shape[0]):
+ for x in range(part_1_edges.shape[1]):
+ if part_1_edges[y][x] == 0.0:
+ # first black point
+ begin_point = (x, y)
+ break
+ if begin_point:
+ break
+ print("begin point: (%d, %d)" % (begin_point[0], begin_point[1]))
+
+ edge_order_lables = np.zeros_like(part_1_edges)
+ edge_order_lables[begin_point[1]][begin_point[0]] = 1.
+
+ curr_point = begin_point
+
+ # find the second point
+ if part_1_edges[y][x+1] == 0.0:
+ print("Second point is 4 position")
+ curr_point = (x+1, y)
+ elif part_1_edges[y+1][x+1] == 0.0:
+ print("Second point is 5 position")
+ curr_point = (x+1, y+1)
+ print(curr_point)
+ edge_order_lables[curr_point[1]][curr_point[0]] = 1.
+
+ edge_points = []
+ edge_points.append(begin_point)
+ edge_points.append(curr_point)
+
+ next_point = curr_point
+ edge_id = 0
+ while True:
+
+ x = curr_point[0]; y = curr_point[1]
+ # 2,4,6,8 position firstly and then 3,5,7,9 position
+ # point in 2 position
+ if part_1_edges[y-1][x] == 0.0 and edge_order_lables[y-1][x] == 0.0:
+ print("%d po" % 2)
+ next_point = (x, y-1)
+ edge_order_lables[y-1][x] = 1.
+
+ # point in 4 position
+ elif part_1_edges[y][x + 1] == 0.0 and edge_order_lables[y][x + 1] == 0.0:
+ print("%d po" % 4)
+ next_point = (x + 1, y)
+ edge_order_lables[y][x + 1] = 1.
+
+ # point in 6 position
+ elif part_1_edges[y + 1][x] == 0.0 and edge_order_lables[y + 1][x] == 0.0:
+ print("%d po" % 6)
+ next_point = (x, y + 1)
+ edge_order_lables[y + 1][x] = 1.
+
+ # point in 8 position
+ elif part_1_edges[y][x - 1] == 0.0 and edge_order_lables[y][x - 1] == 0.0:
+ print("%d po" % 8)
+ next_point = (x - 1, y)
+ edge_order_lables[y][x - 1] = 1.
+
+ # point in 3 position
+ elif part_1_edges[y-1][x+1] == 0.0 and edge_order_lables[y-1][x+1] == 0.0:
+ print("%d po" % 3)
+ next_point = (x+1, y-1)
+ edge_order_lables[y-1][x+1] = 1.
+
+ # point in 5 position
+ elif part_1_edges[y+1][x+1] == 0.0 and edge_order_lables[y+1][x+1] == 0.0:
+ print("%d po" % 5)
+ next_point = (x+1, y+1)
+ edge_order_lables[y+1][x+1] = 1.
+
+ # point in 7 position
+ elif part_1_edges[y+1][x-1] == 0.0 and edge_order_lables[y+1][x-1] == 0.0:
+ print("%d po" % 7)
+ next_point = (x-1, y+1)
+ edge_order_lables[y+1][x-1] = 1.
+
+ # point in 9 position
+ elif part_1_edges[y-1][x-1] == 0.0 and edge_order_lables[y-1][x-1] == 0.0:
+ print("%d po" % 9)
+ next_point = (x-1, y-1)
+ edge_order_lables[y-1][x-1] = 1.
+
+ if next_point == curr_point:
+ print(next_point)
+ print(curr_point)
+ edge_points.append(curr_point)
+ break
+ else:
+ edge_points.append(curr_point)
+ edge_id += 1
+ print("edge id: %d" % edge_id)
+ curr_point = next_point
+
+ print("edge points len: %d" % len(edge_points))
+ for pt in edge_points:
+ print(pt)
+ part_1_rgb_no_branch[pt[1]][pt[0]] = (0, 0, 255)
+
+
+
+
+
+
+ # # houngh lines
+ # rho_resolution = 1
+ # theta_resolution = np.pi / 180
+ # threshold = 155
+ # hough_lines = cv2.HoughLines(part_1_edges, rho_resolution, theta_resolution, threshold)
+ #
+ # print("number of hough lines: %d " % len(hough_lines))
+ #
+ # hough_lines_img = np.zeros_like(part_1_edges)
+ # draw_lines(hough_lines_img, hough_lines)
+ # original_image_with_hough_lines = weighted_img(hough_lines_img, part_1_edges)
+ #
+ # cv2.imshow("hough line image", hough_lines_img)
+ # cv2.imshow("original hough", original_image_with_hough_lines)
+
+
+ # # Corner detection
+ # corners = cv2.goodFeaturesToTrack(part_1_edges, 100, 0.01, 10)
+ # corners = np.int0(corners)
+ #
+ # for i in corners:
+ # x, y = i.ravel()
+ # cv2.circle(part_1_rgb_no_branch, (x, y), 3, 255, -1)
+
+ cv2.imshow("img", target_img)
+ # cv2.imshow("skel", part_skel)
+ # cv2.imshow("img_rgb", target_img_rgb)
+ cv2.imshow("img_rgb_no_branch", part_1_rgb_no_branch)
+ cv2.imshow("edges", part_1_edges)
+
+ cv2.imwrite("../templates/ben_skeleton.png", target_img_rgb)
+ cv2.imwrite("../templates/ben_skeleton_no_branch.png", part_1_rgb_no_branch)
+
+
+ cv2.waitKey(0)
+ cv2.destroyAllWindows()
+
+
+def draw_lines(img, houghLines, color=[0, 255, 0], thickness=2):
+ for line in houghLines:
+ 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), color, thickness)
+
+
+def weighted_img(img, initial_img, alpha=0.8, beta=1., gama=0.):
+ return cv2.addWeighted(initial_img, alpha, img, beta, gama)
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/test/strokeExtractionTool.py b/test/strokeExtractionTool.py
new file mode 100644
index 0000000..1a4154a
--- /dev/null
+++ b/test/strokeExtractionTool.py
@@ -0,0 +1,58 @@
+import numpy as np
+import cv2
+from utils.Functions import getConnectedComponents
+
+
+def main():
+ src_path = "../chars/src_dan_svg_simple_resized.png"
+ tag_path = "../chars/tag_fu_processed.png"
+
+ src_img = cv2.imread(src_path, 0)
+ tag_img = cv2.imread(tag_path, 0)
+
+ _, src_img = cv2.threshold(src_img, 127, 255, cv2.THRESH_BINARY)
+ _, tag_img = cv2.threshold(tag_img, 127, 255, cv2.THRESH_BINARY)
+
+ src_img = np.uint8(src_img)
+ tag_img = np.uint8(tag_img)
+
+ # grayscale to rgb
+ src_img_rgb = cv2.cvtColor(src_img, cv2.COLOR_GRAY2RGB)
+ tag_img_rgb = cv2.cvtColor(tag_img, cv2.COLOR_GRAY2RGB)
+
+ # connectivity
+ components = getConnectedComponents(tag_img)
+ print("components len: %d" % len(components))
+
+ # Check C-points exist or not. if no C-points, the single stroke. But C-points exist, several strokes
+
+ for idx, components in enumerate(components):
+ win_name = "src " + str(idx)
+ cv2.imshow(win_name, components)
+
+ cv2.waitKey(0)
+ cv2.destroyAllWindows()
+
+
+
+
+ # for lab in range(num_labels):
+ #
+ # x0 = stats[lab, cv2.CC_STAT_LEFT]
+ # y0 = stats[lab, cv2.CC_STAT_TOP]
+ # w = stats[lab, cv2.CC_STAT_WIDTH]
+ # h = stats[lab, cv2.CC_STAT_HEIGHT]
+ #
+ # if x0 == 0 and y0 == 0:
+ # continue
+ # src_img_rgb = cv2.rectangle(src_img_rgb, (x0, y0), (x0+w, y0+h), (0,255,0), 2)
+ #
+ # cv2.imshow("source", fig1)
+ #
+ # cv2.waitKey(0)
+ # cv2.destroyAllWindows()
+
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/test/strokeMatchWithTemplates.py b/test/strokeMatchWithTemplates.py
new file mode 100644
index 0000000..19a9b02
--- /dev/null
+++ b/test/strokeMatchWithTemplates.py
@@ -0,0 +1,99 @@
+import cv2
+import numpy
+
+
+from utils.Functions import getSingleMaxBoundingBoxOfImage, calculateCoverageRate
+
+
+def main():
+ pass
+
+
+if __name__ == '__main__':
+ # main()
+ src_path = "../templates/ben.png"
+ temp_base_path = "../templates/stroke_"
+
+ src_img = cv2.imread(src_path, 0)
+ # temp_img = cv2.imread(temp_path, 0)
+ # w, h = temp_img.shape
+
+ _, src_img = cv2.threshold(src_img, 127, 255, cv2.THRESH_BINARY)
+ # _, temp_img = cv2.threshold(temp_img, 127, 255, cv2.THRESH_BINARY)
+
+ cv2.imshow("src", src_img)
+ # cv2.imshow("temp", temp_img)
+
+ # bounding box
+ src_x, src_y, src_w, src_h = getSingleMaxBoundingBoxOfImage(src_img)
+ # temp_x, temp_y, temp_w, temp_h = calculateBoundingBox(temp_img)
+
+ src_window = src_img[src_y: src_y+src_h, src_x:src_x+src_w]
+ # temp_window = temp_img[temp_y:temp_y+temp_h, temp_x:temp_x+temp_w]
+
+ # temp_path = temp_base_path + "2_2.png"
+ # temp_img = cv2.imread(temp_path, 0)
+ # _, temp_img = cv2.threshold(temp_img, 127, 255, cv2.THRESH_BINARY)
+ #
+ # temp_x, temp_y, temp_w, temp_h = calculateBoundingBox(temp_img)
+ # temp_window = temp_img[temp_y:temp_y + temp_h, temp_x:temp_x + temp_w]
+ #
+ # src_part = src_window[185:185+temp_h, 306:306+temp_w]
+ #
+ # src_part1 = src_window[455:455+temp_h, 165:165+temp_w]
+ #
+ # cv2.imshow("src part", src_part)
+ # cv2.imshow("temp", temp_window)
+ # cv2.imshow("src 2", src_part1)
+ #
+ # cr = calculateCR(src_part, temp_window)
+ #
+ # cr1 = calculateCR(src_part1, temp_window)
+ #
+ # print(cr)
+ # print(cr1)
+
+
+
+
+ src_window_rgb = cv2.cvtColor(src_window, cv2.COLOR_GRAY2RGB)
+
+ for id in range(2, 3):
+ temp_path = temp_base_path + str(id) + ".png"
+ print(temp_path)
+ temp_img = cv2.imread(temp_path, 0)
+ _, temp_img = cv2.threshold(temp_img, 127, 255, cv2.THRESH_BINARY)
+
+ temp_img = cv2.resize(temp_img, src_img.shape)
+
+ temp_x, temp_y, temp_w, temp_h = getSingleMaxBoundingBoxOfImage(temp_img)
+ temp_window = temp_img[temp_y:temp_y + temp_h, temp_x:temp_x + temp_w]
+
+ max_cr = -1000000000.0
+ loc_x = 0; loc_y = 0
+ for y in range(src_h-temp_h):
+ for x in range(src_w-temp_w):
+ win_ = src_window[y:y+temp_h, x:x+temp_w]
+ cr = calculateCoverageRate(win_, temp_window)
+ print(cr)
+ if cr > max_cr:
+ print("cr: %f" % cr)
+ max_cr = cr
+ loc_x = x
+ loc_y = y
+ print("loc: (%d, %d)" % (loc_x, loc_y))
+ print("max cr: %f" % max_cr)
+
+ src_window_rgb = cv2.rectangle(src_window_rgb, (loc_x, loc_y), (loc_x+temp_w, loc_y+temp_h), (0,255,0), 1)
+
+ cv2.imshow("src window", src_window)
+ cv2.imshow("temp window", temp_window)
+ cv2.imshow("src window rgb", src_window_rgb)
+
+ cv2.waitKey(0)
+ cv2.destroyAllWindows()
+
+
+
+
+
diff --git a/test/strokesExtractWithTemplates.py b/test/strokesExtractWithTemplates.py
new file mode 100644
index 0000000..38982b1
--- /dev/null
+++ b/test/strokesExtractWithTemplates.py
@@ -0,0 +1,152 @@
+import os
+import cv2
+import numpy as np
+from skimage.measure import compare_ssim as ssim
+from utils.Functions import getSingleMaxBoundingBoxOfImage, calculateCoverageRate
+
+
+def main():
+
+ base_path = "../templates/templates/ben/strokes"
+
+ source_path = "../templates/templates/ben/char/ben.png"
+
+ source_img = cv2.imread(source_path, 0)
+ _, source_img = cv2.threshold(source_img, 127, 255, cv2.THRESH_BINARY)
+
+ stroke_windows = getStrokesListFromTemplates(base_path)
+ print("strokes len: %d" % len(stroke_windows))
+
+ # target image
+ target_path = "../templates/templates_comparison/ben/char/ben.png"
+ target_img = cv2.imread(target_path, 0)
+ _, target_img = cv2.threshold(target_img, 127, 255, cv2.THRESH_BINARY)
+ print(target_img.shape)
+ # resize target image
+ new_tg_h = max(source_img.shape[0], target_img.shape[0])
+ new_tg_w = max(source_img.shape[1], target_img.shape[1])
+
+ target_img = cv2.resize(target_img, (int(new_tg_h*0.2), int(new_tg_w*0.2)))
+
+
+ print(target_img.shape)
+ th = target_img.shape[0]; tw = target_img.shape[1]
+
+
+ # for stroke_window in stroke_windows:
+ # cv2.imshow("stroke", stroke_window)
+ for id, stroke_window in enumerate(stroke_windows):
+ cv2.imshow("stroke_"+str(id), stroke_window)
+
+ # slide the stroke window on the target image
+ stroke_list = []
+ for stroke_window in stroke_windows:
+ # resize stroke window * 0.2
+ stroke_window = cv2.resize(stroke_window, (int(stroke_window.shape[0] * 0.2), int(stroke_window.shape[1]*0.2)))
+
+ print("stroke window size: (%d, %d)" % stroke_window.shape)
+ sh = stroke_window.shape[0]; sw = stroke_window.shape[1]
+
+ min_diff = 1000000
+ max_ssim = 0.0
+ max_cr = -10000000.0
+ min_x = 0; min_y = 0
+
+ for y in range(th-sh):
+ for x in range(tw-sw):
+ target_window = target_img[y: y+sh, x: x+sw]
+ print(target_window.shape)
+ # diff = np.sum((np.abs(target_window - stroke_window))/255)
+ # diff_ssim = ssim(target_window, stroke_window)
+ diff_cr = calculateCoverageRate(stroke_window, target_window)
+ print("diff cr: %f" % diff_cr)
+
+ if diff_cr > max_cr:
+ min_x = x
+ min_y = y
+ max_cr = diff_cr
+
+ # if diff < min_diff:
+ # min_x = x
+ # min_y = y
+ # min_diff = diff
+
+ print("min: (%d, %d)" % (min_x, min_y))
+ print("max cr: %f" % max_cr)
+
+ stroke_list.append((min_x, min_y, sw, sh))
+ break
+
+
+
+ print(len(stroke_list))
+
+ target_img_rgb = cv2.cvtColor(target_img, cv2.COLOR_GRAY2RGB)
+
+ for stroke in stroke_list:
+ target_img_rgb[stroke[0]: stroke[0]+stroke[2], stroke[1]: stroke[1]+stroke[3]] = (255, 0, 0)
+
+
+ cv2.imshow("rgb", target_img_rgb)
+ # cv2.imshow("stroke", stroke_windows[0])
+
+
+ cv2.waitKey(0)
+ cv2.destroyAllWindows()
+
+
+def getStrokesListFromTemplates(templates_path):
+ """
+ Obtain strokes from templates files.
+ :param templates_path:
+ :return:
+ """
+ stroke_windows = []
+ if templates_path == "" or not os.path.exists(templates_path):
+ return
+
+ templates_files = [f for f in os.listdir(templates_path) if '.png' in f]
+ print(templates_files)
+
+ for temp_file in templates_files:
+ temp_path = templates_path + "/" + temp_file
+ temp_img = cv2.imread(temp_path, 0)
+ # scale image
+ # temp_img = cv2.resize(temp_img, (int(temp_img.shape[0]), int(temp_img.shape[1])))
+
+ _, temp_img = cv2.threshold(temp_img, 127, 255, cv2.THRESH_BINARY)
+ print(temp_img.shape)
+
+ # obtain the stroke window
+ x, y, w, h = getSingleMaxBoundingBoxOfImage(temp_img)
+
+ stroke_window = temp_img[y:y+h, x:x+w]
+ print(stroke_window.shape)
+ #
+ # cv2.imshow(temp_path, stroke_window)
+ stroke_windows.append(stroke_window)
+
+ return stroke_windows
+
+
+
+
+
+
+
+
+if __name__ == '__main__':
+ main()
+
+ # base_path = "../templates/templates/ben/strokes"
+ # stroke_windows = getStrokesListFromTemplates(base_path)
+ # print("strokes len: %d" % len(stroke_windows))
+ #
+ # for id, stroke_window in enumerate(stroke_windows):
+ # cv2.imshow("stroke_"+str(id), stroke_window)
+ #
+ # cv2.waitKey(0)
+ # cv2.destroyAllWindows()
+
+
+
diff --git a/test/strokesangletool.py b/test/strokesangletool.py
new file mode 100644
index 0000000..cb9c256
--- /dev/null
+++ b/test/strokesangletool.py
@@ -0,0 +1,50 @@
+import numpy as np
+import cv2
+from utils.Functions import addIntersectedFig
+
+
+def main():
+ src_path = "../strokes/src_strokes2.png"
+ tag_path = "../strokes/tag_strokes2.png"
+
+ src_img = cv2.imread(src_path, 0)
+ tag_img = cv2.imread(tag_path, 0)
+
+ _, src_img = cv2.threshold(src_img, 127, 255, cv2.THRESH_BINARY)
+ _, tag_img = cv2.threshold(tag_img, 127, 255, cv2.THRESH_BINARY)
+
+ src_img_rgb = cv2.cvtColor(src_img, cv2.COLOR_GRAY2RGB)
+ tag_img_rgb = cv2.cvtColor(tag_img, cv2.COLOR_GRAY2RGB)
+
+ _, src_contours, src_he = cv2.findContours(src_img, 1, 2)
+ _, tag_contours, tag_he = cv2.findContours(tag_img, 1, 2)
+
+ src_cnt = src_contours[0]
+ tag_cnt = tag_contours[0]
+
+ rows, cols = src_img.shape
+ [vx, vy, x, y] = cv2.fitLine(src_cnt, cv2.DIST_L2, 0, 0.01, 0.01)
+
+ lefty = int((-x * vy / vx) + y)
+ righty = int(((cols - x) * vy / vx) + y)
+ cv2.line(src_img_rgb, (cols - 1, righty), (0, lefty), (0, 255, 0), 2)
+ cv2.line(src_img_rgb, (cols - 1, lefty), (0, lefty), (0, 0, 255), 1)
+
+ rows, cols = tag_img.shape
+ [vx, vy, x, y] = cv2.fitLine(tag_cnt, cv2.DIST_L2, 0, 0.01, 0.01)
+ lefty = int((-x * vy / vx) + y)
+ righty = int(((cols - x) * vy / vx) + y)
+ cv2.line(tag_img_rgb, (cols - 1, righty), (0, lefty), (0, 255, 0), 2)
+ cv2.line(tag_img_rgb, (cols - 1, lefty), (0, lefty), (0, 0, 255), 1)
+
+ src_img_rgb_ = addIntersectedFig(src_img_rgb)
+ tag_img_rgb_ = addIntersectedFig(tag_img_rgb)
+
+ cv2.imshow("src img", src_img_rgb_)
+ cv2.imshow("tag img", tag_img_rgb_)
+ cv2.waitKey(0)
+ cv2.destroyAllWindows()
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/test/test.ipynb b/test/test.ipynb
new file mode 100644
index 0000000..e6b866b
--- /dev/null
+++ b/test/test.ipynb
@@ -0,0 +1,44 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "collapsed": true
+ },
+ "outputs": [],
+ "source": [
+ "import cv2\n",
+ "import numpy as np\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 2",
+ "language": "python",
+ "name": "python2"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 2
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython2",
+ "version": "2.7.6"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/test/test.py b/test/test.py
new file mode 100644
index 0000000..cbc07d7
--- /dev/null
+++ b/test/test.py
@@ -0,0 +1,164 @@
+# import cv2
+# import numpy as np
+# import math
+#
+# from utils.Functions import getConnectedComponents, getContourOfImage, getSkeletonOfImage, removeBreakPointsOfContour, \
+# removeBranchOfSkeletonLine, removeBranchOfSkeleton, getEndPointsOfSkeletonLine, \
+# getCrossPointsOfSkeletonLine, sortPointsOnContourOfImage, min_distance_point2pointlist, \
+# getNumberOfValidPixels, segmentContourBasedOnCornerPoints, merge_corner_lines_to_point, \
+# fitCurve, draw_cubic_bezier
+#
+# # 1133壬 2252支 0631叟
+# path = "0631叟.jpg"
+#
+# img = cv2.imread(path)
+# img_rgb = img.copy()
+#
+# # get contour of image
+# contour = getContourOfImage(img)
+# contour = getSkeletonOfImage(contour)
+# contour = np.array(contour, dtype=np.uint8)
+#
+# contour_rgb = cv2.cvtColor(contour, cv2.COLOR_GRAY2RGB)
+#
+# contour_points_sorted = sortPointsOnContourOfImage(contour)
+# print("contour points num:%d" % len(contour_points_sorted))
+#
+#
+# img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
+# _, img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
+# print(img.shape)
+# # get skeleton
+# skeleton = getSkeletonOfImage(img)
+# skeleton = removeBranchOfSkeleton(skeleton, distance_threshod=20)
+# skeleton = np.array(skeleton, dtype=np.uint8)
+# skeleton_rgb = cv2.cvtColor(skeleton, cv2.COLOR_GRAY2RGB)
+#
+# # end points
+# end_points = getEndPointsOfSkeletonLine(skeleton)
+# cross_points = getCrossPointsOfSkeletonLine(skeleton)
+#
+#
+# # corner area detect
+# img_corner = np.float32(img.copy())
+# dst = cv2.cornerHarris(img_corner, 2, 3, 0.04)
+# dst = cv2.dilate(dst, None)
+#
+# corners_area_points = []
+# for y in range(dst.shape[0]):
+# for x in range(dst.shape[1]):
+# if dst[y][x] > 0.1 * dst.max():
+# corners_area_points.append((x, y))
+#
+# img_corner_area = img_rgb.copy()
+#
+# for pt in corners_area_points:
+# img_corner_area[pt[1]][pt[0]] = (0, 0, 255)
+# # corner lines in contour rgb
+# corners_line_points = []
+# for pt in corners_area_points:
+# if pt in contour_points_sorted:
+# corners_line_points.append(pt)
+# for pt in corners_line_points:
+# contour_rgb[pt[1]][pt[0]] = (0, 255, 0)
+# # merge all points to single point in corner lines
+# corners_all_points = merge_corner_lines_to_point(corners_line_points, contour_points_sorted)
+#
+# for pt in corners_all_points:
+# contour_rgb[pt[1]][pt[0]] = (255, 0, 0)
+#
+# # obtain all valid corner points
+# corners_points = []
+# threshold_distance = 25
+# for pt in corners_all_points:
+# dist_cross = min_distance_point2pointlist(pt, cross_points)
+# dist_end = min_distance_point2pointlist(pt, end_points)
+# if dist_cross < threshold_distance and dist_end > threshold_distance / 3.:
+# corners_points.append(pt)
+#
+# for pt in corners_points:
+# contour_rgb[pt[1]][pt[0]] = (0, 0, 255)
+# print("corner points num: %d" % len(corners_points))
+#
+#
+# # cv2.imshow("img", img)
+# # cv2.imshow("contour", contour)
+# cv2.imshow("skeleton", skeleton)
+# cv2.imshow("img_corner_area", img_corner_area)
+# cv2.imshow("contour_rgb", contour_rgb)
+#
+# cv2.waitKey(0)
+# cv2.destroyAllWindows()
+
+# strokes = []
+#
+# component_line_relation = {5: [2, 3, 4, 5], 10: [8, 9, 10, 11], 0: [4, 5], 1: [2, 3], 6: [2, 3], 7: [4, 5], 8: [9, 11], 9: [8, 10], 11: [8, 10], 12: [9, 11]}
+# print(component_line_relation)
+#
+# clusters = []
+# for k1, v1 in component_line_relation.items():
+#
+# cluster = [k1]
+# value_sets = [set(v1)]
+#
+# for k2, v2 in component_line_relation.items():
+# is_related = True
+# for value in value_sets:
+# if not value.intersection(set(v2)):
+# is_related = False
+# break
+# if is_related and k2 not in cluster:
+# cluster.append(k2)
+# value_sets.append(set(v2))
+# cluster = sorted(cluster)
+# if cluster not in clusters:
+# clusters.append(cluster)
+#
+# print(clusters)
+import cv2
+import numpy as np
+
+from utils.Functions import getCornersPoints, getContourImage, getSkeletonOfImage, removeExtraBranchesOfSkeleton, \
+ getEndPointsOfSkeletonLine, getCrossPointsOfSkeletonLine, getValidCornersPoints
+
+path = "0034串.jpg"
+
+
+img = cv2.imread(path, 0)
+img_rgb = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
+
+contour_img = getContourImage(img)
+
+corner_img = np.float32(img)
+dst = cv2.cornerHarris(corner_img, 3, 3, 0.04)
+dst = cv2.dilate(dst, None)
+
+_, img = cv2.threshold(img, 200, 255, cv2.THRESH_BINARY)
+
+skeleton_img = getSkeletonOfImage(img)
+# skeleton_img = removeExtraBranchesOfSkeleton(skeleton_img)
+end_points = getEndPointsOfSkeletonLine(skeleton_img) # end points
+cross_points = getCrossPointsOfSkeletonLine(skeleton_img) # crois
+
+corners_area_points = []
+for y in range(dst.shape[0]):
+ for x in range(dst.shape[1]):
+ if dst[y][x] > 0.1 * dst.max():
+ corners_area_points.append((x, y))
+
+
+for pt in corners_area_points:
+ img_rgb[pt[1]][pt[0]] = (0, 0, 255)
+
+corner_points = getCornersPoints(img, contour_img, 3, 3, 0.04)
+corner_points = getValidCornersPoints(corner_points, cross_points, end_points, distance_threshold=45)
+print("corner point num: %d" % len(corner_points))
+
+for pt in corner_points:
+ img_rgb[pt[1]][pt[0]] = (0, 255, 0)
+
+cv2.imshow("rgb", img_rgb)
+cv2.imshow("skeleton", skeleton_img)
+
+cv2.waitKey(0)
+cv2.destroyAllWindows()
diff --git a/test/test_11.py b/test/test_11.py
new file mode 100644
index 0000000..a5c58f5
--- /dev/null
+++ b/test/test_11.py
@@ -0,0 +1,497 @@
+# import numpy as np
+# import cv2
+#
+# from utils.Functions import getContourOfImage, sortPointsOnContourOfImage, getSingleMaxBoundingBoxOfImage
+#
+# from math import acos
+# from math import sqrt
+# from math import pi
+#
+# import math
+#
+# def length(v):
+# return sqrt(v[0]**2+v[1]**2)
+# def dot_product(v,w):
+# return v[0]*w[0]+v[1]*w[1]
+# def determinant(v,w):
+# return v[0]*w[1]-v[1]*w[0]
+# def inner_angle(v,w):
+# cosx=dot_product(v,w)/(length(v)*length(w))
+# if cosx > 1.0:
+# cosx = 1.
+# elif cosx < -1.0:
+# cosx = -1.
+# # print(cosx)
+# rad=acos(cosx) # in radians
+# return rad*180/pi # returns degrees
+# def angle_clockwise(A, B):
+# inner=inner_angle(A,B)
+# det = determinant(A,B)
+# if det<0: #this is a property of the det. If the det < 0 then B is clockwise of A
+# return inner
+# else: # if the det > 0 then A is immediately clockwise of B
+# return 360-inner
+#
+# def get_angle(p0, p1, p2):
+# v0 = np.array(p1) - np.array(p0)
+# v1 = np.array(p2) - np.array(p1)
+#
+# angle = math.atan2(np.linalg.det([v0, v1]), np.dot(v0, v1))
+# return np.degrees(angle)
+#
+# def get_angle1(p0, p1, p2):
+# v0 = np.array(p1) - np.array(p0)
+# v1 = np.array(p2) - np.array(p1)
+#
+# angle = math.atan2(v0[0]*v1[1]-v0[1]*v1[0], v0[0]*v1[0]+v0[1]*v1[1])
+# return np.degrees(angle)
+
+
+# a = np.array([1, 2])
+# b = np.array([1, 1])
+# c = np.array([2, 1])
+#
+# print(get_angle(a, b, c))
+#
+# a = np.array([2, 2])
+# b = np.array([2, 1])
+# c = np.array([1, 1])
+#
+# print(get_angle(a, b, c))
+#
+# a1 = np.array([1, 3])
+# a2 = np.array([2, 3])
+# a3 = np.array([2, 2])
+# a4 = np.array([3, 2])
+# a5 = np.array([3, 1])
+# a6 = np.array([2, 1])
+# a7 = np.array([2, 0])
+# a8 = np.array([1, 0])
+# a9 = np.array([1, 1])
+# a10 = np.array([0, 1])
+# a11 = np.array([0, 2])
+# a12 = np.array([1, 2])
+#
+# print(get_angle(a1, a2, a3))
+# print(get_angle(a2, a3, a4))
+# print(get_angle(a3, a4, a5))
+# print(get_angle(a4, a5, a6))
+# print(get_angle(a5, a6, a7))
+# print(get_angle(a6, a7, a8))
+# print(get_angle(a7, a8, a9))
+# print(get_angle(a8, a9, a10))
+# print(get_angle(a9, a10, a11))
+# print(get_angle(a10, a11, a12))
+#
+# print("-----------------")
+#
+# print(get_angle1(a1, a2, a3))
+# print(get_angle1(a2, a3, a4))
+# print(get_angle1(a3, a4, a5))
+# print(get_angle1(a4, a5, a6))
+# print(get_angle1(a5, a6, a7))
+# print(get_angle1(a6, a7, a8))
+# print(get_angle1(a7, a8, a9))
+# print(get_angle1(a8, a9, a10))
+# print(get_angle1(a9, a10, a11))
+# print(get_angle1(a10, a11, a12))
+
+
+
+
+
+
+# print(angle_clockwise(A, B))
+
+# img_path = "0001ding.jpg"
+#
+# img = cv2.imread(img_path, 0)
+# _, img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
+#
+# contour = getContourOfImage(img)
+# contour_sorted = sortPointsOnContourOfImage(contour)
+#
+# obtuse_points = []
+#
+# for i in range(len(contour_sorted)):
+# start_index = i
+# middl_index = i + 10
+# end_index = i + 20
+#
+# if end_index >= len(contour_sorted)-1:
+# break
+# angle = get_angle(contour_sorted[start_index], contour_sorted[middl_index], contour_sorted[end_index])
+# # vect_a = np.array(contour_sorted[middl_index]) - np.array(contour_sorted[start_index])
+# # vect_b = np.array(contour_sorted[end_index]) - np.array(contour_sorted[middl_index])
+# #
+# # angle = angle_clockwise(vect_a, vect_b)
+# print(angle)
+#
+# if angle < 0.0:
+# obtuse_points.append(np.array(contour_sorted[middl_index]))
+# # break
+#
+# print("obtuse len: %d" % len(obtuse_points))
+#
+# contour_rgb = cv2.cvtColor(np.array(contour, dtype=np.uint8), cv2.COLOR_GRAY2RGB)
+#
+# print(contour_sorted[0])
+#
+# contour_rgb[contour_sorted[0][1]][contour_sorted[0][0]] = (0, 255, 0)
+#
+# for pt in obtuse_points:
+# contour_rgb[pt[1]][pt[0]] = (0, 0, 255)
+#
+# cv2.imshow("contour", contour)0554十.jpg
+# cv2.imshow("contour rgb", contour_rgb)
+#
+# cv2.waitKey(0)
+# cv2.destroyAllWindows()
+
+
+import numpy as np
+import cv2
+import math
+from skimage.morphology import skeletonize
+
+
+from utils.Functions import getContourOfImage, sortPointsOnContourOfImage, removeBreakPointsOfContour, \
+ getSkeletonOfImage, removeBranchOfSkeletonLine, getEndPointsOfSkeletonLine, \
+ getCrossPointsOfSkeletonLine, getNumberOfValidPixels
+
+# 0107亻 1133壬 0554十
+path = "0107亻.jpg"
+
+img = cv2.imread(path, 0)
+_, img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
+img_copy = img.copy()
+
+contour = getContourOfImage(img)
+contour = removeBreakPointsOfContour(contour)
+
+contour_sorted = sortPointsOnContourOfImage(contour)
+print(len(contour_sorted))
+print(contour_sorted[0])
+print(contour_sorted[-1])
+
+img = np.float32(img)
+dst = cv2.cornerHarris(img, 3, 3, 0.05)
+dst = cv2.dilate(dst, None)
+
+img_rgb = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
+contour_rgb = cv2.cvtColor(contour, cv2.COLOR_GRAY2RGB)
+contour_rgb1 = contour_rgb.copy()
+print(dst.shape)
+
+corner_points = []
+for y in range(dst.shape[0]):
+ for x in range(dst.shape[1]):
+ if dst[y][x] > 0.1*dst.max():
+ corner_points.append((x, y))
+print("corner points len: %d" % len(corner_points))
+
+for pt in corner_points:
+ if img[pt[1]][pt[0]] == 0:
+ img_rgb[pt[1]][pt[0]] = (0, 255, 0)
+ else:
+ img_rgb[pt[1]][pt[0]] = (0, 0, 255)
+
+corners_nomerged = []
+for y in range(contour.shape[0]):
+ for x in range(contour.shape[1]):
+ if contour[y][x] == 0 and (x, y) in corner_points:
+ contour_rgb[y][x] = (0, 0, 255)
+ corners_nomerged.append((x, y))
+print("corners sequence len: %d" % len(corners_nomerged))
+
+# merge points of corner points
+corners_segments = []
+corners_merged = []
+i = 0
+while True:
+ midd_index = -1
+ pt = contour_sorted[i]
+ if pt in corners_nomerged:
+ # red point
+ start = i
+ end = start
+ while True:
+ end += 1
+ if end >= len(contour_sorted):
+ break
+ # next point
+ next_pt = contour_sorted[end]
+ if next_pt in corners_nomerged:
+ # red point
+ continue
+ else:
+ # black point
+ break
+ end -= 1
+ midd_index = start + int((end-start)/2.0)
+ i = end
+
+ i += 1
+
+ if i >= len(contour_sorted):
+ break
+ if midd_index != -1:
+ corners_merged.append(contour_sorted[midd_index])
+
+for pt in corners_merged:
+ contour_rgb[pt[1]][pt[0]] = (255, 0, 0)
+
+print("cornet segments: %d" % len(corners_segments))
+print("corner merged len: %d" % len(corners_merged))
+
+# get skeleton of image
+skeleton = getSkeletonOfImage(img_copy)
+skeleton_rgb = cv2.cvtColor(skeleton, cv2.COLOR_GRAY2RGB)
+skeleton_points = []
+for y in range(skeleton.shape[0]):
+ for x in range(skeleton.shape[1]):
+ if skeleton[y][x] == 0.0:
+ skeleton_points.append((x, y))
+
+end_points = getEndPointsOfSkeletonLine(skeleton)
+cross_points = getCrossPointsOfSkeletonLine(skeleton)
+print("end points len:%d" % len(end_points))
+print("cross points len: %d" % len(cross_points))
+
+# skeleton = removeBranchOfSkeletonLine(skeleton, end_points, cross_points, DIST_THRESHOLD=20)
+end_points = getEndPointsOfSkeletonLine(skeleton)
+cross_points = getCrossPointsOfSkeletonLine(skeleton)
+
+for pt in cross_points:
+ skeleton_rgb[pt[1]][pt[0]] = (0, 0, 255)
+for pt in end_points:
+ skeleton_rgb[pt[1]][pt[0]] = (255, 0, 0)
+
+# remove points not be the C points
+if cross_points is None or len(cross_points) == 0:
+ print("single stroke, need not to extract!")
+else:
+ print("multi-strokes composed!")
+
+def min_distance_point2pointlist(point, points):
+ min_dist = 1000000000
+ if point is None or points is None:
+ return min_dist
+ for pt in points:
+ dist = math.sqrt((point[0]-pt[0])**2+(point[1]-pt[1])**2)
+ if dist < min_dist:
+ min_dist = dist
+ return min_dist
+
+
+singular_points = []
+DIST_THRESHOLD = 30
+for pt in corners_merged:
+ dist = min_distance_point2pointlist(pt, cross_points)
+ if dist < DIST_THRESHOLD:
+ # this point is the singlular point on contour
+ singular_points.append(pt)
+print("singular points num: %d" % len(singular_points))
+
+
+for pt in corners_merged:
+ contour_rgb[pt[1]][pt[0]] = (0, 0, 255)
+for pt in skeleton_points:
+ contour_rgb[pt[1]][pt[0]] = (255, 0, 0)
+for pt in cross_points:
+ contour_rgb[pt[1]][pt[0]] = (0, 0, 255)
+for pt in singular_points:
+ contour_rgb[pt[1]][pt[0]] = (0, 255, 0)
+
+# segment contour to sub-contours with singular points
+sub_contour_index = []
+for pt in singular_points:
+ index = contour_sorted.index(pt)
+ sub_contour_index.append(index)
+print("sub contour index num: %d" % len(sub_contour_index))
+print(sub_contour_index)
+
+sub_contours = []
+for i in range(len(sub_contour_index)):
+ if i == len(sub_contour_index)-1:
+ sub_contour = contour_sorted[sub_contour_index[i]:len(contour_sorted)] + contour_sorted[0: sub_contour_index[0]+1]
+ else:
+ sub_contour = contour_sorted[sub_contour_index[i]:sub_contour_index[i+1]+1]
+ sub_contours.append(sub_contour)
+print("sub contours num: %d" % len(sub_contours))
+
+for i in range(len(sub_contours)):
+ if i % 3 == 0:
+ color = (0, 0, 255)
+ elif i % 3 == 1:
+ color = (0, 255, 0)
+ elif i % 3 == 2:
+ color = (255, 0, 0)
+ for pt in sub_contours[i]:
+ contour_rgb1[pt[1]][pt[0]] = color
+
+# merge two sub-contours
+# 亻T type
+index = 0
+for sub_contour in sub_contours:
+
+ start_pt = sub_contour[-1]
+ end_pt = sub_contour[0]
+
+ stroke_img = np.ones_like(contour) * 255
+ stroke_img = np.array(stroke_img, dtype=np.uint8)
+
+ for pt in sub_contour:
+ stroke_img[pt[1]][pt[0]] = 0
+
+ cv2.line(stroke_img, start_pt, end_pt, 0, 1)
+ stroke_contour_sort = sortPointsOnContourOfImage(stroke_img)
+ stroke_contour_sort = np.array([stroke_contour_sort], "int32")
+ cv2.fillPoly(stroke_img, stroke_contour_sort, 0)
+
+ cv2.imshow("stroke image %d" % index, stroke_img)
+
+ index += 1
+
+
+
+
+
+
+
+
+
+
+cv2.imshow("contour", contour)
+cv2.imshow("img rgb", img_rgb)
+cv2.imshow("contour rbg", contour_rgb)
+cv2.imshow("sub contours", contour_rgb1)
+
+# cv2.imshow("skeleton", skeleton)
+cv2.imshow("skeleton rgb", skeleton_rgb)
+
+cv2.waitKey(0)
+cv2.destroyAllWindows()
+
+
+
+
+
+
+
+# cv2.imshow("dst", img_rgb)
+
+
+# contour = getContourOfImage(img)
+#
+# contour_sort_points = sortPointsOnContourOfImage(contour, isClockwise=False)
+# print("contour points len: %d" % len(contour_sort_points))
+#
+# contour_len = len(contour_sort_points)
+#
+# theta_angles = []
+# for i in range(1, contour_len):
+# pt1 = contour_sort_points[i-1]
+# pt2 = contour_sort_points[i]
+#
+# theta = math.atan2((pt2[1]-pt1[1]), -(pt2[0]-pt1[0]))
+# theta_angles.append(theta)
+#
+# # N point and the first point
+# theta = math.atan2(-(contour_sort_points[0][1]-contour_sort_points[contour_len-1][1]), (contour_sort_points[0][0]-contour_sort_points[contour_len-1][0]))
+# theta_angles.append(theta)
+#
+# print("theta angles: %d" % len(theta_angles))
+# print(theta_angles)
+#
+# single_points = []
+# for i in range(len(theta_angles)):
+# if i+10 >= len(theta_angles):
+# break
+# delt = abs(theta_angles[i] - theta_angles[i+10])
+# if delt > math.pi:
+# single_points.append(contour_sort_points[i])
+#
+#
+# contour_rgb = cv2.cvtColor(contour, cv2.COLOR_GRAY2RGB)
+#
+# for pt in single_points:
+# contour_rgb[pt[1]][pt[0]] = (0, 0, 255)
+
+# import numpy as np
+# import cv2
+# import math
+# from skimage.morphology import skeletonize
+#
+#
+# from utils.Functions import getContourOfImage, sortPointsOnContourOfImage, removeBreakPointsOfContour, \
+# getSkeletonOfImage, removeBranchOfSkeletonLine, getEndPointsOfSkeletonLine, \
+# getCrossPointsOfSkeletonLine, getNumberOfValidPixels
+#
+# # 0107亻 1133壬 0554十
+# path = "0107亻.jpg"
+#
+# img = cv2.imread(path, 0)
+# _, img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
+# img_copy = img.copy()
+#
+# skeleton = getSkeletonOfImage(img.copy())
+# skeleton_rgb = cv2.cvtColor(skeleton, cv2.COLOR_GRAY2RGB)
+#
+# cross_points = getCrossPointsOfSkeletonLine(skeleton)
+# end_points = getEndPointsOfSkeletonLine(skeleton)
+# skeleton = removeBranchOfSkeletonLine(skeleton, end_points, cross_points)
+#
+# cross_points = getCrossPointsOfSkeletonLine(skeleton)
+# end_points = getEndPointsOfSkeletonLine(skeleton)
+# print("Before merge num: %d" % len(cross_points))
+#
+# # merge the close points
+# cross_points_merged = []
+# cross_distance_threshold = 10
+# used_index = []
+# for i in range(len(cross_points)):
+# if i in used_index:
+# continue
+# pt1 = cross_points[i]
+# midd_pt = None
+# used_index.append(i)
+# for j in range(len(cross_points)):
+# if i == j or j in used_index:
+# continue
+# pt2 = cross_points[j]
+#
+# dist = math.sqrt((pt2[0]-pt1[0])**2 + (pt2[1]-pt1[1])**2)
+# if dist < cross_distance_threshold:
+# used_index.append(j)
+# offset = (pt1[0]-pt2[0], pt1[1]-pt2[1])
+# print(offset)
+# midd_pt = (pt2[0]+int(offset[0]/2.), pt2[1]+int(offset[1]/2.0))
+# if skeleton[midd_pt[1]][midd_pt[0]] == 0.0:
+# cross_points_merged.append(midd_pt)
+# else:
+# min_distance = 100000000
+# current_pt = None
+# for y in range(skeleton.shape[0]):
+# for x in range(skeleton.shape[1]):
+# if skeleton[y][x] == 0:
+# dist = math.sqrt((midd_pt[0]-x)**2 + (midd_pt[1]-y)**2)
+# if dist < min_distance:
+# min_distance = dist
+# current_pt = (x, y)
+# if current_pt:
+# cross_points_merged.append(current_pt)
+#
+# print("After merge cross points num: %d" % len(cross_points_merged))
+#
+# for pt in cross_points_merged:
+# print(pt)
+# skeleton_rgb[pt[1]][pt[0]] = (0, 255, 0)
+#
+#
+# cv2.imshow("img", img)
+# cv2.imshow("skeleton", skeleton)
+# cv2.imshow("skeleton", skeleton_rgb)
+#
+# cv2.waitKey(0)
+# cv2.destroyAllWindows()
\ No newline at end of file
diff --git a/test/test_bezier.py b/test/test_bezier.py
new file mode 100644
index 0000000..86b7d35
--- /dev/null
+++ b/test/test_bezier.py
@@ -0,0 +1,161 @@
+# import cv2
+# import numpy as np
+#
+#
+# def cubic_bezier_sum(t, w):
+# t2 = t * t
+# t3 = t2 * t
+# mt = 1 - t
+# mt2 = mt * mt
+# mt3 = mt2 * mt
+#
+# return w[0] * mt3 + 3 * w[1] * mt2 * t + 3 * w[2] * mt * t2 + w[3] * t3
+#
+#
+# def draw_cubic_bezier(p1, p2, p3, p4):
+# points = []
+# t = 0
+# while t < 1:
+# x = int(cubic_bezier_sum(t, (p1[0], p2[0], p3[0], p4[0])))
+# y = int(cubic_bezier_sum(t, (p1[1], p2[1], p3[1], p4[1])))
+#
+# points.append((x, y))
+#
+# t += 0.01
+# return points
+#
+# points = []
+# points.append((635, 489))
+# points.append((612, 489))
+# points.append((540, 486))
+# points.append((519, 476))
+#
+# bezier_points = draw_cubic_bezier(points[0], points[1], points[2], points[3])
+#
+# print(len(bezier_points))
+#
+# img_path = "../templates/stroke_dan.png"
+#
+# img = cv2.imread(img_path, 0)
+# _, img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
+# img_rgb = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
+#
+# for id in range(len(bezier_points)-1):
+# start_pt = bezier_points[id]
+# end_pt = bezier_points[id+1]
+# cv2.line(img_rgb, start_pt, end_pt, (0, 255, 0))
+#
+# cv2.imshow("rgb", img_rgb)
+#
+# cv2.waitKey(0)
+# cv2.destroyAllWindows()
+
+# import numpy as np
+# from scipy.misc import comb
+#
+# def bernstein_poly(i, n, t):
+# """
+# The Bernstein polynomial of n, i as a function of t
+# """
+#
+# return comb(n, i) * ( t**(n-i) ) * (1 - t)**i
+#
+#
+# def bezier_curve(points, nTimes=100):
+# """
+# Given a set of control points, return the
+# bezier curve defined by the control points.
+#
+# points should be a list of lists, or list of tuples
+# such as [ [1,1],
+# [2,3],
+# [4,5], ..[Xn, Yn] ]
+# nTimes is the number of time steps, defaults to 1000
+#
+# See http://processingjs.nihongoresources.com/bezierinfo/
+# """
+#
+# nPoints = len(points)
+# xPoints = np.array([p[0] for p in points])
+# yPoints = np.array([p[1] for p in points])
+#
+# t = np.linspace(0.0, 1.0, nTimes)
+#
+# polynomial_array = np.array([ bernstein_poly(i, nPoints-1, t) for i in range(0, nPoints) ])
+#
+# xvals = np.dot(xPoints, polynomial_array)
+# yvals = np.dot(yPoints, polynomial_array)
+#
+# return xvals, yvals
+#
+#
+# if __name__ == "__main__":
+# from matplotlib import pyplot as plt
+#
+# # nPoints = 4
+# # points = np.random.rand(nPoints,2)*200
+#
+# points = [(646, 737), (647, 737), (648, 738), (648, 739), (648, 740), (648, 741), (648, 742), (648, 743), (648, 744), (648, 745), (648, 746), (648, 747), (648, 748), (648, 749), (648, 750), (647, 750), (646, 750), (645, 750), (644, 751), (643, 752), (643, 753), (643, 754), (643, 755), (643, 756), (643, 757), (643, 758), (643, 759), (643, 760), (643, 761), (643, 762), (643, 763), (643, 764), (643, 765), (643, 766), (643, 767), (643, 768), (643, 769), (643, 770), (642, 770), (641, 770), (640, 771), (639, 772), (639, 773), (639, 774), (639, 775), (639, 776), (639, 777), (639, 778), (639, 779), (638, 779), (637, 779), (636, 780), (635, 781), (635, 782), (635, 783), (635, 784), (635, 785), (635, 786), (635, 787), (635, 788), (635, 789), (635, 790), (635, 791), (635, 792), (635, 793), (635, 794), (635, 795), (635, 796), (635, 797), (635, 798), (635, 799), (635, 800), (634, 800), (633, 800), (632, 801), (631, 802), (631, 803), (631, 804), (631, 805), (631, 806), (631, 807), (631, 808), (630, 808), (629, 808), (628, 808), (627, 808), (626, 808), (625, 808), (624, 809), (623, 810), (623, 811), (623, 812), (622, 812), (621, 812), (620, 812), (619, 812), (618, 812), (617, 812), (616, 812), (615, 813), (614, 814), (614, 815), (614, 816), (613, 816), (612, 816), (611, 817), (610, 818), (610, 819), (610, 820), (609, 820), (608, 820), (607, 821), (606, 822), (606, 823), (606, 824), (606, 825), (605, 825), (604, 825), (603, 826), (602, 827), (602, 828), (602, 829), (602, 830), (602, 831), (602, 832), (602, 833), (601, 833), (600, 833), (599, 834), (598, 835), (598, 836), (598, 837), (597, 837), (596, 837), (595, 837), (594, 838), (593, 839), (593, 840), (593, 841), (592, 841), (591, 841), (590, 842), (589, 843), (589, 844), (589, 845), (589, 846), (589, 847), (589, 848), (589, 849), (589, 850), (589, 851), (589, 852), (589, 853), (589, 854), (589, 855), (589, 856), (589, 857), (589, 858), (588, 858), (587, 858), (586, 859), (585, 860), (585, 861), (585, 862), (585, 863), (585, 864), (585, 865), (585, 866), (584, 866), (583, 866), (582, 866), (581, 866), (580, 866), (579, 866), (578, 867), (577, 868), (577, 869), (577, 870), (577, 871), (577, 872), (577, 873), (577, 874), (577, 875), (577, 876), (577, 877), (577, 878), (577, 879), (577, 880), (577, 881), (577, 882), (577, 883), (576, 883), (575, 883), (574, 884), (573, 885), (573, 886), (573, 887), (572, 887), (571, 887), (570, 887), (569, 888), (568, 889), (568, 890), (568, 891), (567, 891), (566, 891), (565, 892), (564, 893), (564, 894), (564, 895), (564, 896), (564, 897), (564, 898), (564, 899), (564, 900), (564, 901), (564, 902), (564, 903), (564, 904), (563, 904), (562, 904), (561, 905), (560, 906), (560, 907), (560, 908), (559, 908), (558, 908), (557, 909), (556, 910), (556, 911), (556, 912), (555, 912), (554, 912), (553, 913), (552, 914), (552, 915), (552, 916), (552, 917), (552, 918), (552, 919), (553, 920), (554, 920), (555, 920), (556, 921), (556, 922), (556, 923), (556, 924), (556, 925), (556, 926), (556, 927), (556, 928), (556, 929), (555, 929), (554, 929), (553, 930), (552, 931), (552, 932), (552, 933), (551, 933), (550, 933), (549, 934), (548, 935), (548, 936), (548, 937), (547, 937), (546, 937), (545, 937), (544, 937), (543, 937), (542, 937), (541, 937), (540, 938), (539, 939), (539, 940), (539, 941), (538, 941), (537, 941), (536, 942), (535, 943), (535, 944), (535, 945), (534, 945), (533, 945), (532, 946), (531, 947), (531, 948), (531, 949), (531, 950), (530, 950), (529, 950), (528, 951), (527, 952), (527, 953), (527, 954), (527, 955), (527, 956), (527, 957), (527, 958), (526, 958), (525, 958), (524, 959), (523, 960), (523, 961), (523, 962), (522, 962), (521, 962), (520, 962), (519, 962), (518, 962), (517, 962), (516, 962), (515, 963), (514, 964), (514, 965), (514, 966), (513, 966), (512, 966), (511, 967), (510, 968), (510, 969), (510, 970), (509, 970), (508, 970), (507, 971), (506, 972), (506, 973), (506, 974), (506, 975), (506, 976), (506, 977), (506, 978), (506, 979), (505, 979), (504, 979), (503, 979), (502, 979), (501, 979), (500, 979), (499, 980), (498, 981), (498, 982), (498, 983), (497, 983), (496, 983), (495, 983), (494, 984), (493, 985), (493, 986), (493, 987), (492, 987), (491, 987), (490, 987), (489, 987), (488, 987), (487, 987), (486, 988), (485, 989), (485, 990), (485, 991), (485, 992), (485, 993), (485, 994), (485, 995), (485, 996), (485, 997), (485, 998), (485, 999), (485, 1000), (485, 1001), (485, 1002), (485, 1003), (485, 1004), (484, 1004), (483, 1004), (482, 1004), (481, 1004), (480, 1004), (479, 1004), (478, 1005), (477, 1006), (477, 1007), (477, 1008), (476, 1008), (475, 1008), (474, 1008), (473, 1008), (472, 1008), (471, 1008), (470, 1008), (469, 1009), (468, 1010), (468, 1011), (468, 1012), (467, 1012), (466, 1012), (465, 1012), (464, 1012), (463, 1012), (462, 1012), (461, 1013), (460, 1014), (460, 1015), (460, 1016), (459, 1016), (458, 1016), (457, 1016), (456, 1016), (455, 1016), (454, 1016), (453, 1016), (452, 1016), (451, 1016), (450, 1016), (449, 1017), (448, 1018), (448, 1019), (448, 1020), (447, 1020), (446, 1020), (445, 1020), (444, 1021), (443, 1022), (443, 1023), (443, 1024), (443, 1025), (443, 1026), (443, 1027), (443, 1028), (443, 1029), (442, 1029), (441, 1029), (440, 1030), (439, 1031), (439, 1032), (439, 1033), (438, 1033), (437, 1033), (436, 1034), (435, 1035), (435, 1036), (435, 1037), (434, 1037), (433, 1037), (432, 1037), (431, 1037), (430, 1037), (429, 1037), (428, 1038), (427, 1039), (427, 1040), (427, 1041), (426, 1041), (425, 1041), (424, 1041), (423, 1041), (422, 1041), (421, 1041), (420, 1041), (419, 1042), (418, 1043), (418, 1044), (418, 1045), (417, 1045), (416, 1045), (415, 1046), (414, 1047), (414, 1048), (414, 1049), (414, 1050), (413, 1050), (412, 1050), (411, 1051), (410, 1052), (410, 1053), (410, 1054), (409, 1054), (408, 1054), (407, 1055), (406, 1056), (406, 1057), (406, 1058), (405, 1058), (404, 1058), (403, 1059), (402, 1060), (402, 1061), (402, 1062), (401, 1062), (400, 1062), (399, 1062), (398, 1062), (397, 1062), (396, 1062), (395, 1062), (394, 1063), (393, 1064), (393, 1065), (393, 1066), (392, 1066), (391, 1066), (390, 1067), (389, 1068), (389, 1069), (389, 1070), (388, 1070), (387, 1070), (386, 1071), (385, 1072), (385, 1073), (385, 1074), (385, 1075), (384, 1075), (383, 1075), (382, 1076), (381, 1077), (381, 1078), (381, 1079), (380, 1079), (379, 1079), (378, 1079), (377, 1079), (376, 1079), (375, 1079), (374, 1080), (373, 1081), (373, 1082), (373, 1083), (372, 1083), (371, 1083), (370, 1083), (369, 1083), (368, 1083), (367, 1083), (366, 1083), (365, 1083), (364, 1083), (363, 1083), (362, 1083), (361, 1084), (360, 1085), (360, 1086), (360, 1087), (359, 1087), (358, 1087), (357, 1087), (356, 1087), (355, 1087), (354, 1087), (353, 1088), (352, 1089), (352, 1090), (352, 1091), (351, 1091), (350, 1091), (349, 1091), (348, 1091), (347, 1091), (346, 1091), (345, 1091), (344, 1092), (343, 1093), (343, 1094), (343, 1095), (342, 1095), (341, 1095), (340, 1095), (339, 1095), (338, 1095), (337, 1095), (336, 1096), (335, 1097), (335, 1098), (335, 1099), (335, 1100), (334, 1100), (333, 1100), (332, 1100), (331, 1100), (330, 1100), (329, 1100), (328, 1100), (327, 1100), (326, 1100), (325, 1100), (324, 1101), (323, 1102), (323, 1103), (323, 1104), (322, 1104), (321, 1104), (320, 1104), (319, 1104), (318, 1104), (317, 1104), (316, 1104), (315, 1104), (314, 1104), (313, 1104), (312, 1104), (311, 1105), (310, 1106), (310, 1107), (310, 1108), (309, 1108), (308, 1108), (307, 1108), (306, 1108), (305, 1108), (304, 1108), (303, 1108), (302, 1108), (301, 1108), (300, 1108), (299, 1108), (298, 1108), (297, 1108), (296, 1108), (295, 1108), (294, 1109), (293, 1110), (293, 1111), (293, 1112), (292, 1112), (291, 1112), (290, 1112), (289, 1112), (288, 1112), (287, 1112), (286, 1112), (285, 1112), (284, 1112), (283, 1112), (282, 1112), (281, 1112), (280, 1112), (279, 1112), (278, 1112), (277, 1112), (276, 1112), (275, 1112), (274, 1112), (273, 1112), (272, 1112), (271, 1112), (270, 1112), (269, 1112), (268, 1112), (267, 1112), (266, 1112), (265, 1113), (264, 1114), (264, 1115), (264, 1116), (263, 1116), (262, 1116), (261, 1116), (260, 1116), (259, 1116), (258, 1116), (257, 1116), (256, 1116), (255, 1116), (254, 1116), (253, 1116), (252, 1116), (251, 1116), (250, 1116), (249, 1116), (248, 1116), (247, 1116), (246, 1116), (245, 1116), (244, 1116), (243, 1116), (242, 1116), (241, 1116), (240, 1116), (239, 1115), (239, 1114), (239, 1113), (238, 1112), (237, 1112), (236, 1112), (235, 1111), (235, 1110), (235, 1109), (234, 1108), (233, 1108), (232, 1108), (231, 1108), (230, 1108), (229, 1108), (228, 1108), (227, 1108), (226, 1108), (225, 1108), (224, 1109), (223, 1110), (223, 1111), (223, 1112), (222, 1112), (221, 1112), (220, 1112), (219, 1113), (218, 1114), (218, 1115), (218, 1116), (217, 1116), (216, 1116), (215, 1116), (214, 1116), (213, 1116), (212, 1116), (211, 1116), (210, 1116), (209, 1116), (208, 1116), (207, 1116), (206, 1116), (205, 1116), (204, 1116), (203, 1116), (202, 1115), (202, 1114), (202, 1113), (201, 1112), (200, 1112), (199, 1112), (198, 1111), (198, 1110), (198, 1109), (198, 1108), (198, 1107), (198, 1106), (198, 1105), (198, 1104), (198, 1103), (198, 1102), (198, 1101), (198, 1100), (198, 1099), (198, 1098), (198, 1097), (199, 1096), (200, 1095), (201, 1095), (202, 1095), (202, 1094), (202, 1093), (202, 1092), (202, 1091), (202, 1090), (202, 1089), (203, 1088), (204, 1087), (205, 1087), (206, 1087), (207, 1087), (208, 1087), (209, 1087), (210, 1087), (210, 1086), (210, 1085), (211, 1084), (212, 1083), (213, 1083), (214, 1083), (215, 1083), (216, 1083), (217, 1083), (218, 1083), (219, 1083), (220, 1083), (221, 1083), (222, 1083), (223, 1083), (224, 1083), (225, 1083), (226, 1083), (227, 1083), (228, 1083), (229, 1083), (230, 1083), (231, 1083), (231, 1082), (231, 1081), (232, 1080), (233, 1079), (234, 1079), (235, 1079), (235, 1078), (235, 1077), (236, 1076), (237, 1075), (238, 1075), (239, 1075), (240, 1075), (241, 1075), (242, 1075), (243, 1075), (244, 1075), (245, 1075), (246, 1075), (247, 1075), (248, 1075), (249, 1075), (250, 1075), (251, 1075), (252, 1075), (253, 1075), (254, 1075), (255, 1075), (256, 1075), (256, 1074), (256, 1073), (256, 1072), (257, 1071), (258, 1070), (259, 1070), (260, 1070), (261, 1070), (262, 1070), (263, 1070), (264, 1070), (265, 1070), (266, 1070), (267, 1070), (268, 1070), (268, 1069), (268, 1068), (268, 1067), (268, 1066), (268, 1065), (268, 1064), (268, 1063), (268, 1062), (268, 1061), (268, 1060), (268, 1059), (268, 1058), (268, 1057), (268, 1056), (268, 1055), (268, 1054), (268, 1053), (268, 1052), (269, 1051), (270, 1050), (271, 1050), (272, 1050), (273, 1050), (273, 1049), (273, 1048), (273, 1047), (273, 1046), (273, 1045), (273, 1044), (273, 1043), (274, 1042), (275, 1041), (276, 1041), (277, 1041), (278, 1041), (279, 1041), (280, 1041), (281, 1041), (281, 1040), (281, 1039), (282, 1038), (283, 1037), (284, 1037), (285, 1037), (285, 1036), (285, 1035), (285, 1034), (285, 1033), (285, 1032), (285, 1031), (286, 1030), (287, 1029), (288, 1029), (289, 1029), (289, 1028), (289, 1027), (290, 1026), (291, 1025), (292, 1025), (293, 1025), (294, 1025), (295, 1025), (296, 1025), (297, 1025), (298, 1025), (298, 1024), (298, 1023), (298, 1022), (299, 1021), (300, 1020), (301, 1020), (302, 1020), (302, 1019), (302, 1018), (303, 1017), (304, 1016), (305, 1016), (306, 1016), (306, 1015), (306, 1014), (306, 1013), (306, 1012), (306, 1011), (306, 1010), (307, 1009), (308, 1008), (309, 1008), (310, 1008), (311, 1008), (312, 1008), (313, 1008), (314, 1008), (314, 1007), (314, 1006), (315, 1005), (316, 1004), (317, 1004), (318, 1004), (319, 1004), (320, 1004), (321, 1004), (322, 1004), (323, 1004), (324, 1004), (325, 1004), (326, 1004), (327, 1004), (327, 1003), (327, 1002), (328, 1001), (329, 1000), (330, 1000), (331, 1000), (331, 999), (331, 998), (331, 997), (332, 996), (333, 995), (334, 995), (335, 995), (335, 994), (335, 993), (336, 992), (337, 991), (338, 991), (339, 991), (340, 991), (341, 991), (342, 991), (343, 991), (343, 990), (343, 989), (344, 988), (345, 987), (346, 987), (347, 987), (348, 987), (348, 986), (348, 985), (349, 984), (350, 983), (351, 983), (352, 983), (352, 982), (352, 981), (353, 980), (354, 979), (355, 979), (356, 979), (356, 978), (356, 977), (357, 976), (358, 975), (359, 975), (360, 975), (361, 975), (362, 975), (363, 975), (364, 975), (364, 974), (364, 973), (364, 972), (365, 971), (366, 970), (367, 970), (368, 970), (368, 969), (368, 968), (369, 967), (370, 966), (371, 966), (372, 966), (373, 966), (373, 965), (373, 964), (374, 963), (375, 962), (376, 962), (377, 962), (377, 961), (377, 960), (377, 959), (377, 958), (377, 957), (377, 956), (378, 955), (379, 954), (380, 954), (381, 954), (381, 953), (381, 952), (382, 951), (383, 950), (384, 950), (385, 950), (385, 949), (385, 948), (385, 947), (386, 946), (387, 945), (388, 945), (389, 945), (389, 944), (389, 943), (389, 942), (389, 941), (389, 940), (389, 939), (389, 938), (389, 937), (389, 936), (389, 935), (390, 934), (391, 933), (392, 933), (393, 933), (393, 932), (393, 931), (393, 930), (393, 929), (393, 928), (393, 927), (394, 926), (395, 925), (396, 925), (397, 925), (398, 925), (399, 925), (400, 925), (401, 925), (402, 925), (402, 924), (402, 923), (402, 922), (403, 921), (404, 920), (405, 920), (406, 920), (407, 920), (408, 920), (409, 920), (410, 920), (410, 919), (410, 918), (411, 917), (412, 916), (413, 916), (414, 916), (414, 915), (414, 914), (415, 913), (416, 912), (417, 912), (418, 912), (418, 911), (418, 910), (419, 909), (420, 908), (421, 908), (422, 908), (423, 908), (423, 907), (423, 906), (424, 905), (425, 904), (426, 904), (427, 904), (427, 903), (427, 902), (427, 901), (427, 900), (427, 899), (427, 898), (427, 897), (428, 896), (429, 895), (430, 895), (431, 895), (431, 894), (431, 893), (432, 892), (433, 891), (434, 891), (435, 891), (435, 890), (435, 889), (435, 888), (435, 887), (435, 886), (435, 885), (435, 884), (435, 883), (435, 882), (435, 881), (436, 880), (437, 879), (438, 879), (439, 879), (440, 879), (441, 879), (442, 879), (443, 879), (443, 878), (443, 877), (444, 876), (445, 875), (446, 875), (447, 875), (448, 875), (449, 875), (450, 875), (451, 875), (452, 875), (452, 874), (452, 873), (452, 872), (452, 871), (452, 870), (452, 869), (452, 868), (453, 867), (454, 866), (455, 866), (456, 866), (456, 865), (456, 864), (457, 863), (458, 862), (459, 862), (460, 862), (460, 861), (460, 860), (460, 859), (460, 858), (460, 857), (460, 856), (460, 855), (460, 854), (460, 853), (460, 852), (461, 851), (462, 850), (463, 850), (464, 850), (464, 849), (464, 848), (464, 847), (465, 846), (466, 845), (467, 845), (468, 845), (468, 844), (468, 843), (469, 842), (470, 841), (471, 841), (472, 841), (473, 841), (474, 841), (475, 841), (476, 841), (477, 841), (477, 840), (477, 839), (478, 838), (479, 837), (480, 837), (481, 837), (482, 837), (483, 837), (484, 837), (485, 837), (485, 836), (485, 835), (486, 834), (487, 833), (488, 833), (489, 833), (489, 832), (489, 831), (490, 830), (491, 829), (492, 829), (493, 829), (493, 828), (493, 827), (494, 826), (495, 825), (496, 825), (497, 825), (498, 825), (498, 824), (498, 823), (498, 822), (498, 821), (498, 820), (498, 819), (498, 818), (498, 817), (498, 816), (498, 815), (498, 814), (498, 813), (498, 812), (498, 811), (498, 810), (499, 809), (500, 808), (501, 808), (502, 808), (502, 807), (502, 806), (503, 805), (504, 804), (505, 804), (506, 804), (506, 803), (506, 802), (507, 801), (508, 800), (509, 800), (510, 800), (510, 799), (510, 798), (510, 797), (511, 796), (512, 795), (513, 795), (514, 795), (514, 794), (514, 793), (515, 792), (516, 791), (517, 791), (518, 791), (518, 790), (518, 789), (518, 788), (518, 787), (518, 786), (518, 785), (519, 784), (520, 783), (521, 783), (522, 783), (523, 783), (523, 782), (523, 781), (524, 780), (525, 779), (526, 779), (527, 779), (527, 778), (527, 777), (528, 776), (529, 775), (530, 775), (531, 775), (531, 774), (531, 773), (531, 772), (531, 771), (531, 770), (531, 769), (531, 768), (532, 767), (533, 766), (534, 766), (535, 766), (536, 766), (537, 766), (538, 766), (539, 766), (540, 766), (541, 766), (542, 766), (543, 766), (543, 765), (543, 764), (544, 763), (545, 762), (546, 762), (547, 762), (548, 762), (548, 761), (548, 760), (549, 759), (550, 758), (551, 758), (552, 758), (552, 757), (552, 756), (552, 755), (552, 754), (552, 753), (552, 752), (553, 751), (554, 750), (555, 750), (556, 750), (556, 749), (556, 748), (556, 747), (556, 746), (556, 745), (556, 744), (556, 743), (557, 742), (558, 741), (559, 741), (560, 741), (560, 740), (560, 739), (561, 738), (562, 737), (563, 737), (564, 737), (564, 736), (564, 735), (564, 734), (564, 733), (564, 732), (564, 731), (564, 730), (564, 729), (564, 728), (564, 727), (565, 726), (566, 725), (567, 725), (568, 725), (568, 724), (568, 723), (568, 722), (568, 721), (568, 720), (568, 719), (568, 718), (568, 717), (568, 716), (568, 715), (568, 714), (568, 713), (568, 712), (568, 711), (568, 710), (569, 709), (570, 708), (571, 708), (572, 708), (573, 708), (573, 707), (573, 706), (573, 705), (573, 704), (573, 703), (573, 702), (574, 701), (575, 700), (576, 700), (577, 700), (577, 699), (577, 698), (577, 697), (577, 696), (577, 695), (577, 694), (577, 693), (577, 692), (577, 691), (577, 690), (577, 689), (577, 688), (577, 687), (577, 686), (577, 685), (577, 684), (577, 683), (577, 682), (577, 681), (578, 680), (579, 679), (580, 679), (581, 679), (581, 678), (581, 677), (582, 676), (583, 675), (584, 675), (585, 675), (585, 674), (585, 673), (585, 672), (585, 671), (585, 670), (585, 669), (585, 668), (585, 667), (585, 666), (585, 665), (585, 664), (585, 663), (585, 662), (585, 661), (585, 660), (586, 659), (587, 658), (588, 658), (589, 658), (589, 657), (589, 656), (589, 655), (589, 654), (589, 653), (589, 652), (589, 651), (589, 650), (589, 649), (589, 648), (589, 647), (590, 646), (591, 645), (592, 645), (593, 645), (593, 644), (593, 643), (593, 642), (593, 641), (593, 640), (593, 639), (594, 638), (595, 637), (596, 637), (597, 637), (598, 637), (598, 636), (598, 635), (599, 634), (600, 633), (601, 633), (602, 633), (602, 632), (602, 631), (602, 630), (602, 629), (602, 628), (602, 627), (603, 626), (604, 625), (605, 625), (606, 625), (606, 624), (606, 623), (606, 622), (606, 621), (606, 620), (606, 619), (606, 618), (606, 617), (606, 616), (606, 615), (606, 614), (606, 613), (606, 612), (606, 611), (606, 610), (607, 609), (608, 608), (609, 608), (610, 608), (610, 607), (610, 606), (610, 605), (610, 604), (610, 603), (610, 602), (610, 601), (610, 600), (610, 599), (610, 598), (610, 597), (611, 596), (612, 595), (613, 595), (614, 595), (614, 594), (614, 593), (614, 592), (614, 591), (614, 590), (614, 589), (614, 588), (614, 587), (614, 586), (614, 585), (614, 584), (614, 583), (614, 582), (614, 581), (615, 580), (616, 579), (617, 579), (618, 579), (618, 578), (618, 577), (618, 576), (618, 575), (618, 574), (618, 573), (618, 572), (618, 571), (618, 570), (618, 569), (618, 568), (619, 567), (620, 566), (621, 566), (622, 566), (623, 566), (623, 565), (623, 564), (623, 563), (623, 562), (623, 561), (623, 560)]
+#
+# xpoints = [p[0] for p in points]
+# ypoints = [p[1] for p in points]
+#
+# xvals, yvals = bezier_curve(points, nTimes=1000)
+# plt.plot(xvals, yvals)
+# plt.plot(xpoints, ypoints, "ro")
+# # for nr in range(len(points)):
+# # plt.text(points[nr][0], points[nr][1], nr)
+#
+# plt.show()
+
+
+def point_inside_polygon(x, y, poly, include_edges=True):
+ '''
+ Test if point (x,y) is inside polygon poly.
+
+ poly is N-vertices polygon defined as
+ [(x1,y1),...,(xN,yN)] or [(x1,y1),...,(xN,yN),(x1,y1)]
+ (function works fine in both cases)
+
+ Geometrical idea: point is inside polygon if horisontal beam
+ to the right from point crosses polygon even number of times.
+ Works fine for non-convex polygons.
+ '''
+ n = len(poly)
+ inside = False
+
+ p1x, p1y = poly[0]
+ for i in range(1, n + 1):
+ p2x, p2y = poly[i % n]
+ if p1y == p2y:
+ if y == p1y:
+ if min(p1x, p2x) <= x <= max(p1x, p2x):
+ # point is on horisontal edge
+ inside = include_edges
+ break
+ elif x < min(p1x, p2x): # point is to the left from current edge
+ inside = not inside
+ else: # p1y!= p2y
+ if min(p1y, p2y) <= y <= max(p1y, p2y):
+ xinters = (y - p1y) * (p2x - p1x) / float(p2y - p1y) + p1x
+
+ if x == xinters: # point is right on the edge
+ inside = include_edges
+ break
+
+ if x < xinters: # point is to the left from current edge
+ inside = not inside
+
+ p1x, p1y = p2x, p2y
+
+ return inside
+
+
+polygon = [(0,0),(1,0),(1,2),(0,2),(0,0)]
+
+is_inside = point_inside_polygon(1, 1, polygon)
+
+print(is_inside)
+
+is_inside = point_inside_polygon(1, 1, polygon, include_edges=False)
+print(is_inside)
\ No newline at end of file
diff --git a/test/test_corner_detect.py b/test/test_corner_detect.py
new file mode 100644
index 0000000..0450083
--- /dev/null
+++ b/test/test_corner_detect.py
@@ -0,0 +1,141 @@
+# coding: utf-8
+import cv2
+import numpy as np
+import math
+
+from utils.Functions import getConnectedComponents, getContourOfImage, getSkeletonOfImage, removeBreakPointsOfContour, \
+ removeBranchOfSkeletonLine, removeExtraBranchesOfSkeleton, getEndPointsOfSkeletonLine, \
+ getCrossPointsOfSkeletonLine, sortPointsOnContourOfImage, min_distance_point2pointlist, \
+ getNumberOfValidPixels, segmentContourBasedOnCornerPoints, createBlankGrayscaleImage, \
+ getLinePoints, getBreakPointsFromContour, merge_corner_lines_to_point, getCropLines, \
+ getCornerPointsOfImage, getClusterOfCornerPoints, getCropLinesPoints, \
+ getConnectedComponentsOfGrayScale, getAllMiniBoundingBoxesOfImage
+
+
+path = "radical_gray.jpg"
+
+img = cv2.imread(path, 0)
+_, img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
+
+# img_bit = cv2.bitwise_not(img)
+
+
+ret, labels = cv2.connectedComponents(img, connectivity=4)
+components = []
+for r in range(1, ret):
+ img_ = np.ones(img.shape, dtype=np.uint8) * 255
+ for y in range(img.shape[0]):
+ for x in range(img.shape[1]):
+ if labels[y][x] == r:
+ img_[y][x] = 0.0
+ components.append(img_)
+
+print("components num : %d" % len(components))
+
+
+cv2.imshow("img", img)
+cv2.imshow("img_bit", img)
+
+for i in range(len(components)):
+ cv2.imshow("component_%d" % i, components[i])
+
+
+cv2.waitKey(0)
+cv2.destroyAllWindows()
+
+
+
+
+# contour_img = getContourOfImage(img_gray)
+# contour_img = np.array(contour_img, dtype=np.uint8)
+
+# rbg corner
+
+# img_corner = np.float32(img_rgb.copy())
+# dst = cv2.cornerHarris(img_corner, 2, 3, 0.04)
+# dst = cv2.dilate(dst, None)
+#
+# corners_area_points = []
+# for y in range(dst.shape[0]):
+# for x in range(dst.shape[1]):
+# if dst[y][x] > 0.1 * dst.max():
+# corners_area_points.append((x, y))
+#
+# for pt in corners_area_points:
+# img_rgb[y][x] = (0, 255, 0)
+
+# # grayscale corner
+# img_corner = np.float32(img_gray.copy())
+# dst = cv2.cornerHarris(img_corner, 3, 3, 0.04)
+# dst = cv2.dilate(dst, None)
+#
+# corners_area_points = []
+# for y in range(dst.shape[0]):
+# for x in range(dst.shape[1]):
+# if dst[y][x] > 0.1 * dst.max():
+# corners_area_points.append((x, y))
+# print(len(corners_area_points))
+# # for pt in corners_area_points:
+# # img_rgb[pt[1]][pt[0]] = (255, 0, 0)
+#
+#
+#
+#
+# # binary corner
+# img_corner = np.float32(img_bit.copy())
+# dst = cv2.cornerHarris(img_corner, 3, 3, 0.04)
+# dst = cv2.dilate(dst, None)
+#
+# corners_area_points = []
+# for y in range(dst.shape[0]):
+# for x in range(dst.shape[1]):
+# if dst[y][x] > 0.1 * dst.max():
+# corners_area_points.append((x, y))
+# print(len(corners_area_points))
+# for pt in corners_area_points:
+# img_rgb[pt[1]][pt[0]] = (0, 0, 255)
+#
+# corners_img = createBlankGrayscaleImage(img_gray)
+# for pt in corners_area_points:
+# corners_img[pt[1]][pt[0]] = 0.0
+#
+# rectangles = getAllMiniBoundingBoxesOfImage(corners_img)
+# print("rectangle num: %d" % len(rectangles))
+#
+# for rect in rectangles:
+# cv2.rectangle(img_rgb, (rect[0], rect[1]), (rect[0]+rect[2], rect[1]+rect[3]), (0, 255, 0), 1)
+#
+# corners_area_center_points = []
+# for rect in rectangles:
+# corners_area_center_points.append((rect[0]+int(rect[2]/2.), rect[1]+int(rect[3]/2.)))
+# img_rgb[rect[1]+int(rect[3]/2.)][rect[0]+int(rect[2]/2.)] = (0, 255, 0)
+# print("corner area num: %d" % len(corners_area_center_points))
+#
+# corners_points = []
+# for pt in corners_area_center_points:
+# if contour_img[pt[1]][pt[0]] == 0.0:
+# corners_points.append(pt)
+# else:
+# min_dist = 100000
+# min_x = min_y = 0
+# for y in range(contour_img.shape[0]):
+# for x in range(contour_img.shape[1]):
+# cpt = contour_img[y][x]
+# if cpt == 0.0:
+# dist = math.sqrt((x-pt[0])**2 + (y-pt[1])**2)
+# if dist < min_dist:
+# min_dist = dist
+# min_x = x
+# min_y = y
+# # points on contour
+# corners_points.append((min_x, min_y))
+# print("corner points num: %d" % len(corners_points))
+# for pt in corners_points:
+# img_rgb[pt[1]][pt[0]] = (255, 0, 0)
+#
+#
+#
+# cv2.imshow("rgb", img_rgb)
+#
+# cv2.waitKey(0)
+# cv2.destroyAllWindows()
\ No newline at end of file
diff --git a/test/test_gui.py b/test/test_gui.py
new file mode 100644
index 0000000..74f284a
--- /dev/null
+++ b/test/test_gui.py
@@ -0,0 +1,318 @@
+# # import sys
+# # from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout
+# # from PyQt5.QtGui import QPainter, QPixmap, QImage
+# # from PyQt5.QtCore import Qt, QPoint
+# #
+# #
+# # class Winform(QWidget):
+# # def __init__(self, parent=None):
+# # super(Winform, self).__init__(parent)
+# # layout = QVBoxLayout()
+# #
+# # self.setWindowTitle("双缓冲绘图例子")
+# # self.pix = QPixmap()
+# # self.lastPoint = QPoint()
+# # self.endPoint = QPoint()
+# #
+# # self.points = []
+# #
+# # # 辅助画布
+# # self.tempPix = QPixmap()
+# # # 标志是否正在绘图
+# # self.isDrawing = False
+# # self.initUi()
+# #
+# # def initUi(self):
+# # # 窗口大小设置为600*500
+# # self.resize(600, 500);
+# # # 画布大小为400*400,背景为白色
+# # self.pix = QPixmap(400, 400);
+# # image = QImage("ben.png")
+# # self.pix = QPixmap.fromImage(image)
+# #
+# # def paintEvent(self, event):
+# # painter = QPainter(self)
+# # x = self.lastPoint.x()
+# # y = self.lastPoint.y()
+# #
+# # x1 = self.endPoint.x()
+# # y1 = self.endPoint.y()
+# #
+# # w = self.endPoint.x() - x
+# # h = self.endPoint.y() - y
+# #
+# # # 如果正在绘图,就在辅助画布上绘制
+# # if self.isDrawing:
+# # # 将以前pix中的内容复制到tempPix中,保证以前的内容不消失
+# # self.tempPix = self.pix
+# # pp = QPainter(self.tempPix)
+# # # pp.drawRect(x, y, w, h)
+# # pp.drawLine(self.lastPoint, self.endPoint)
+# # painter.drawPixmap(0, 0, self.tempPix)
+# # else:
+# # pp = QPainter(self.pix)
+# # # pp.drawRect(x, y, w, h)
+# # pp.drawLine(self.lastPoint, self.endPoint)
+# # painter.drawPixmap(0, 0, self.pix)
+# #
+# # def mousePressEvent(self, event):
+# # # 鼠标左键按下
+# # if event.button() == Qt.LeftButton:
+# # self.lastPoint = event.pos()
+# # self.endPoint = self.lastPoint
+# # self.isDrawing = True
+# #
+# # def mouseReleaseEvent(self, event):
+# # # 鼠标左键释放
+# # if event.button() == Qt.LeftButton:
+# # self.endPoint = event.pos()
+# # # 进行重新绘制
+# # self.update()
+# # self.isDrawing = False
+# # print(self.endPoint)
+# #
+# #
+# #
+# # if __name__ == "__main__":
+# # app = QApplication(sys.argv)
+# # form = Winform()
+# # form.show()
+# # sys.exit(app.exec_())
+# #
+# # import numpy as np
+# # from scipy.misc import comb
+# #
+# # def bernstein_poly(i, n, t):
+# # """
+# # The Bernstein polynomial of n, i as a function of t
+# # """
+# #
+# # return comb(n, i) * ( t**(n-i) ) * (1 - t)**i
+# #
+# #
+# # def bezier_curve(points, nTimes=1000):
+# # """
+# # Given a set of control points, return the
+# # bezier curve defined by the control points.
+# #
+# # points should be a list of lists, or list of tuples
+# # such as [ [1,1],
+# # [2,3],
+# # [4,5], ..[Xn, Yn] ]
+# # nTimes is the number of time steps, defaults to 1000
+# #
+# # See http://processingjs.nihongoresources.com/bezierinfo/
+# # """
+# #
+# # nPoints = len(points)
+# # xPoints = np.array([p[0] for p in points])
+# # yPoints = np.array([p[1] for p in points])
+# #
+# # t = np.linspace(0.0, 1.0, nTimes)
+# #
+# # polynomial_array = np.array([ bernstein_poly(i, nPoints-1, t) for i in range(0, nPoints) ])
+# #
+# # xvals = np.dot(xPoints, polynomial_array)
+# # yvals = np.dot(yPoints, polynomial_array)
+# #
+# # return xvals, yvals
+# #
+# #
+# # if __name__ == "__main__":
+# # from matplotlib import pyplot as plt
+# #
+# # nPoints = 4
+# # points = np.random.rand(nPoints,2)*200
+# # xpoints = [p[0] for p in points]
+# # ypoints = [p[1] for p in points]
+# #
+# # xvals, yvals = bezier_curve(points, nTimes=1000)
+# # plt.plot(xvals, yvals)
+# # plt.plot(xpoints, ypoints, "ro")
+# # for nr in range(len(points)):
+# # plt.text(points[nr][0], points[nr][1], nr)
+# #
+# # plt.show()
+#
+# # import cv2
+# # from utils.Functions import splitConnectedComponents
+# #
+# # path = "quan1.png"
+# #
+# # img = cv2.imread(path, 0)
+# #
+# # _, img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
+# #
+# # radicals = splitConnectedComponents(img)
+# #
+# # print("number of radicals: %d" % len(radicals))
+# #
+# # for id, ra in enumerate(radicals):
+# # print(ra.shape)
+# # cv2.imshow("id_"+str(id), ra)
+# #
+# # img = cv2.resize(img, (int(img.shape[0]/2.), int(img.shape[1]/2.)))
+# # img_rgb = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
+# #
+# # cv2.imwrite("quan1.png", img_rgb)
+# #
+# # cv2.waitKey(0)
+# # cv2.destroyAllWindows()
+#
+#
+# import cv2
+# import numpy as np
+#
+# size = (250, 250)
+# back_gray = np.ones(size, dtype=np.uint8) * 255
+# back_rgb = cv2.cvtColor(back_gray, cv2.COLOR_GRAY2RGB)
+#
+# # add border lines with color red and 2 pixels width
+# back_rgb[0:2, ] = (0, 0, 255)
+# back_rgb[0:size[0], 0:2] = (0, 0, 255)
+# back_rgb[size[0]-2:size[0], ] = (0, 0, 255)
+# back_rgb[0:size[1], size[1]-2:size[1]] = (0, 0, 255)
+#
+# type = "net_20"
+#
+#
+# def image_segment_with_net(image, n):
+# """
+# Segment RGB image with NxN horizontal lines and vertical lines.
+# :param image: RGB image
+# :param n:
+# :return:
+# """
+# if image is None:
+# print("image segment should not be none!")
+# return
+# if n == 0:
+# return image
+#
+# # section range
+# w = image.shape[0];
+# h = image.shape[1]
+# sect_w = int(w / n)
+# sect_h = int(h / n)
+#
+# if w - sect_w * n > n / 2.0:
+# print("w: %d sect_w*n: %d" % (w, sect_w * n))
+# sect_w += 1
+# else:
+# print("w: %d sect_w*n: %d" % (w, sect_w * n))
+# if h - sect_h * n > n / 2.0:
+# print("h: %d sect_h*n: %d" %(h, sect_h * n))
+# sect_h += 1
+#
+# for i in range(1, n):
+# cv2.line(image, (0, i * sect_w), (h - 1, i * sect_w), (0, 0, 255), 1)
+# cv2.line(image, (i * sect_h, 0), (i * sect_h, w - 1), (0, 0, 255), 1)
+#
+# return image
+#
+#
+# if type == "mizi":
+# # MiZi grid
+# cv2.line(back_rgb, (0,0), (size[0]-1, size[1]-1), (0, 0, 255), 1)
+# cv2.line(back_rgb, (0, size[1]-1), (size[0]-1, 0), (0, 0, 255), 1)
+#
+# midd_w = int(size[0]/2.)
+# midd_h = int(size[1]/2.)
+#
+# cv2.line(back_rgb, (0, midd_h), (size[0]-1, midd_h), (0, 0, 255), 1)
+# cv2.line(back_rgb, (midd_w, 0), (midd_w, size[1]-1), (0, 0, 255), 1)
+#
+# elif type == "jingzi":
+# # JingZi grid
+# n = 3
+# back_rgb = image_segment_with_net(back_rgb, n)
+#
+# elif type == "net_20":
+# n = 8
+# back_rgb = image_segment_with_net(back_rgb, n)
+#
+#
+# cv2.imshow("gray", back_gray)
+# cv2.imshow("rgb", back_rgb)
+#
+# cv2.waitKey(0)
+# cv2.destroyAllWindows()
+
+# def isSubList(l1, l2):
+# if l1 == [] and l2 != []:
+# return True
+# if l1 != [] and l2 == []:
+# return False
+# if l1 == [] and l2 == []:
+# return True
+# for item in l1:
+# if item not in l2:
+# return False
+# return True
+#
+# a = [(272, 243), (272, 268)]
+# b = [(272, 243), (272, 268), (243, 270), (243, 247)]
+#
+# print(isSubList(a, b))
+
+# import cv2
+# import numpy as np
+# from skimage import feature
+#
+# from utils.Functions import createBlankGrayscaleImage, createBlankRGBImage
+#
+#
+# # 1133壬 2252支 0631叟
+# path = "0631叟.jpg"
+#
+# img = cv2.imread(path, 0)
+# _, img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
+# img_rbg = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
+#
+# edges = feature.canny(img)
+# edges1 = feature.canny(img, sigma=3)
+#
+# print(edges)
+#
+# contour_gray = createBlankGrayscaleImage(img)
+# contour_rgb = createBlankRGBImage(img)
+#
+# cv2.imshow("edge", edges)
+# cv2.imshow("edge1", edges1)
+#
+# cv2.waitKey(0)
+# cv2.destroyAllWindows()
+
+import numpy as np
+
+
+def medfilt (x, k):
+ """Apply a length-k median filter to a 1D array x.
+ Boundaries are extended by repeating endpoints.
+ """
+ assert k % 2 == 1, "Median filter length must be odd."
+ assert x.ndim == 1, "Input must be one-dimensional."
+ k2 = (k - 1) // 2
+ y = np.zeros ((len (x), k), dtype=x.dtype)
+ y[:,k2] = x
+ for i in range (k2):
+ j = k2 - i
+ y[j:,i] = x[:-j]
+ y[:j,i] = x[0]
+ y[:-j,-(i+1)] = x[j:]
+ y[-j:,-(i+1)] = x[-1]
+ return np.median (y, axis=1)
+
+
+def test ():
+ import pylab as p
+ x = np.linspace (0, 1, 101)
+ x[3::10] = 1.5
+ p.plot (x)
+ p.plot (medfilt (x,3))
+ p.plot (medfilt (x,5))
+ p.show ()
+
+
+if __name__ == '__main__':
+ test ()
\ No newline at end of file
diff --git a/test/test_radical_stroke_extract.py b/test/test_radical_stroke_extract.py
new file mode 100644
index 0000000..9492143
--- /dev/null
+++ b/test/test_radical_stroke_extract.py
@@ -0,0 +1,424 @@
+import cv2
+import numpy as np
+import math
+
+from utils.Functions import getConnectedComponents, getContourOfImage, getSkeletonOfImage, removeBreakPointsOfContour, \
+ removeBranchOfSkeletonLine, removeExtraBranchesOfSkeleton, getEndPointsOfSkeletonLine, \
+ getCrossPointsOfSkeletonLine, sortPointsOnContourOfImage, min_distance_point2pointlist, \
+ getNumberOfValidPixels, segmentContourBasedOnCornerPoints, createBlankGrayscaleImage, \
+ getLinePoints, getBreakPointsFromContour, merge_corner_lines_to_point, getCropLines, \
+ getCornerPointsOfImage, getClusterOfCornerPoints, getCropLinesPoints
+
+# 1133壬 2252支 0631叟 0633口
+path = "1133壬.jpg"
+img = cv2.imread(path)
+
+# contour
+contour = getContourOfImage(img)
+contour = getSkeletonOfImage(contour)
+contour = np.array(contour, dtype=np.uint8)
+
+img = cv2.imread(path, 0)
+_, img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
+img_rgb = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
+
+components = getConnectedComponents(img)
+print("radicals num: %d" % len(components))
+
+radicals = components[0]
+radicals = np.array(radicals, dtype=np.uint8)
+
+# sorted the contour po
+contour_sorted = sortPointsOnContourOfImage(contour)
+contour_rgb = cv2.cvtColor(contour, cv2.COLOR_GRAY2RGB)
+
+# # skeleton
+skeleton = getSkeletonOfImage(radicals)
+# # remove extra branches
+skeleton = removeExtraBranchesOfSkeleton(skeleton, distance_threshod=20)
+#
+end_points = getEndPointsOfSkeletonLine(skeleton)
+cross_points = getCrossPointsOfSkeletonLine(skeleton)
+skeleton_rgb = cv2.cvtColor(skeleton, cv2.COLOR_GRAY2RGB)
+
+# corner area detect
+corner_points = getCornerPointsOfImage(img.copy(), contour, cross_points, end_points)
+
+for pt in corner_points:
+ contour_rgb[pt[1]][pt[0]] = (0, 0, 255)
+print("corner points num: %d" % len(corner_points))
+
+# cluster the corner points
+dist_threshold = 30
+corner_points_cluster = getClusterOfCornerPoints(corner_points, cross_points)
+print("corner cluster num:%d" % len(corner_points_cluster))
+
+# detect corner points type: two point, four point (rectangle or diamond)
+crop_lines = getCropLines(corner_points_cluster)
+for line in crop_lines:
+ cv2.line(contour_rgb, line[0], line[1], (0, 255, 0), 1)
+
+# crop lines points
+crop_lines_points = getCropLinesPoints(img, crop_lines)
+print("crop lines num: %d" % len(crop_lines_points))
+
+img_separate = img.copy()
+for line in crop_lines:
+ cv2.line(img_separate, line[0], line[1], 255, 1)
+# for line in crop_lines_points:
+# if line is None:
+# continue
+# for pt in line:
+# img_separate[pt[1]][pt[0]] = 255
+
+strokes_components = getConnectedComponents(img_separate, connectivity=8)
+print("storkes components num: %d" % len(strokes_components))
+
+# find region based on the crop lines
+sub_contours = segmentContourBasedOnCornerPoints(contour_sorted, corner_points)
+print("sub contours num: %d" % len(sub_contours))
+
+for i in range(len(strokes_components)):
+ component = strokes_components[i]
+ cv2.imshow("component_%d" % i, component)
+# recompose strokes components
+
+contour_separate_region = cv2.cvtColor(contour_rgb, cv2.COLOR_RGB2GRAY)
+_, contour_separate_region = cv2.threshold(contour_separate_region, 240, 255, cv2.THRESH_BINARY)
+
+
+# seprarate contour
+# sub_contours_cluster = []
+# used_index = []
+# for i in range(len(sub_contours)):
+# if i in used_index:
+# continue
+# used_index.append(i)
+# c_start_pt = sub_contours[i][0]
+# c_end_pt = sub_contours[i][-1]
+#
+# sub_contour = [sub_contours[i]]
+#
+# for line in crop_lines:
+# index = 0
+# if c_start_pt in line and c_end_pt not in line:
+# index = line.index(c_start_pt)
+# elif c_start_pt not in line and c_end_pt in line:
+# index = line.index(c_end_pt)
+# print(index)
+#
+# if index == 0:
+# next_pt = line[1]
+# elif index == 1:
+# next_pt = line[0]
+# for j in range(len(sub_contours)):
+# if i == j or j in used_index:
+# continue
+# if next_pt in sub_contours[j] and line[index] not in sub_contours[j]:
+# sub_contour.append(sub_contours[j])
+# used_index.append(j)
+#
+# sub_contours_cluster.append(sub_contour)
+#
+# print(sub_contours_cluster)
+
+# for i in range(len(sub_contours_cluster)):
+# bk = createBlankGrayscaleImage(img)
+#
+# for sub in sub_contours_cluster[i]:
+# for pt in sub:
+# bk[pt[1]][pt[0]] = 0
+# cv2.imshow("bk%d" % i, bk)
+
+# for i in range(len(sub_contours)):
+# bk = createBlankGrayscaleImage(img)
+#
+# for pt in sub_contours[i]:
+# bk[pt[1]][pt[0]] = 0
+#
+# cv2.imshow("bk_%d" % i, bk)
+#
+# for i in range(len(crop_lines)):
+# bk = createBlankGrayscaleImage(img)
+#
+# cv2.line(bk, crop_lines[i][0], crop_lines[i][1], 0, 1)
+#
+# cv2.imshow("line_%d"%i, bk)
+
+sub_contours_cluster = []
+sub_contours_cluster.append([0, 2,4,6])
+sub_contours_cluster.append([1,5])
+sub_contours_cluster.append([3])
+sub_contours_cluster.append([7])
+
+crop_lines_cluster = []
+crop_lines_cluster.append([0,1, 2, 5])
+crop_lines_cluster.append([3, 4])
+crop_lines_cluster.append([5])
+crop_lines_cluster.append([0])
+
+for i in range(len(sub_contours_cluster)):
+ sub_cluster = sub_contours_cluster[i]
+ line_cluster = crop_lines_cluster[i]
+
+ bk = createBlankGrayscaleImage(img)
+
+ for index in sub_cluster:
+ sub = sub_contours[index]
+ for pt in sub:
+ bk[pt[1]][pt[0]] = 0
+
+ for index in line_cluster:
+ line = crop_lines[index]
+ cv2.line(bk, line[0], line[1], 0, 1)
+
+ # fill color
+ cont_sorted = sortPointsOnContourOfImage(bk)
+ cont_sorted = np.array([cont_sorted], "int32")
+ stroke_img = createBlankGrayscaleImage(bk)
+ stroke_img = cv2.fillPoly(stroke_img, cont_sorted, 0)
+
+
+ cv2.imshow("stroke_%d" % i, stroke_img)
+
+
+
+
+
+# cv2.imshow("radicals", radicals)
+cv2.imshow("contour", contour)
+cv2.imshow("skeleton", skeleton)
+# cv2.imshow("skeleton rgb", skeleton_rgb)
+cv2.imshow("img_corner_area", img_corner_area)
+# cv2.imshow("contour_rgb", contour_rgb)
+cv2.imshow("contour_separate_region", contour_separate_region)
+# cv2.imshow("contour_separate_region_bit", contour_separate_region_bit)
+cv2.imshow("img_separate", img_separate)
+cv2.waitKey(0)
+cv2.destroyAllWindows()
+
+
+
+# exit()
+#
+# def isInCluster(point_pair, cluster):
+# if point_pair is None or cluster is None:
+# return False
+# label = False
+# for cl in cluster:
+# if point_pair[0] in cl and point_pair[1] in cl:
+# label = True
+# break
+# return label
+#
+#
+# # segment contour to sub-contour
+# print("contor point num: %d" % len(contour_sorted))
+# sub_contours = segmentContourBasedOnCornerPoints(contour_sorted, corner_points)
+# print("sub contour num: %d" % len(sub_contours))
+#
+# # corner points correspondence
+#
+#
+# def isInOneSubContour(pt1, pt2, sub_contours):
+# if pt1 is None or pt2 is None or sub_contours is None:
+# return False
+# label = False
+# for sub in sub_contours:
+# if pt1 in sub and pt2 in sub:
+# label = True
+# break
+# return label
+#
+# # co-linear |y1-y2| <= 10 pixels and not in same sub-contour
+# co_linear_points = []
+# parallel_points = []
+# co_sub_contour = []
+# for i in range(len(corner_points)):
+# pt1 = corner_points[i]
+# for j in range(len(corner_points)):
+# if i == j:
+# continue
+# pt2 = corner_points[j]
+#
+# # co-linear should be in same cluster and can be in same sub-contour
+# if abs(pt1[0] - pt2[0]) <= 10 and isInCluster([pt1, pt2], corner_points_cluster):
+# # co-linear
+# if [pt1, pt2] not in co_linear_points and [pt2, pt1] not in co_linear_points:
+# co_linear_points.append([pt1, pt2])
+#
+# # parallel, should not be in same sub-contour, but should be in same cluster
+# if abs(pt1[1] - pt2[1]) <= 10 and not isInOneSubContour(pt1, pt2, sub_contours) and isInCluster([pt1, pt2], corner_points_cluster):
+# # parallel
+# if [pt1, pt2] not in parallel_points and [pt2, pt1] not in parallel_points:
+# if [pt1, pt2] not in co_linear_points and [pt2, pt1] not in co_linear_points:
+# parallel_points.append([pt1, pt2])
+#
+# # co sub-contour, and do not repeat in previous lists.
+# if isInOneSubContour(pt1, pt2, sub_contours):
+# # co subcontour
+# if [pt1, pt2] not in co_sub_contour and [pt2, pt1] not in co_sub_contour:
+# if [pt1, pt2] not in co_linear_points and [pt2, pt1] not in co_linear_points:
+# if [pt1, pt2] not in parallel_points and [pt2, pt1] not in parallel_points and isInCluster([pt1, pt2], corner_points_cluster):
+# co_sub_contour.append([pt1, pt2])
+#
+# print(co_linear_points)
+# print(parallel_points)
+# print(co_sub_contour)
+#
+# co_linear_points_cluster = []
+# parallel_points_cluster = []
+# co_sub_contour_cluster = []
+#
+# def isSubList(l1, l2):
+# if l1 == [] and l2 != []:
+# return True
+# if l1 != [] and l2 == []:
+# return False
+# if l1 == [] and l2 == []:
+# return True
+# for item in l1:
+# if item not in l2:
+# return False
+# return True
+#
+# # cluster co_linear points pair based on the cluster points
+# used_index = []
+# for i in range(len(co_linear_points)):
+# if i in used_index:
+# continue
+# used_index.append(i)
+# pair_cluster = [co_linear_points[i]]
+# cluster = None
+# for cl in corner_points_cluster:
+# if isSubList(co_linear_points[i], cl):
+# cluster = cl.copy()
+#
+# # j
+# if cluster is None:
+# print("cluster should not be None!")
+# for j in range(len(co_linear_points)):
+# if j == i or j in used_index:
+# continue
+# if isSubList(co_linear_points[j], cluster):
+# pair_cluster.append(co_linear_points[j])
+# used_index.append(j)
+# co_linear_points_cluster.append(pair_cluster)
+#
+# print(co_linear_points_cluster)
+#
+# # cluster the parallel points pair
+# used_index = []
+# for i in range(len(parallel_points)):
+# if i in used_index:
+# continue
+# used_index.append(i)
+# pair_cluster = [parallel_points[i]]
+# cluster = None
+# for cl in corner_points_cluster:
+# if isSubList(parallel_points[i], cl):
+# cluster = cl.copy()
+#
+# # j
+# if cluster is None:
+# print("cluster should not be None!")
+# for j in range(len(parallel_points)):
+# if j == i or j in used_index:
+# continue
+# if isSubList(parallel_points[j], cluster):
+# pair_cluster.append(parallel_points[j])
+# used_index.append(j)
+# parallel_points_cluster.append(pair_cluster)
+#
+# print(parallel_points_cluster)
+#
+# # cluster the co-subcontour points pair
+# used_index = []
+# for i in range(len(co_sub_contour)):
+# if i in used_index:
+# continue
+# used_index.append(i)
+# pair_cluster = [co_sub_contour[i]]
+# cluster = None
+# for cl in corner_points_cluster:
+# if isSubList(co_sub_contour[i], cl):
+# cluster = cl.copy()
+#
+# # j
+# if cluster is None:
+# print("cluster should not be None!")
+# for j in range(len(co_sub_contour)):
+# if j == i or j in used_index:
+# continue
+# if isSubList(co_sub_contour[j], cluster):
+# pair_cluster.append(co_sub_contour[j])
+# used_index.append(j)
+# co_sub_contour_cluster.append(pair_cluster)
+#
+# print(co_sub_contour_cluster)
+#
+# def findCoLinearSubContours(point_pair, sub_contours):
+# """
+# Find two co-linear sub-contours based on the point pair.
+# :param point_pair:
+# :param sub_contours:
+# :return:
+# """
+# if point_pair is None or sub_contours is None:
+# return
+# pt1 = point_pair[0]; pt2 = point_pair[1]
+# sub1 = None; sub2 = None
+# for sub in sub_contours:
+# if pt1 in sub and pt2 not in sub:
+# sub1 = sub.copy()
+# if pt2 in sub and pt1 not in sub:
+# sub2 = sub.copy()
+# return [sub1, sub2]
+#
+# #
+# print("sub-contours num : %d" % len(sub_contours))
+#
+# # def findCoSubContour(pair, sub_contours):
+# # if pair is None or sub_contours is None:
+# # return
+# # for sub in sub_contours:
+# # if pair[0] in sub
+# #
+# # for pair in co_sub_contour:
+# # pass
+#
+#
+
+
+
+#
+#
+#
+# # crop character
+# print("contor point num: %d" % len(contour_sorted))
+# sub_contours = segmentContourBasedOnCornerPoints(contour_sorted, corner_points)
+# print("sub contours num: %d" % len(sub_contours))
+#
+# # separate single region to several region
+# contour_separate_region = cv2.cvtColor(contour_rgb, cv2.COLOR_RGB2GRAY)
+# _, contour_separate_region = cv2.threshold(contour_separate_region, 240, 255, cv2.THRESH_BINARY)
+#
+# contour_separate_region_bit = np.array(255-contour_separate_region, dtype=np.uint8)
+
+# for i in range(len(sub_contours)):
+# sub = sub_contours[i]
+# bk_img = createBlankGrayscaleImage(img)
+# for pt in sub:
+# bk_img[pt[1]][pt[0]] = 0
+#
+# cv2.imshow("sub contour_%d" % i, bk_img)
+
+
+# for y in range(radicals.shape[0]):
+# for x in range(radicals.shape[1]):
+# if radicals[y][x] == 255:
+# contour_separate_region_bit[y][x] = 255
+#
+# stroke_components = getConnectedComponents(contour_separate_region_bit)
+# print(len(stroke_components))
+
diff --git a/test/test_skeleton.py b/test/test_skeleton.py
new file mode 100644
index 0000000..d418183
--- /dev/null
+++ b/test/test_skeleton.py
@@ -0,0 +1,36 @@
+# coding: utf-8
+import cv2
+import numpy as np
+
+from utils.Functions import getSkeletonOfImage, getEndPointsOfSkeletonLine, getCrossPointsOfSkeletonLine, \
+ removeExtraBranchesOfSkeleton
+
+# 1133壬 2252支 0631叟 0633口 0242俄 0195佛 0860善 0059乘 0098亩
+path = "2252支.jpg"
+
+img = cv2.imread(path, 0)
+_, img_bit = cv2.threshold(img, 200, 255, cv2.THRESH_BINARY)
+
+skeleton = getSkeletonOfImage(img_bit)
+skeleton = removeExtraBranchesOfSkeleton(skeleton)
+
+skeleton_rgb = cv2.cvtColor(skeleton, cv2.COLOR_GRAY2RGB)
+
+end_points = getEndPointsOfSkeletonLine(skeleton)
+
+cross_points = getCrossPointsOfSkeletonLine(skeleton)
+
+# end points
+for pt in end_points:
+ skeleton_rgb[pt[1]][pt[0]] = (0, 0, 255)
+
+for pt in cross_points:
+ skeleton_rgb[pt[1]][pt[0]] = (0, 255, 0)
+
+
+cv2.imshow("skeleton rgb", skeleton_rgb)
+
+cv2.waitKey(0)
+cv2.destroyAllWindows()
+
+
diff --git a/test/test_stroke_extract.py b/test/test_stroke_extract.py
new file mode 100644
index 0000000..2eb5b80
--- /dev/null
+++ b/test/test_stroke_extract.py
@@ -0,0 +1,325 @@
+# coding:utf-8
+import cv2
+import numpy as np
+import math
+
+
+from utils.Functions import getContourOfImage, sortPointsOnContourOfImage, removeBreakPointsOfContour, \
+ getSkeletonOfImage, removeBranchOfSkeletonLine, getEndPointsOfSkeletonLine, \
+ getCrossPointsOfSkeletonLine, getNumberOfValidPixels, splitConnectedComponents, \
+ min_distance_point2pointlist
+
+
+def main():
+
+ # 0107亻 1133壬 0554十 0427凹
+ path = "0554十.jpg"
+
+ # open image
+ img = cv2.imread(path, 0)
+ _, img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
+ img_rgb = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
+
+ # contour without break points
+ contour = getContourOfImage(img.copy())
+ contour = removeBreakPointsOfContour(contour)
+ contour_rgb = cv2.cvtColor(contour, cv2.COLOR_GRAY2RGB)
+
+ contours = splitConnectedComponents(contour)
+ print("contours num: %d" % len(contours))
+
+ contours_sorted = []
+ for cont in contours:
+ points = sortPointsOnContourOfImage(cont)
+ print("points num: %d" % len(points))
+ contours_sorted.append(points)
+
+ contour_points = []
+ for y in range(contour.shape[0]):
+ for x in range(contour.shape[1]):
+ if contour[y][x] == 0.0:
+ # black points
+ contour_points.append((x, y))
+ print("contour points num:%d" % len(contour_points))
+
+ # skeleton without extra branches
+ skeleton = getSkeletonOfImage(img.copy())
+ # remove extra branches
+ end_points = getEndPointsOfSkeletonLine(skeleton)
+ cross_points = getCrossPointsOfSkeletonLine(skeleton)
+ print("originale end: %d and cross: %d" % (len(end_points), len(cross_points)))
+ skeleton_nobranches = removeBranchOfSkeletonLine(skeleton.copy(), end_points, cross_points)
+ skeleton = skeleton_nobranches
+ # new end points and cross points
+ end_points = getEndPointsOfSkeletonLine(skeleton)
+ cross_points = getCrossPointsOfSkeletonLine(skeleton)
+ cross_points_bk = cross_points.copy()
+
+ # merge the close points
+ cross_points_merged = []
+ cross_distance_threshold = 10
+ used_index = []
+ for i in range(len(cross_points)):
+ if i in used_index:
+ continue
+ pt1 = cross_points[i]
+ midd_pt = None
+ used_index.append(i)
+ for j in range(len(cross_points)):
+ if i == j or j in used_index:
+ continue
+ pt2 = cross_points[j]
+
+ dist = math.sqrt((pt2[0] - pt1[0]) ** 2 + (pt2[1] - pt1[1]) ** 2)
+ if dist < cross_distance_threshold:
+ used_index.append(j)
+ offset = (pt1[0] - pt2[0], pt1[1] - pt2[1])
+ print(offset)
+ midd_pt = (pt2[0] + int(offset[0] / 2.), pt2[1] + int(offset[1] / 2.0))
+ if skeleton[midd_pt[1]][midd_pt[0]] == 0.0:
+ cross_points_merged.append(midd_pt)
+ else:
+ min_distance = 100000000
+ current_pt = None
+ for y in range(skeleton.shape[0]):
+ for x in range(skeleton.shape[1]):
+ if skeleton[y][x] == 0:
+ dist = math.sqrt((midd_pt[0] - x) ** 2 + (midd_pt[1] - y) ** 2)
+ if dist < min_distance:
+ min_distance = dist
+ current_pt = (x, y)
+ if current_pt:
+ cross_points_merged.append(current_pt)
+
+ print("After merge cross points num: %d" % len(cross_points_merged))
+ cross_points = cross_points_merged
+
+ print("After end: %d and cross: %d" % (len(end_points), len(cross_points)))
+ skeleton_rgb = cv2.cvtColor(skeleton, cv2.COLOR_GRAY2RGB)
+ # display all end points
+ for pt in end_points:
+ skeleton_rgb[pt[1]][pt[0]] = (0, 0, 255)
+ for pt in cross_points:
+ skeleton_rgb[pt[1]][pt[0]] = (0, 255, 0)
+ for pt in cross_points_bk:
+ skeleton_rgb[pt[1]][pt[0]] = (0, 0, 255)
+
+ # all corner points on contour
+ img = np.float32(img.copy())
+ dst = cv2.cornerHarris(img, 3, 3, 0.03)
+ dst = cv2.dilate(dst, None)
+
+ corners_area_points = []
+ for y in range(dst.shape[0]):
+ for x in range(dst.shape[1]):
+ if dst[y][x] > 0.1 * dst.max():
+ corners_area_points.append((x, y))
+ # show the corner points
+ for pt in corners_area_points:
+ if img[pt[1]][pt[0]] == 0:
+ img_rgb[pt[1]][pt[0]] = (0, 255, 0)
+ else:
+ img_rgb[pt[1]][pt[0]] = (0, 0, 255)
+ # all corner area points on the contour
+ corners_lines_points = []
+ for pt in corners_area_points:
+ if pt in contour_points:
+ corners_lines_points.append(pt)
+
+ for pt in corners_lines_points:
+ contour_rgb[pt[1]][pt[0]] = (0, 255, 0)
+
+ # merge points of corner points
+ corners_merged_points = []
+ for contour_sorted in contours_sorted:
+ i = 0
+ while True:
+ midd_index = -1
+ pt = contour_sorted[i]
+ if pt in corners_lines_points:
+ # red point
+ start = i
+ end = start
+ while True:
+ end += 1
+ if end >= len(contour_sorted):
+ break
+ # next point
+ next_pt = contour_sorted[end]
+ if next_pt in corners_lines_points:
+ # red point
+ continue
+ else:
+ # black point
+ break
+ end -= 1
+ midd_index = start + int((end - start) / 2.0)
+ i = end
+ i += 1
+ if i >= len(contour_sorted):
+ break
+ if midd_index != -1:
+ corners_merged_points.append(contour_sorted[midd_index])
+ print("After merged, corner points num: %d" % len(corners_merged_points))
+ for pt in corners_merged_points:
+ contour_rgb[pt[1]][pt[0]] = (0, 0, 255)
+
+ # remove the no-corner points
+ corners_points = []
+ threshold_distance = 30
+ for pt in corners_merged_points:
+ dist_cross = min_distance_point2pointlist(pt, cross_points)
+ dist_end = min_distance_point2pointlist(pt, end_points)
+ if dist_cross < threshold_distance and dist_end > threshold_distance / 3.:
+ corners_points.append(pt)
+ print("corner pints num: %d" % len(corners_points))
+ for pt in corners_points:
+ contour_rgb[pt[1]][pt[0]] = (255, 0, 0)
+
+ # segment contour to sub-contours based on the corner points
+ def segmentContourBasedOnCornerPoints(contour_sorted, corner_points):
+ """
+ Segment contour to sub-contours based on the corner points
+ :param contour_sorted:
+ :param corner_points:
+ :return:
+ """
+ if contour_sorted is None or corner_points is None:
+ return
+ # sub conotour index
+ sub_contour_index = []
+ for pt in corner_points:
+ index = contour_sorted.index(pt)
+ sub_contour_index.append(index)
+ print("sub contour index num: %d" % len(sub_contour_index))
+ sub_contours = []
+ for i in range(len(sub_contour_index)):
+ if i == len(sub_contour_index) - 1:
+ sub_contour = contour_sorted[sub_contour_index[i]:len(contour_sorted)] + contour_sorted[0: sub_contour_index[0] + 1]
+ else:
+ sub_contour = contour_sorted[sub_contour_index[i]:sub_contour_index[i + 1] + 1]
+ sub_contours.append(sub_contour)
+ print("sub contours num: %d" % len(sub_contours))
+
+ return sub_contours
+
+ # segment contour to sub-contours
+ for contour in contours:
+ cont_sorted = sortPointsOnContourOfImage(contour)
+ sub_contours = segmentContourBasedOnCornerPoints(cont_sorted, corners_points)
+
+ # cluster corner points
+ corner_points_cluster = []
+ used_index = []
+ colinear_couple = []
+ for i in range(len(corners_points)):
+ if i in used_index:
+ continue
+ for j in range(len(corners_points)):
+ if i == j or j in used_index:
+ continue
+ min_offset = min(abs(corners_points[i][0]-corners_points[j][0]), abs(corners_points[i][1]-corners_points[j][1]))
+ if min_offset < 20:
+ couple = [corners_points[i], corners_points[j]]
+ colinear_couple.append(couple)
+ used_index.append(j)
+ print("co linear num: %d" % len(colinear_couple ))
+
+
+ print("sub contours num: %d" % len(sub_contours))
+
+ stroke1_img = np.ones_like(contour) * 255
+ stroke1_img = np.array(stroke1_img, dtype=np.uint8)
+ stroke1_img_rgb = cv2.cvtColor(stroke1_img, cv2.COLOR_GRAY2RGB)
+
+ for pt in sub_contours[0]:
+ stroke1_img_rgb[pt[1]][pt[0]] = (0, 0, 0)
+ stroke1_img[pt[1]][pt[0]] = 0
+ for pt in sub_contours[2]:
+ stroke1_img_rgb[pt[1]][pt[0]] = (0, 0, 0)
+ stroke1_img[pt[1]][pt[0]] = 0
+
+ cv2.line(stroke1_img_rgb, sub_contours[0][0], sub_contours[2][-1], (0, 0, 255), 1)
+ cv2.line(stroke1_img_rgb, sub_contours[0][-1], sub_contours[2][0], (0, 0, 255), 1)
+ cv2.line(stroke1_img, sub_contours[0][0], sub_contours[2][-1], 0, 1)
+ cv2.line(stroke1_img, sub_contours[0][-1], sub_contours[2][0], 0, 1)
+
+ stroke2_img = np.ones_like(contour) * 255
+ stroke2_img = np.array(stroke2_img, dtype=np.uint8)
+ stroke2_img_rgb = cv2.cvtColor(stroke2_img, cv2.COLOR_GRAY2RGB)
+
+
+ for pt in sub_contours[1]:
+ stroke2_img_rgb[pt[1]][pt[0]] = (0, 0, 0)
+ stroke2_img[pt[1]][pt[0]] = 0
+ for pt in sub_contours[3]:
+ stroke2_img_rgb[pt[1]][pt[0]] = (0, 0, 0)
+ stroke2_img[pt[1]][pt[0]] = 0
+
+ cv2.line(stroke2_img_rgb, sub_contours[1][0], sub_contours[3][-1], (0, 0, 255), 1)
+ cv2.line(stroke2_img_rgb, sub_contours[1][-1], sub_contours[3][0], (0, 0, 255), 1)
+ cv2.line(stroke2_img, sub_contours[1][0], sub_contours[3][-1], 0, 1)
+ cv2.line(stroke2_img, sub_contours[1][-1], sub_contours[3][0], 0, 1)
+
+ storke1_points = sortPointsOnContourOfImage(stroke1_img)
+ stroke2_points = sortPointsOnContourOfImage(stroke2_img)
+
+ stroke1_img = np.ones_like(stroke1_img) * 255
+ stroke1_img = np.array(stroke1_img, dtype=np.uint8)
+
+ storke1_points = np.array([storke1_points], "int32")
+ cv2.fillPoly(stroke1_img, storke1_points, 0)
+
+ stroke2_img = np.ones_like(stroke2_img) * 255
+ stroke2_img = np.array(stroke2_img, dtype=np.uint8)
+
+ storke2_points = np.array([stroke2_points], "int32")
+ cv2.fillPoly(stroke2_img, storke2_points, 0)
+
+
+
+
+
+
+
+
+ # find corresponding sub-contours based on the co-linear couple
+ # for sub in sub_contours:
+ # pt1 = sub[0]
+ # pt2 = sub[-1]
+ #
+ # couples = []
+ # for coup in colinear_couple:
+ # if pt1 in coup or pt2 in coup:
+ # # if 4 points, 2 points should be in same sub-contour
+ # if pt1 in coup and pt2 in coup:
+ # continue
+ # couples.append(coup)
+ # print("sub couples num: %d" % len(couples))
+
+
+
+
+
+
+
+
+ # cv2.imshow("img rgb", img_rgb)
+ # cv2.imshow("skeleton", skeleton)
+ # cv2.imshow("skeleton no branches", skeleton_nobranches )
+ cv2.imshow("skeleton rgb", skeleton_rgb)
+ cv2.imshow("contour rgb", contour_rgb)
+ cv2.imshow("stroke 1", stroke1_img)
+ cv2.imshow("stroke 2", stroke2_img)
+ cv2.imshow("stroke1rgb", stroke1_img_rgb)
+ cv2.imshow("stroke2rgb", stroke2_img_rgb)
+
+ # for i in range(len(contours)):
+ # cv2.imshow("contour %d" % i, contours[i])
+
+ cv2.waitKey(0)
+ cv2.destroyAllWindows()
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/test/xyhistogramtool.py b/test/xyhistogramtool.py
new file mode 100644
index 0000000..bdd5d6a
--- /dev/null
+++ b/test/xyhistogramtool.py
@@ -0,0 +1,70 @@
+import numpy as np
+import cv2
+from matplotlib import pyplot as plt
+
+
+def main():
+ src_path = "../chars/src_dan_svg_simple_resized.png"
+ tag_path = "../chars/tag_dan_svg_simple_resized.png"
+
+ src_img = cv2.imread(src_path, 0)
+ tag_img = cv2.imread(tag_path, 0)
+
+ _, src_img = cv2.threshold(src_img, 127, 255, cv2.THRESH_BINARY)
+ _, tag_img = cv2.threshold(tag_img, 127, 255, cv2.THRESH_BINARY)
+
+ # x-axis and y-axis statistics histogram
+ src_x_hist = np.zeros(src_img.shape[1])
+ src_y_hist = np.zeros(src_img.shape[0])
+
+ tag_x_hist = np.zeros(tag_img.shape[1])
+ tag_y_hist = np.zeros(tag_img.shape[0])
+
+ for y in range(src_img.shape[0]):
+ for x in range(src_img.shape[1]):
+ if src_img[y][x] == 0:
+ src_y_hist[y] += 1
+ src_x_hist[x] += 1
+
+ for y in range(tag_img.shape[0]):
+ for x in range(tag_img.shape[1]):
+ if tag_img[y][x] == 0:
+ tag_y_hist[y] += 1
+ tag_x_hist[x] += 1
+
+ # print(src_x_hist)
+ # print(src_y_hist)
+
+ plt.subplot(221);
+ plt.plot(src_x_hist)
+ plt.title("X axis")
+ plt.ylabel("Source image")
+ plt.subplot(222);
+ plt.plot(src_y_hist)
+ plt.title("Y axis")
+ plt.subplot(223);
+ plt.ylabel("Target image")
+ plt.plot(tag_x_hist)
+ plt.subplot(224);
+ plt.plot(tag_y_hist)
+
+
+ # plt.subplot(121);
+ # line1, = plt.plot(src_x_hist, label="source")
+ # line2, = plt.plot(tag_x_hist, label="target")
+ # plt.xlabel("X axis")
+ # plt.legend(handles=[line1, line2], loc=2)
+ #
+ # plt.subplot(122);
+ # line3, = plt.plot(src_y_hist, label="source")
+ # line4, = plt.plot(tag_y_hist, label="target")
+ # plt.xlabel("Y axis")
+ #
+ # plt.legend(handles=[line3, line4], loc=2)
+
+ plt.show()
+
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test_images/0001ding.jpg b/test_images/0001ding.jpg
new file mode 100644
index 0000000..4856230
Binary files /dev/null and b/test_images/0001ding.jpg differ
diff --git a/test_images/0001ding_stroke.jpg b/test_images/0001ding_stroke.jpg
new file mode 100644
index 0000000..1078171
Binary files /dev/null and b/test_images/0001ding_stroke.jpg differ
diff --git "a/test_images/0034\344\270\262.jpg" "b/test_images/0034\344\270\262.jpg"
new file mode 100644
index 0000000..7b55f94
Binary files /dev/null and "b/test_images/0034\344\270\262.jpg" differ
diff --git "a/test_images/0059\344\271\230.jpg" "b/test_images/0059\344\271\230.jpg"
new file mode 100644
index 0000000..8da83a3
Binary files /dev/null and "b/test_images/0059\344\271\230.jpg" differ
diff --git "a/test_images/0098\344\272\251.jpg" "b/test_images/0098\344\272\251.jpg"
new file mode 100644
index 0000000..c422509
Binary files /dev/null and "b/test_images/0098\344\272\251.jpg" differ
diff --git "a/test_images/0107\344\272\273.jpg" "b/test_images/0107\344\272\273.jpg"
new file mode 100644
index 0000000..59d4939
Binary files /dev/null and "b/test_images/0107\344\272\273.jpg" differ
diff --git "a/test_images/0125\344\273\225.jpg" "b/test_images/0125\344\273\225.jpg"
new file mode 100644
index 0000000..6444e99
Binary files /dev/null and "b/test_images/0125\344\273\225.jpg" differ
diff --git "a/test_images/0195\344\275\233.jpg" "b/test_images/0195\344\275\233.jpg"
new file mode 100644
index 0000000..224e742
Binary files /dev/null and "b/test_images/0195\344\275\233.jpg" differ
diff --git "a/test_images/0242\344\277\204.jpg" "b/test_images/0242\344\277\204.jpg"
new file mode 100644
index 0000000..e8fdfc1
Binary files /dev/null and "b/test_images/0242\344\277\204.jpg" differ
diff --git "a/test_images/0301\345\201\245.jpg" "b/test_images/0301\345\201\245.jpg"
new file mode 100644
index 0000000..86bc65a
Binary files /dev/null and "b/test_images/0301\345\201\245.jpg" differ
diff --git "a/test_images/0427\345\207\271.jpg" "b/test_images/0427\345\207\271.jpg"
new file mode 100644
index 0000000..20bcf4e
Binary files /dev/null and "b/test_images/0427\345\207\271.jpg" differ
diff --git "a/test_images/0554\345\215\201.jpg" "b/test_images/0554\345\215\201.jpg"
new file mode 100644
index 0000000..7fe8596
Binary files /dev/null and "b/test_images/0554\345\215\201.jpg" differ
diff --git "a/test_images/0631\345\217\237.jpg" "b/test_images/0631\345\217\237.jpg"
new file mode 100644
index 0000000..4d484f8
Binary files /dev/null and "b/test_images/0631\345\217\237.jpg" differ
diff --git "a/test_images/0633\345\217\243.jpg" "b/test_images/0633\345\217\243.jpg"
new file mode 100644
index 0000000..a410845
Binary files /dev/null and "b/test_images/0633\345\217\243.jpg" differ
diff --git "a/test_images/0860\345\226\204.jpg" "b/test_images/0860\345\226\204.jpg"
new file mode 100644
index 0000000..b0cd855
Binary files /dev/null and "b/test_images/0860\345\226\204.jpg" differ
diff --git "a/test_images/1133\345\243\254.jpg" "b/test_images/1133\345\243\254.jpg"
new file mode 100644
index 0000000..5ccc0ed
Binary files /dev/null and "b/test_images/1133\345\243\254.jpg" differ
diff --git "a/test_images/2252\346\224\257.jpg" "b/test_images/2252\346\224\257.jpg"
new file mode 100644
index 0000000..30c143d
Binary files /dev/null and "b/test_images/2252\346\224\257.jpg" differ
diff --git a/test_images/ben.png b/test_images/ben.png
new file mode 100644
index 0000000..733f4e4
Binary files /dev/null and b/test_images/ben.png differ
diff --git a/test_images/ben_stroke_1.png b/test_images/ben_stroke_1.png
new file mode 100644
index 0000000..7950a06
Binary files /dev/null and b/test_images/ben_stroke_1.png differ
diff --git a/test_images/ben_stroke_3.png b/test_images/ben_stroke_3.png
new file mode 100644
index 0000000..835a426
Binary files /dev/null and b/test_images/ben_stroke_3.png differ
diff --git a/test_images/cover.png b/test_images/cover.png
new file mode 100644
index 0000000..69dbb06
Binary files /dev/null and b/test_images/cover.png differ
diff --git a/test_images/exit.png b/test_images/exit.png
new file mode 100644
index 0000000..ca43b46
Binary files /dev/null and b/test_images/exit.png differ
diff --git a/test_images/hurulin3.jpg b/test_images/hurulin3.jpg
new file mode 100644
index 0000000..c52eadb
Binary files /dev/null and b/test_images/hurulin3.jpg differ
diff --git a/test_images/open.png b/test_images/open.png
new file mode 100644
index 0000000..875ce9e
Binary files /dev/null and b/test_images/open.png differ
diff --git a/test_images/quan.png b/test_images/quan.png
new file mode 100644
index 0000000..303782d
Binary files /dev/null and b/test_images/quan.png differ
diff --git a/test_images/quan1.png b/test_images/quan1.png
new file mode 100644
index 0000000..0b53abc
Binary files /dev/null and b/test_images/quan1.png differ
diff --git a/test_images/radical_gray.jpg b/test_images/radical_gray.jpg
new file mode 100644
index 0000000..7002b08
Binary files /dev/null and b/test_images/radical_gray.jpg differ
diff --git a/test_images/seg_test.jpg b/test_images/seg_test.jpg
new file mode 100644
index 0000000..f30c65b
Binary files /dev/null and b/test_images/seg_test.jpg differ
diff --git a/test_images/skele.png b/test_images/skele.png
new file mode 100644
index 0000000..6f4f805
Binary files /dev/null and b/test_images/skele.png differ
diff --git a/test_images/src_resize.png b/test_images/src_resize.png
new file mode 100644
index 0000000..d041979
Binary files /dev/null and b/test_images/src_resize.png differ
diff --git a/test_images/tag_resize.png b/test_images/tag_resize.png
new file mode 100644
index 0000000..87ec0bc
Binary files /dev/null and b/test_images/tag_resize.png differ
diff --git a/test_images/test.png b/test_images/test.png
new file mode 100644
index 0000000..4fa4c7a
Binary files /dev/null and b/test_images/test.png differ
diff --git a/test_images/test111.png b/test_images/test111.png
new file mode 100644
index 0000000..04e2e4d
Binary files /dev/null and b/test_images/test111.png differ