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: + + + + + + + + + + + + + 0 + 0 + 1128 + 22 + + + + + + + + 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 + + + + + + + + + + 0 + 0 + 1343 + 22 + + + + + + + + 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 + + + + + + + 0 + 0 + 1056 + 22 + + + + + + + + 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 + + + + + + + + + + 0 + 0 + 1030 + 22 + + + + + + + + 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 + + + + + + + + + + 0 + 0 + 1278 + 22 + + + + + + + + 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 + + + + + + + + + + 0 + 0 + 1278 + 22 + + + + + + + + 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