def __init__(self, window): super().__init__(window) self.left_viewer = PhotoViewer(self, window) # Debug styling self.setStyleSheet("border: 2px solid gray") self.left_viewer.setStyleSheet("border: 2px solid blue") self.left_viewer.setUpdatesEnabled(True) layout = QHBoxLayout(self) layout.addWidget(self.left_viewer) self.setLayout(layout)
def __init__(self, parent=None): super(BPA_App, self).__init__(parent) self.setupUi(self) self.setWindowTitle("Automated Blood Stain Pattern Analysis - ABPA") self.viewer = PhotoViewer(self.centralwidget) self.viewer.setMinimumSize(500, 500) self.horizontalLayout.addWidget(self.viewer) self.actionLoad.triggered.connect(self.load_image) self.actionExport.triggered.connect(self.export) self.actionSegment_Image.triggered.connect(self.show_metrics) self.actionBatch_process.triggered.connect(self.show_batch_dialog) self.file_name = "" self.folder_name = '' self.progressBar.hide() self.pixmap = None self.result = None self.annotations = {} self.pattern_metrics = True
class DoubleViewer(QLabel): def __init__(self, window): super().__init__(window) self.left_viewer = PhotoViewer(self, window) # Debug styling self.setStyleSheet("border: 2px solid gray") self.left_viewer.setStyleSheet("border: 2px solid blue") self.left_viewer.setUpdatesEnabled(True) layout = QHBoxLayout(self) layout.addWidget(self.left_viewer) self.setLayout(layout) def setPhoto(self, image, image_name=None): self.left_viewer.setPhoto(image, image_name, self.left_viewer) self.update()
def init_WINDOW(self): # 存储加载的图片和抽取的轮廓 self.image = array([]) self.cnt = array([]) self.color_dict = get_corlor_list() self.choosePoints = [] self.ocr_flag = False # 鼠标点击坐标和鼠标释放坐标 self.x_clicked = 0 self.y_clicked = 0 self.x_released = 0 self.y_released = 0 self.fnmae = '' # 加载的map self.baidu_api = BaiDuAPI() # 图片图元 self.viewer = PhotoViewer(self) # UI初始化 # 'Load image' button self.btnLoad = QPushButton(self) self.btnLoad.setText('图片加载') # self.btnLoad.setText('Img Load') self.btnLoad.clicked.connect(self.loadImage) self.editYearInfo = QLineEdit(self) self.editYearInfo.setPlaceholderText('时间') # Button to change from drag/pan to getting pixel info self.btnClickDrag = QPushButton(self) self.btnClickDrag.setText('点击/拖拽') self.btnClickDrag.setIcon(QIcon("image/dragIcon.png")) self.btnClickDrag.clicked.connect(self.clickDrag) # 点击处的信息 self.editClickInfo = QLineEdit(self) self.editClickInfo.setReadOnly(True) self.viewer.mouse_clicked.connect( self.photoClicked) # 自建的鼠标点击的传递信号连接photoClicked函数 # 轮廓生成按钮,连接轮廓生成函数 self.btnContour = QPushButton(self) self.btnContour.setText('轮廓生成') self.btnContour.clicked.connect(self.getContour) # 轮廓拖动按钮,连接轮廓拖动函数 self.btnDrag = QPushButton(self) self.btnDrag.setText("轮廓拖拽") self.btnDrag.clicked.connect(self.dragOutline) self.viewer.mouse_released.connect( self.mouse_releasePoint) # 自建鼠标松开信号连接了两个函数 self.viewer.mouse_released.connect(self.contourDraged) # 获取轮廓信息的按钮 self.btnContourInfo = QPushButton(self) self.btnContourInfo.setText("轮廓信息") # self.btnContourInfo.setText("Contour Info") self.btnContourInfo.clicked.connect(self.contour_info) # 显示轮廓的信息 self.editcontourName = QLineEdit(self) self.editcontourName.setPlaceholderText('轮廓名称') # self.editcontourName.setPlaceholderText('Contour Name') self.editContourArea = QLineEdit(self) self.editContourArea.setReadOnly(True) self.editContourArea.setPlaceholderText("轮廓面积") # self.editContourArea.setPlaceholderText("Area") self.editContourPerimeter = QLineEdit(self) self.editContourPerimeter.setReadOnly(True) self.editContourPerimeter.setPlaceholderText("轮廓周长") self.editContourCentre = QLineEdit(self) self.editContourCentre.setPlaceholderText("轮廓重心") self.editContourCentre.setReadOnly(True) # 存储轮廓的信息 self.btnContourSave = QPushButton(self) self.btnContourSave.setText("存储轮廓信息") # self.btnContourSave.setText("Store Profile Information") self.btnContourSave.clicked.connect(self.save_contour) # 地点识别 self.btnSite = QPushButton(self) self.btnSite.setText("地点选择") # self.btnSite.setText("Site Selection") self.btnSite.clicked.connect(self.site_choose) self.viewer.mouse_moved.connect(self.draw_line) self.btnSiteInfo = QPushButton(self) self.btnSiteInfo.setText("地点信息") # self.btnSiteInfo.setText("Site Info") self.btnSiteInfo.clicked.connect(self.site_info) self.site_ContourName = QLineEdit(self) self.site_ContourName.setPlaceholderText("所属轮廓") # self.site_ContourName.setPlaceholderText("Contour Belong") self.editSiteName = QLineEdit(self) self.editSiteName.setPlaceholderText("地名") self.editSiteSlope = QLineEdit(self) self.editSiteSlope.setPlaceholderText('斜度') self.editSiteLenth = QLineEdit(self) self.editSiteLenth.setPlaceholderText('长度') self.editSitePos = QLineEdit(self) self.editSitePos.setPlaceholderText("位置") self.btnSiteSave = QPushButton(self) self.btnSiteSave.setText("存储地点信息") self.btnSiteSave.clicked.connect(self.save_site) self.setStyleSheet("QPushButton{background-color: rgb(39, 118, 148)}" "QPushButton{color:white}" "QPushButton:hover{color:yellow}" "QPushButton:pressed{color:red;}" "QPushButton{border-radius:5px}" "QPushButton{border:2px groove gray}" "QPushButton{border-style: outset}" "QPushButton{padding:2px 4px}" "QLineEdit {border:2px groove gray}" "QLineEdit {border-radius:5px}" "QLineEdit{padding: 2px 4px}") # Arrange layout VBlayout = QVBoxLayout(self) VBlayout.addWidget(self.viewer) HBlayout = QHBoxLayout() HBlayout.setAlignment(Qt.AlignLeft) HBlayout.addWidget(self.btnLoad) HBlayout.addWidget(self.editYearInfo) HBlayout.addWidget(self.btnClickDrag) HBlayout.addWidget(self.editClickInfo) HBlayout.addWidget(self.btnContour) HBlayout.addWidget(self.btnDrag) # 第二行信息 HBlayoutSecond = QHBoxLayout() HBlayoutSecond.setAlignment(Qt.AlignLeft) HBlayoutSecond.addWidget(self.btnContourInfo) HBlayoutSecond.addWidget(self.editcontourName) HBlayoutSecond.addWidget(self.editContourArea) HBlayoutSecond.addWidget(self.editContourPerimeter) HBlayoutSecond.addWidget(self.editContourCentre) HBlayoutSecond.addWidget(self.btnContourSave) # 第三行信息 HBlayoutThird = QHBoxLayout() HBlayoutThird.setAlignment(Qt.AlignLeft) HBlayoutThird.addWidget(self.btnSite) HBlayoutThird.addWidget(self.btnSiteInfo) HBlayoutThird.addWidget(self.site_ContourName) HBlayoutThird.addWidget(self.editSiteName) HBlayoutThird.addWidget(self.editSiteSlope) HBlayoutThird.addWidget(self.editSiteLenth) HBlayoutThird.addWidget(self.editSitePos) HBlayoutThird.addWidget(self.btnSiteSave) VBlayout.addLayout(HBlayout) VBlayout.addLayout(HBlayoutSecond) VBlayout.addLayout(HBlayoutThird) self.setGeometry(500, 300, 800, 600) self.setWindowTitle('历史时空') self.setWindowIcon(QIcon('windowIcon.png')) self.show()
class Window(QWidget): def __init__(self): super(Window, self).__init__() self.init_WINDOW() def init_WINDOW(self): # 存储加载的图片和抽取的轮廓 self.image = array([]) self.cnt = array([]) self.color_dict = get_corlor_list() self.choosePoints = [] self.ocr_flag = False # 鼠标点击坐标和鼠标释放坐标 self.x_clicked = 0 self.y_clicked = 0 self.x_released = 0 self.y_released = 0 self.fnmae = '' # 加载的map self.baidu_api = BaiDuAPI() # 图片图元 self.viewer = PhotoViewer(self) # UI初始化 # 'Load image' button self.btnLoad = QPushButton(self) self.btnLoad.setText('图片加载') # self.btnLoad.setText('Img Load') self.btnLoad.clicked.connect(self.loadImage) self.editYearInfo = QLineEdit(self) self.editYearInfo.setPlaceholderText('时间') # Button to change from drag/pan to getting pixel info self.btnClickDrag = QPushButton(self) self.btnClickDrag.setText('点击/拖拽') self.btnClickDrag.setIcon(QIcon("image/dragIcon.png")) self.btnClickDrag.clicked.connect(self.clickDrag) # 点击处的信息 self.editClickInfo = QLineEdit(self) self.editClickInfo.setReadOnly(True) self.viewer.mouse_clicked.connect( self.photoClicked) # 自建的鼠标点击的传递信号连接photoClicked函数 # 轮廓生成按钮,连接轮廓生成函数 self.btnContour = QPushButton(self) self.btnContour.setText('轮廓生成') self.btnContour.clicked.connect(self.getContour) # 轮廓拖动按钮,连接轮廓拖动函数 self.btnDrag = QPushButton(self) self.btnDrag.setText("轮廓拖拽") self.btnDrag.clicked.connect(self.dragOutline) self.viewer.mouse_released.connect( self.mouse_releasePoint) # 自建鼠标松开信号连接了两个函数 self.viewer.mouse_released.connect(self.contourDraged) # 获取轮廓信息的按钮 self.btnContourInfo = QPushButton(self) self.btnContourInfo.setText("轮廓信息") # self.btnContourInfo.setText("Contour Info") self.btnContourInfo.clicked.connect(self.contour_info) # 显示轮廓的信息 self.editcontourName = QLineEdit(self) self.editcontourName.setPlaceholderText('轮廓名称') # self.editcontourName.setPlaceholderText('Contour Name') self.editContourArea = QLineEdit(self) self.editContourArea.setReadOnly(True) self.editContourArea.setPlaceholderText("轮廓面积") # self.editContourArea.setPlaceholderText("Area") self.editContourPerimeter = QLineEdit(self) self.editContourPerimeter.setReadOnly(True) self.editContourPerimeter.setPlaceholderText("轮廓周长") self.editContourCentre = QLineEdit(self) self.editContourCentre.setPlaceholderText("轮廓重心") self.editContourCentre.setReadOnly(True) # 存储轮廓的信息 self.btnContourSave = QPushButton(self) self.btnContourSave.setText("存储轮廓信息") # self.btnContourSave.setText("Store Profile Information") self.btnContourSave.clicked.connect(self.save_contour) # 地点识别 self.btnSite = QPushButton(self) self.btnSite.setText("地点选择") # self.btnSite.setText("Site Selection") self.btnSite.clicked.connect(self.site_choose) self.viewer.mouse_moved.connect(self.draw_line) self.btnSiteInfo = QPushButton(self) self.btnSiteInfo.setText("地点信息") # self.btnSiteInfo.setText("Site Info") self.btnSiteInfo.clicked.connect(self.site_info) self.site_ContourName = QLineEdit(self) self.site_ContourName.setPlaceholderText("所属轮廓") # self.site_ContourName.setPlaceholderText("Contour Belong") self.editSiteName = QLineEdit(self) self.editSiteName.setPlaceholderText("地名") self.editSiteSlope = QLineEdit(self) self.editSiteSlope.setPlaceholderText('斜度') self.editSiteLenth = QLineEdit(self) self.editSiteLenth.setPlaceholderText('长度') self.editSitePos = QLineEdit(self) self.editSitePos.setPlaceholderText("位置") self.btnSiteSave = QPushButton(self) self.btnSiteSave.setText("存储地点信息") self.btnSiteSave.clicked.connect(self.save_site) self.setStyleSheet("QPushButton{background-color: rgb(39, 118, 148)}" "QPushButton{color:white}" "QPushButton:hover{color:yellow}" "QPushButton:pressed{color:red;}" "QPushButton{border-radius:5px}" "QPushButton{border:2px groove gray}" "QPushButton{border-style: outset}" "QPushButton{padding:2px 4px}" "QLineEdit {border:2px groove gray}" "QLineEdit {border-radius:5px}" "QLineEdit{padding: 2px 4px}") # Arrange layout VBlayout = QVBoxLayout(self) VBlayout.addWidget(self.viewer) HBlayout = QHBoxLayout() HBlayout.setAlignment(Qt.AlignLeft) HBlayout.addWidget(self.btnLoad) HBlayout.addWidget(self.editYearInfo) HBlayout.addWidget(self.btnClickDrag) HBlayout.addWidget(self.editClickInfo) HBlayout.addWidget(self.btnContour) HBlayout.addWidget(self.btnDrag) # 第二行信息 HBlayoutSecond = QHBoxLayout() HBlayoutSecond.setAlignment(Qt.AlignLeft) HBlayoutSecond.addWidget(self.btnContourInfo) HBlayoutSecond.addWidget(self.editcontourName) HBlayoutSecond.addWidget(self.editContourArea) HBlayoutSecond.addWidget(self.editContourPerimeter) HBlayoutSecond.addWidget(self.editContourCentre) HBlayoutSecond.addWidget(self.btnContourSave) # 第三行信息 HBlayoutThird = QHBoxLayout() HBlayoutThird.setAlignment(Qt.AlignLeft) HBlayoutThird.addWidget(self.btnSite) HBlayoutThird.addWidget(self.btnSiteInfo) HBlayoutThird.addWidget(self.site_ContourName) HBlayoutThird.addWidget(self.editSiteName) HBlayoutThird.addWidget(self.editSiteSlope) HBlayoutThird.addWidget(self.editSiteLenth) HBlayoutThird.addWidget(self.editSitePos) HBlayoutThird.addWidget(self.btnSiteSave) VBlayout.addLayout(HBlayout) VBlayout.addLayout(HBlayoutSecond) VBlayout.addLayout(HBlayoutThird) self.setGeometry(500, 300, 800, 600) self.setWindowTitle('历史时空') self.setWindowIcon(QIcon('windowIcon.png')) self.show() # 图片加载,并存入数据库 def loadImage(self): fname, _ = QFileDialog.getOpenFileName( self, "选择地图", 'D:\\0Kind of File\\Map\\中国历史地图第三版', 'Image files(*.jpg *.gif *.png)') # fi = QtCott # re.QFileInfo(fname) 获取fname的信息 if not fname: pass else: # 从新加载图片,清空变量 # 存储加载的图片和抽取的轮廓 self.image = array([]) self.cnt = array([]) # 鼠标点击坐标和鼠标释放坐标 self.x_clicked = 0 self.y_clicked = 0 self.x_released = 0 self.y_released = 0 self.image = cv.imdecode(fromfile(fname, dtype=uint8), -1) year_img = self.image[0:100, 0:500] cut_bila = cv.bilateralFilter(year_img, d=0, sigmaColor=75, sigmaSpace=15) cv.imwrite('image/year.jpg', cut_bila) year_str = self.baidu_api.picture2text('image/year.jpg') year_int = findall(r'\d+', year_str) if '前' in year_str: map_year = -int(year_int[0]) else: map_year = int(year_int[0]) myimage = open(fname, 'rb') map_img = myimage.read() mysqlConn = MySqlConn() self.editYearInfo.setText(str(map_year)) # print(len(map_img)) mysqlConn.img_insert(map_year=map_year, map_img=map_img) image_height, image_width, image_depth = self.image.shape QIm = cv.cvtColor(self.image, cv.COLOR_BGR2RGB) QIm = QImage(QIm.data, image_width, image_height, image_width * image_depth, QImage.Format_RGB888) self.viewer.setPhoto(QPixmap.fromImage(QIm)) self.viewer.fitInView() # 鼠标切换点击和拖拽模式 def clickDrag(self): if self.viewer._contour or self.viewer._ocr: pass else: if self.viewer.dragMode(): self.btnClickDrag.setIcon(QIcon("image/clickIcon.png")) else: self.btnClickDrag.setIcon(QIcon("image/dragIcon.png")) self.viewer.toggleDragMode() # 传递鼠标点击的坐标 def photoClicked(self, pos): if self.viewer.dragMode() == QGraphicsView.NoDrag: self.editClickInfo.setText('%d, %d' % (pos.x(), pos.y())) self.x_clicked = pos.x() self.y_clicked = pos.y() # 获取轮廓 def getContour(self): # 获取鼠标点击处的hsv值 hsv = cv.cvtColor(self.image, cv.COLOR_BGR2HSV) # 使用区域的hsv均值代替某一点的hsv的值 area_hsv = [0, 0, 0] for i in range(-3, 6, 3): for j in range(-3, 6, 3): area_hsv = area_hsv + hsv[self.y_clicked - i, self.x_clicked - j] color_hsv = area_hsv // 9 # 判断需要提取的hsv区间 color = False for key, value in self.color_dict.items(): if color_hsv[0] >= value[0][0] and color_hsv[0] <= value[1][0] and color_hsv[1] >= value[0][1] and \ color_hsv[1] <= value[1][1] and color_hsv[2] >= value[0][2] and color_hsv[2] <= value[1][2]: color = True lower_HSV = value[0] upper_HSV = value[1] self.color_dict.pop(key) self.color_dict[key] = value break if not color: print("请重新选区颜色") else: # 中值滤波去除椒盐噪声 blurImg = cv.medianBlur(self.image, 21) # cv.namedWindow("BlurImg",cv.WINDOW_KEEPRATIO) # cv.imshow("BlurImg",blurImg) # cv.imwrite("blur.jpg",blurImg) img = cv.cvtColor(blurImg, cv.COLOR_BGR2HSV) # 提取对应的hsv区域 mask = cv.inRange(img, lower_HSV, upper_HSV) choose_color = cv.bitwise_and(img, img, mask=mask) result = cv.cvtColor(choose_color, cv.COLOR_HSV2BGR) gray = cv.cvtColor(result, cv.COLOR_BGR2GRAY) # 图象灰度化 # 提取图象梯度(可改进) gradX = cv.Sobel(gray, ddepth=cv.CV_32F, dx=1, dy=0, ksize=-1) gradY = cv.Sobel(gray, ddepth=cv.CV_32F, dx=0, dy=1, ksize=-1) # gradX = cv.Scharr(gray, cv.CV_64F, 1, 0) # gradY = cv.Scharr(gray, cv.CV_64F, 0, 1) # 保留水平和竖直梯度大的 gradient = cv.max(gradX, gradY) # cv.namedWindow("Img0", cv.WINDOW_KEEPRATIO) # cv.imshow("Img0", gradient) # cv.imwrite("img0.jpg", gradient) gradient = cv.convertScaleAbs(gradient) # 寻找轮廓 # 采用三角形法进行二值化 # hist = cv.calcHist([gradient],[0],None,[256],[0,256]) # plt.plot(hist) # plt.show() ret, th = cv.threshold(gradient, 0, 255, cv.THRESH_BINARY | cv.THRESH_TRIANGLE) # cv.namedWindow("Img", cv.WINDOW_KEEPRATIO) # cv.imshow("Img", th) # cv.imwrite("img.jpg", th) kernel = cv.getStructuringElement(cv.MORPH_ELLIPSE, (5, 5)) # 闭运算,先腐蚀后膨胀,去除黑色小点 closed = cv.morphologyEx(th, cv.MORPH_CLOSE, kernel, iterations=6) # 轮廓生成 # cv.namedWindow("Img1", cv.WINDOW_KEEPRATIO) # cv.imshow("Img1", closed) # cv.imwrite("img1.jpg", closed) # OpenCV3 的findContours有三个返回值,OpenCV 4的有四个返回值 # 应该安装opencv-python 3.*版本。 myImage, cnts, hierarchy = cv.findContours(closed, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_NONE) #print(cnts.size) print(type(cnts)) print(len(cnts)) num = 0 con_exit = False for cnt in cnts: # pointPolygonTest检测点是否在轮廓内, # 若返回值为+1,表示点在多边形内部,返回值为-1,表示在多边形外部,返回值为0,表示在多边形上 mesure = cv.pointPolygonTest(cnt, (self.x_clicked, self.y_clicked), measureDist=False) # 鼠标在轮廓内部 if mesure == 1: con_exit = True break num = num + 1 if con_exit: # 点在轮廓内 self.cnt = cnts[num] # 确定轮廓 # print(self.cnt) # print(self.cnt.shape) #print(type(self.cnt)) back = ones(self.image.shape, uint8) * 255 img_contour = cv.drawContours(self.image.copy(), cnts, num, (255, 255, 255), 3) # img_contour1 = cv.drawContours(back, cnts, num, (0, 0, 255), 3) # 重新渲染 # cv.namedWindow("Img2", cv.WINDOW_KEEPRATIO) # cv.imshow("Img2", img_contour1) # cv.imwrite("img2.jpg", img_contour1) image_height, image_width, image_depth = img_contour.shape QIm = cv.cvtColor(img_contour, cv.COLOR_BGR2RGB) QIm = QImage(QIm.data, image_width, image_height, image_width * image_depth, QImage.Format_RGB888) self.viewer.setPhoto(QPixmap.fromImage(QIm)) # 重新渲染后,图元回到拖拽模式 self.btnClickDrag.setIcon(QIcon("image/dragIcon.png")) else: print("重新选区颜色点") # 轮廓拖动模式切换 def dragOutline(self): if not self.viewer._contour: self.viewer._contour = True self.viewer.toggleDragMode() self.btnClickDrag.setIcon(QIcon("image/clickIcon.png")) self.btnDrag.setStyleSheet("QPushButton{color:red}") else: self.viewer._contour = False self.btnDrag.setStyleSheet("QPushButton{color:white}") # 获取鼠标松开的位置坐标 def mouse_releasePoint(self, pos): self.x_released = pos.x() self.y_released = pos.y() # 轮廓拖动函数 def contourDraged(self, pos): if self.viewer.dragMode( ) == QGraphicsView.NoDrag and self.viewer._contour == True: if self.cnt.any(): for p in self.cnt: if ((p[0][0] - self.x_clicked)**2 + (p[0][1] - self.y_clicked)**2) <= 25: p[0][0] = p[0][0] + (pos.x() - self.x_clicked) * 0.9 p[0][1] = p[0][1] + (pos.y() - self.y_clicked) * 0.9 elif ((p[0][0] - self.x_clicked)**2 + (p[0][1] - self.y_clicked)**2) <= 100: p[0][0] = p[0][0] + (pos.x() - self.x_clicked) * 0.7 p[0][1] = p[0][1] + (pos.y() - self.y_clicked) * 0.7 elif ((p[0][0] - self.x_clicked)**2 + (p[0][1] - self.y_clicked)**2) <= 225: p[0][0] = p[0][0] + (pos.x() - self.x_clicked) * 0.5 p[0][1] = p[0][1] + (pos.y() - self.y_clicked) * 0.5 elif ((p[0][0] - self.x_clicked)**2 + (p[0][1] - self.y_clicked)**2) <= 400: p[0][0] = p[0][0] + (pos.x() - self.x_clicked) * 0.3 p[0][1] = p[0][1] + (pos.y() - self.y_clicked) * 0.3 elif ((p[0][0] - self.x_clicked)**2 + (p[0][1] - self.y_clicked)**2) <= 625: p[0][0] = p[0][0] + (pos.x() - self.x_clicked) * 0.1 p[0][1] = p[0][1] + (pos.y() - self.y_clicked) * 0.1 # 重新渲染图元 img_contour = cv.drawContours(self.image.copy(), self.cnt, -1, (255, 255, 255), 3) image_height, image_width, image_depth = img_contour.shape QIm = cv.cvtColor(img_contour, cv.COLOR_BGR2RGB) QIm = QImage(QIm.data, image_width, image_height, image_width * image_depth, QImage.Format_RGB888) self.viewer.setPhoto(QPixmap.fromImage(QIm)) self.viewer.curve_mode = True self.viewer.toggleDragMode() # 判断是否还处于拖拽模式 # 获取轮廓的信息 def contour_info(self): if self.cnt.any(): ROI = zeros(self.image.shape[:2], uint8) proimage = self.image.copy() roi = cv.drawContours(ROI, [self.cnt], 0, (255, 255, 255), -1) x, y, w, h = cv.boundingRect(self.cnt) imgroi = cv.bitwise_and(proimage, proimage, mask=roi) site = imgroi[y:y + h, x:x + w] median_img = cv.medianBlur(site, 9) img_gray = cv.cvtColor(median_img, cv.COLOR_BGR2GRAY) th = cv.adaptiveThreshold(img_gray, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY_INV, 65, 30) g = cv.getStructuringElement(cv.MORPH_RECT, (2, 1)) # 形态学处理,开运算 ersion = cv.erode(th, g, iterations=4) cv.imwrite('image/contour_name.jpg', ersion) contour_name = self.baidu_api.picture2text( 'image/contour_name.jpg') self.editcontourName.setText(contour_name) contour_area = cv.contourArea(self.cnt) contour_perimeter = cv.arcLength(self.cnt, True) M = cv.moments(self.cnt) self.contour_centre = str(int(M['m10'] / M['m00'])) + ',' + str( int(M['m01'] / M['m00'])) self.editContourArea.setText("%d" % (contour_area)) self.editContourPerimeter.setText("%d" % (contour_perimeter)) self.editContourCentre.setText( "%d,%d" % (int(M['m10'] / M['m00']), int(M['m01'] / M['m00']))) # 轮廓存储 def save_contour(self): # 判断鼠标点击是否在原轮廓, # mesure = cv.pointPolygonTest(self.cnt, (self.x_clicked, self.y_clicked), measureDist=False) # # 不在,说明原轮廓处理完毕,可以将其所包含的区域填充黑色 # if mesure != 1: # # 将self.image 修改了 # 获取色调范围列表 self.image = cv.drawContours(self.image, [self.cnt], 0, (0, 0, 0), -1) contour_year = int(self.editYearInfo.text()) contour_name = self.editcontourName.text() contour_points_arr = self.cnt print(contour_points_arr.shape) # narray 转换为二进制 contour_points = dumps(contour_points_arr) print(len(contour_points)) contour_area = float(self.editContourArea.text()) contour_perimeter = float(self.editContourPerimeter.text()) contour_centre = self.editContourCentre.text() mysqlConn = MySqlConn() mysqlConn.contour_insert(contour_year, contour_name, contour_points, contour_area, contour_perimeter, contour_centre) # 开启文字识别模式 def site_choose(self): if not self.viewer._ocr: self.viewer._ocr = True self.viewer.toggleDragMode() self.x_clicked = 0 self.y_clicked = 0 self.x_released = 0 self.y_released = 0 self.btnSite.setStyleSheet("QPushButton{color:red}") self.ocr_flag = True self.btnClickDrag.setIcon(QIcon("image/clickIcon")) else: self.viewer._ocr = False self.btnSite.setStyleSheet("QPushButton{color:white}") self.ocr_flag = False # 文字区域画线 def draw_line(self, pos): if self.viewer._ocr and self.x_clicked or self.y_clicked: img_line = cv.line(self.image.copy(), (self.x_clicked, self.y_clicked), (pos.x(), pos.y()), (0, 0, 0), 2) image_height, image_width, image_depth = img_line.shape QIm = cv.cvtColor(img_line, cv.COLOR_BGR2RGB) QIm = QImage(QIm.data, image_width, image_height, image_width * image_depth, QImage.Format_RGB888) self.viewer.setPhoto(QPixmap.fromImage(QIm)) # 终止画线 if self.x_released or self.y_released: self.choosePoints = [] # 初始化存储点组 inf = float("inf") if self.x_released or self.y_released: if (self.x_released - self.x_clicked) == 0: slope = inf else: slope = (self.y_released - self.y_clicked) / ( self.x_released - self.x_clicked) siteLenth = 0.5 * math.sqrt( square(self.y_released - self.y_clicked) + square(self.x_released - self.x_clicked)) mySiteLenth = 2 * siteLenth self.siteLenth = ("%.2f" % mySiteLenth) self.editSiteLenth.setText(self.siteLenth) radian = math.atan(slope) self.siteSlope = ("%.2f" % radian) self.editSiteSlope.setText(self.siteSlope) x_bas = math.ceil(math.fabs(0.5 * siteLenth * math.sin(radian))) y_bas = math.ceil(math.fabs(0.5 * siteLenth * math.cos(radian))) if slope <= 0: self.choosePoints.append([(self.x_clicked - x_bas), (self.y_clicked - y_bas)]) self.choosePoints.append([(self.x_clicked + x_bas), (self.y_clicked + y_bas)]) self.choosePoints.append([(self.x_released + x_bas), (self.y_released + y_bas)]) self.choosePoints.append([(self.x_released - x_bas), (self.y_released - y_bas)]) elif slope > 0: self.choosePoints.append([(self.x_clicked + x_bas), (self.y_clicked - y_bas)]) self.choosePoints.append([(self.x_clicked - x_bas), (self.y_clicked + y_bas)]) self.choosePoints.append([(self.x_released - x_bas), (self.y_released + y_bas)]) self.choosePoints.append([(self.x_released + x_bas), (self.y_released - y_bas)]) self.viewer._ocr = False # 求地点信息 def site_info(self): if self.ocr_flag: if self.choosePoints: site_contourName = self.editcontourName.text() self.site_ContourName.setText(site_contourName) points = array([self.choosePoints], int32) pts = points.reshape(-1, 1, 2) ROI = zeros(self.image.shape[:2], uint8) proimage = self.image.copy() roi = cv.drawContours(ROI, [pts], 0, (255, 255, 255), -1) x, y, w, h = cv.boundingRect(pts) imgroi = cv.bitwise_and(proimage, proimage, mask=roi) site = imgroi[y:y + h, x:x + w] cut_bila = cv.bilateralFilter(site, d=0, sigmaColor=75, sigmaSpace=15) self.site_centre = ((x + 0.5 * w), (y + 0.5 * h)) cv.imwrite('image/site.jpg', cut_bila) siteName = self.baidu_api.picture2text('image/site.jpg') self.editSiteName.setText(siteName) self.editSitePos.setText(str(self.site_centre)) self.viewer._ocr = True self.viewer.toggleDragMode() self.x_clicked = 0 self.y_clicked = 0 self.x_released = 0 self.y_released = 0 # 存储地点信息 def save_site(self): site_year = int(self.editYearInfo.text()) site_name = self.editSiteName.text() site_contour = self.site_ContourName.text() site_slpoe = float(self.siteSlope) site_lenth = float(self.siteLenth) site_centre = str(self.site_centre) mysqlConn = MySqlConn() mysqlConn.site_insert(site_year, site_name, site_contour, site_slpoe, site_lenth, site_centre)
def __init__(self, parent=None): """ Initialize the Planetary System LRGB Aligner environment. :param parent: None """ # The (generated) QtGui class is contained in module main_gui.py. QtWidgets.QWidget.__init__(self, parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) # Insert the photo viewer into the main GUI. self.ImageWindow = PhotoViewer(self) self.ImageWindow.setObjectName("ImageWindow") self.ui.verticalLayout_3.insertWidget(1, self.ImageWindow, stretch=1) # Connect main GUI events with method invocations. self.ui.buttonLoadBW.clicked.connect(self.load_bw_image) self.ui.buttonLoadColor.clicked.connect(self.load_color_image) self.ui.buttonRegistration.clicked.connect(self.compute_registration) self.ui.buttonComputeLRGB.clicked.connect(self.compute_lrgb) self.ui.buttonSetConfigParams.clicked.connect(self.edit_configuration) self.ui.buttonSaveRegisteredColorImage.clicked.connect( self.save_registered_image) self.ui.buttonSaveLRGB.clicked.connect(self.save_lrgb_image) self.ui.buttonExit.clicked.connect(self.closeEvent) self.ui.radioShowBW.clicked.connect( lambda: self.show_pixmap(pixmap_index=0)) self.ui.radioShowColorOrig.clicked.connect( lambda: self.show_pixmap(pixmap_index=1)) self.ui.radioShowColorRigidTransform.clicked.connect( lambda: self.show_pixmap(pixmap_index=2)) self.ui.radioShowMatches.clicked.connect( lambda: self.show_pixmap(pixmap_index=3)) self.ui.radioShowColorOptFlow.clicked.connect( lambda: self.show_pixmap(pixmap_index=4)) self.ui.radioShowLRGB.clicked.connect( lambda: self.show_pixmap(pixmap_index=5)) # Initialize the path to the home directory. self.current_dir = str(Path.home()) # Initialize instance variables. self.image_reference = None self.image_reference_8bit_gray = None self.image_target = None self.image_target_8bit_gray = None self.image_dewarped = None self.image_lrgb = None self.pixmaps = [None, None, None, None, None, None] self.current_pixmap_index = None # Initialize status variables self.status_list = [ False, False, False, False, False, False, False, False ] self.status_pointer = { "initialized": 0, "bw_loaded": 1, "color_loaded": 2, "rigid_transformed": 3, "optical_flow_computed": 4, "lrgb_computed": 5, "results_saved": 6 } self.radio_buttons = [ self.ui.radioShowBW, # 0 self.ui.radioShowColorOrig, # 1 self.ui.radioShowColorRigidTransform, # 2 self.ui.radioShowMatches, # 3 self.ui.radioShowColorOptFlow, # 4 self.ui.radioShowLRGB ] # 5 self.control_buttons = [ self.ui.buttonSetConfigParams, # 0 self.ui.buttonLoadBW, # 1 self.ui.buttonLoadColor, # 2 self.ui.buttonRegistration, # 3 self.ui.buttonSaveRegisteredColorImage, # 4 self.ui.buttonComputeLRGB, # 5 self.ui.buttonSaveLRGB, # 6 self.ui.buttonExit ] # 7 self.max_button = [0, 1, 2, 4, 5, 6, 6] self.max_control_button = [2, 3, 4, 4, 6, 7, 8] self.status_busy = False # Create configuration object and set configuration parameters to standard values. self.configuration = Configuration() # Write the program version into the window title. self.setWindowTitle(self.configuration.version) # Start the workflow thread. It controls the computations and control of external devices. # By decoupling those activities from the main thread, the GUI is kept from freezing during # long-running activities. self.workflow = Workflow(self) sleep(self.configuration.wait_for_workflow_initialization) # The workflow thread sends signals during computations. Connect those signals with the # appropriate GUI activity. self.workflow.set_status_busy_signal.connect(self.set_busy) self.workflow.set_status_signal.connect(self.set_status) self.workflow.set_error_signal.connect(self.show_error_message) # Reset downstream status flags. self.set_status(0)
class LrgbAligner(QtWidgets.QMainWindow): """ This class is the main class of the "Planetary System LRGB Aligner" software. It implements the main GUI for the communication with the user. It creates the workflow thread which controls all program activities asynchronously. """ def __init__(self, parent=None): """ Initialize the Planetary System LRGB Aligner environment. :param parent: None """ # The (generated) QtGui class is contained in module main_gui.py. QtWidgets.QWidget.__init__(self, parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) # Insert the photo viewer into the main GUI. self.ImageWindow = PhotoViewer(self) self.ImageWindow.setObjectName("ImageWindow") self.ui.verticalLayout_3.insertWidget(1, self.ImageWindow, stretch=1) # Connect main GUI events with method invocations. self.ui.buttonLoadBW.clicked.connect(self.load_bw_image) self.ui.buttonLoadColor.clicked.connect(self.load_color_image) self.ui.buttonRegistration.clicked.connect(self.compute_registration) self.ui.buttonComputeLRGB.clicked.connect(self.compute_lrgb) self.ui.buttonSetConfigParams.clicked.connect(self.edit_configuration) self.ui.buttonSaveRegisteredColorImage.clicked.connect( self.save_registered_image) self.ui.buttonSaveLRGB.clicked.connect(self.save_lrgb_image) self.ui.buttonExit.clicked.connect(self.closeEvent) self.ui.radioShowBW.clicked.connect( lambda: self.show_pixmap(pixmap_index=0)) self.ui.radioShowColorOrig.clicked.connect( lambda: self.show_pixmap(pixmap_index=1)) self.ui.radioShowColorRigidTransform.clicked.connect( lambda: self.show_pixmap(pixmap_index=2)) self.ui.radioShowMatches.clicked.connect( lambda: self.show_pixmap(pixmap_index=3)) self.ui.radioShowColorOptFlow.clicked.connect( lambda: self.show_pixmap(pixmap_index=4)) self.ui.radioShowLRGB.clicked.connect( lambda: self.show_pixmap(pixmap_index=5)) # Initialize the path to the home directory. self.current_dir = str(Path.home()) # Initialize instance variables. self.image_reference = None self.image_reference_8bit_gray = None self.image_target = None self.image_target_8bit_gray = None self.image_dewarped = None self.image_lrgb = None self.pixmaps = [None, None, None, None, None, None] self.current_pixmap_index = None # Initialize status variables self.status_list = [ False, False, False, False, False, False, False, False ] self.status_pointer = { "initialized": 0, "bw_loaded": 1, "color_loaded": 2, "rigid_transformed": 3, "optical_flow_computed": 4, "lrgb_computed": 5, "results_saved": 6 } self.radio_buttons = [ self.ui.radioShowBW, # 0 self.ui.radioShowColorOrig, # 1 self.ui.radioShowColorRigidTransform, # 2 self.ui.radioShowMatches, # 3 self.ui.radioShowColorOptFlow, # 4 self.ui.radioShowLRGB ] # 5 self.control_buttons = [ self.ui.buttonSetConfigParams, # 0 self.ui.buttonLoadBW, # 1 self.ui.buttonLoadColor, # 2 self.ui.buttonRegistration, # 3 self.ui.buttonSaveRegisteredColorImage, # 4 self.ui.buttonComputeLRGB, # 5 self.ui.buttonSaveLRGB, # 6 self.ui.buttonExit ] # 7 self.max_button = [0, 1, 2, 4, 5, 6, 6] self.max_control_button = [2, 3, 4, 4, 6, 7, 8] self.status_busy = False # Create configuration object and set configuration parameters to standard values. self.configuration = Configuration() # Write the program version into the window title. self.setWindowTitle(self.configuration.version) # Start the workflow thread. It controls the computations and control of external devices. # By decoupling those activities from the main thread, the GUI is kept from freezing during # long-running activities. self.workflow = Workflow(self) sleep(self.configuration.wait_for_workflow_initialization) # The workflow thread sends signals during computations. Connect those signals with the # appropriate GUI activity. self.workflow.set_status_busy_signal.connect(self.set_busy) self.workflow.set_status_signal.connect(self.set_status) self.workflow.set_error_signal.connect(self.show_error_message) # Reset downstream status flags. self.set_status(0) def edit_configuration(self): """ This method is invoked with the "Set configuration parameters" GUI button. Open the configuration editor. If the configuration is changed, reset the workflow status to invalidate the computations done so far. :return: - """ editor = ConfigurationEditor(self.configuration) editor.exec_() if editor.configuration_changed: # If parameters have changed, a new alignment has to be computed. If both images are # available, set process status to 2. if self.current_status > 2: self.set_status(2) def load_bw_image(self): """ Load the B/W reference image from a file. Keep it together with a grayscale version. :return: - """ try: self.image_reference, self.image_reference_8bit_gray, self.image_reference_8bit_color = \ self.load_image("Load B/W reference image", 0, color=False) self.ui.radioShowBW.setChecked(True) # Set the index of the image viewer to the B/W image. The pixmap with this index will # be loaded into the viewer with the "set_status" method. self.current_pixmap_index = 0 self.set_status(1) except Exception as e: self.show_error_message( "Error in reading B/W image. Detailed message: " + str(e)) def load_color_image(self): """ Load the color target image from a file. Keep it together with a grayscale version. :return: - """ try: self.image_target, self.image_target_8bit_gray, self.image_target_8bit_color = \ self.load_image("Load color image to be registered", 1, color=True) self.ui.radioShowColorOrig.setChecked(True) self.current_pixmap_index = 1 self.set_status(2) except Exception as e: self.show_error_message( "Error in reading color image. Detailed message: " + str(e)) def load_image(self, message, pixmap_index, color=False): """ Read an image from a file. Convert it to color mode if optional parameter is set to True. :param pixmap_index: Index into the list of pixel maps used to show images in GUI. :param color: If True, convert image to color mode. If False, convert it to grayscale. :return: 3-tupel with numpy arrays with image data in three versions: - Original depth and color / grayscale - 8bit grayscale - 8bit color """ options = QtWidgets.QFileDialog.Options() filename = QtWidgets.QFileDialog.getOpenFileName( self, message, self.current_dir, "Images (*.tif *.tiff *.png *.jpg)", options=options) file_name = filename[0] if file_name == '': raise Exception("File dialog aborted") # Remember the current directory for next file dialog. self.current_dir = str(Path(file_name).parents[0]) if color: image_read = cv2.imread(file_name, cv2.IMREAD_UNCHANGED) if image_read.dtype == np.uint16: image_read_8bit_color = (image_read / 256).astype('uint8') else: image_read_8bit_color = image_read image_read_8bit_gray = cv2.cvtColor(image_read_8bit_color, cv2.COLOR_BGR2GRAY) else: image_read = cv2.imread(file_name, cv2.IMREAD_ANYDEPTH) # If color image, convert to grayscale. if len(image_read.shape) == 3: image_read = cv2.cv2.cvtColor(image_read, cv2.COLOR_BayerRG2GRAY) if image_read.dtype == np.uint16: image_read_8bit_gray = cv2.convertScaleAbs(image_read, alpha=(255.0 / 65535.0)) else: image_read_8bit_gray = image_read image_read_8bit_color = cv2.cvtColor(image_read_8bit_gray, cv2.COLOR_GRAY2BGR) # Convert image into QT pixmel map, store it in list and load it into GUI viewer. self.pixmaps[pixmap_index] = self.create_pixmap(image_read_8bit_color) self.ImageWindow.setPhoto(self.pixmaps[pixmap_index]) self.ImageWindow.fitInView() return image_read, image_read_8bit_gray, image_read_8bit_color def create_pixmap(self, cv_image): """ Transform an image in OpenCV color representation (BGR) into a QT pixmap :param cv_image: Image array :return: QT QPixmap object """ return QtGui.QPixmap( QtGui.QImage(cv_image, cv_image.shape[1], cv_image.shape[0], cv_image.shape[1] * 3, QtGui.QImage.Format_RGB888).rgbSwapped()) def show_pixmap(self, pixmap_index=None): """ Load a pixmap into the GUI image viewer. Adapt the view scale according to the relative sizes of the new and old pixmaps. :param pixmap_index: Index of the selected pixmap in the list. If not selected, the current index is taken. :return: - """ if pixmap_index is None: pixmap_index = self.current_pixmap_index if self.pixmaps[pixmap_index] is not None: self.current_pixmap_index = pixmap_index # Get the ratio of old pixmap and viewport sizes. factor_old = self.ImageWindow.fitInView(scale=False) # Load the new pixmap. self.ImageWindow.setPhoto(self.pixmaps[pixmap_index]) # Get the ratio of new pixmap and viewport sizes. factor_new = self.ImageWindow.fitInView(scale=False) if factor_old is not None: # Scale the view by the relative size factors. factor = factor_new / factor_old self.ImageWindow.scale(factor, factor) def compute_registration(self): """ If both B/W and color images are available, start the registration process. :return: - """ if self.image_reference is not None and self.image_target is not None: # Tell the workflow thread to compute a new alignment. self.workflow.compute_alignment_flag = True def compute_lrgb(self): """ If both B/W and color images are available, start the registration process. :return: - """ if self.image_reference is not None and self.image_dewarped is not None: # Tell the workflow thread to compute a new alignment. self.workflow.compute_lrgb_flag = True def save_registered_image(self): """ Open a file chooser dialog and save the de-warped image. :return: - """ try: self.save_image( "Select location to store the registered color image", self.image_dewarped) # Udpate the status line. self.set_status(6) except: pass def save_lrgb_image(self): """ Open a file chooser dialog and save the combined LRBG image. :return: - """ try: self.save_image("Select location to store the combined LRGB image", self.image_lrgb) # Udpate the status line. self.set_status(6) except: pass def save_image(self, message, image): """ Open a file chooser. If a valid file name is selected, store the image to that location. :param message: Text to be displayed in the file chooser window. :param image: Image file :return: - """ options = QtWidgets.QFileDialog.Options() filename = QtWidgets.QFileDialog.getSaveFileName( self, message, self.current_dir, "Images (*.tif *.tiff *.png *.jpg)", options=options) # Store image only if the chooser did not return with a cancel. file_name = filename[0] if file_name != "": my_file = Path(file_name) # Remember the current directory for next file dialog. self.current_dir = str(my_file.parents[0]) if my_file.is_file(): os.remove(str(my_file)) if my_file.suffix == '.tif' or my_file.suffix == '.tiff': cv2.imwrite(file_name, image) elif image.dtype == np.uint16: image_8bit = (image / 256).astype('uint8') cv2.imwrite(file_name, image_8bit) else: cv2.imwrite(file_name, image) else: raise Exception("File dialog aborted") def set_busy(self, busy): """ Set the "busy" status flag and update the status bar. :param busy: True, if the workflow thread is active in a computation. False, otherwise. :return: - """ if busy: for button in self.control_buttons[0:7]: button.setEnabled(False) else: self.set_status(self.current_status) self.status_busy = busy self.set_statusbar() def set_status(self, status): """ Enable radio buttons to show images in GUI and set the status bar according to the workflow status. :param status: Status variable :return: - """ # Store current status. self.current_status = status # Set the current status. self.status_list[status] = True # Reset all downstream status variables to False. self.status_list[status + 1:] = [False] * (len(self.status_list) - status - 1) if status != 6: # Enable radio buttons which can be used at this point: for button in self.radio_buttons[:self.max_button[status]]: button.setEnabled(True) # Disable the radio buttons for showing images which do not exist at this point. for button in self.radio_buttons[self.max_button[status]:]: button.setEnabled(False) if not self.status_busy: # Enable control buttons which can be used at this point: for button in self.control_buttons[:self. max_control_button[status]]: button.setEnabled(True) # Disable the control buttons which do not make sense at this point. for button in self.control_buttons[ self.max_control_button[status]:-1]: button.setEnabled(False) if self.configuration.skip_rigid_transformation: self.ui.radioShowColorRigidTransform.setEnabled(False) self.ui.radioShowMatches.setEnabled(False) if self.configuration.skip_optical_flow: self.ui.radioShowColorOptFlow.setEnabled(False) # Refresh the image viewer. if self.current_pixmap_index is not None: self.show_pixmap() # Update the status bar. self.set_statusbar() def set_statusbar(self): """ The status bar at the bottom of the main GUI summarizes various infos on the process status. Read out flags to decide which infos to present. The status information is concatenated into a single "status_text" which eventually is written into the main GUI status bar. :return: - """ status_text = "" # Tell if input images are loaded. if self.status_list[self.status_pointer["initialized"]]: status_text += "Process initialized" # Tell if input images are loaded. if self.status_list[self.status_pointer["bw_loaded"]]: if self.status_list[self.status_pointer["color_loaded"]]: status_text += ", B/W reference and color frames loaded" else: status_text += ", B/W reference frame loaded" # Tell if rigid transformation is done. if not self.configuration.skip_rigid_transformation: if self.status_list[self.status_pointer["rigid_transformed"]]: status_text += ", rigid transformation computed" # Tell if optical flow has been computed. if not self.configuration.skip_optical_flow: if self.status_list[self.status_pointer["optical_flow_computed"]]: status_text += ", images pixel-wise aligned" # Tell if the LRGB image is computed. if self.status_list[self.status_pointer["lrgb_computed"]]: status_text += ", LRGB image computed" # Tell if results are written to disk. if self.status_list[self.status_pointer["results_saved"]]: status_text += ", results written to disk" # Tell if the workflow thread is busy at this point. if self.status_busy: status_text += ", busy" # Write the complete message to the status bar. self.ui.statusbar.showMessage(status_text) def show_error_message(self, message): """ Show an error message. This method is invoked from the workflow thread via the "set_error_signal" signal. :param message: Error message to be displayed :return: - """ error_dialog = QtWidgets.QErrorMessage(self) error_dialog.setMinimumSize(400, 0) error_dialog.setWindowTitle(self.configuration.version) error_dialog.showMessage(message) def closeEvent(self, evnt): """ This event is triggered when the user closes the main window by clicking on the cross in the window corner. :param evnt: event object :return: - """ self.workflow.exiting = True sleep(4. * self.configuration.polling_interval) sys.exit(0)
def init_WINDOW(self): # 存储加载的图片和抽取的轮廓 self.image = array([]) self.cnt = array([]) self.color_dict = get_corlor_list() self.choosePoints = [] self.ocr_flag = False # 存储加载的图片和抽取的轮廓——第二个图片的信息 self.image_1 = array([]) self.cnt_1 = array([]) self.color_dict_1 = get_corlor_list() self.choosePoints_1 = [] self.ocr_flag_1 = False # 鼠标点击坐标和鼠标释放坐标 self.x_clicked = 0 self.y_clicked = 0 self.x_released = 0 self.y_released = 0 # 鼠标点击坐标和鼠标释放坐标——第二个图片的坐标 self.x_clicked_1 = 0 self.y_clicked_1 = 0 self.x_released_1 = 0 self.y_released_1 = 0 self.fnmae = '' # 加载的map self.baidu_api = BaiDuAPI() #第二个图片的信息 self.fnmae_1 = '' # 加载的map self.baidu_api_1 = BaiDuAPI() # 图片图元 self.viewer = PhotoViewer(self) self.viewer_1=PhotoViewer(self) # UI初始化 # 'Load image' button self.btnLoad = QPushButton(self) self.btnLoad.setText('图片加载') # self.btnLoad.setText('Img Load') self.btnLoad.clicked.connect(self.loadImage) # 'Load image' button ——第二个图片 self.btnLoad_1 = QPushButton(self) self.btnLoad_1.setText('图片加载') # self.btnLoad.setText('Img Load') self.btnLoad_1.clicked.connect(self.loadImage_1) self.editYearInfo = QLineEdit(self) self.editYearInfo.setPlaceholderText('时间') self.editYearInfo_1 = QLineEdit(self) self.editYearInfo_1.setPlaceholderText('时间') # Button to change from drag/pan to getting pixel info self.btnClickDrag = QPushButton(self) self.btnClickDrag.setText('点击/拖拽') self.btnClickDrag.setIcon(QIcon("image/dragIcon.png")) self.btnClickDrag.clicked.connect(self.clickDrag) # Button to change from drag/pan to getting pixel info self.btnClickDrag_1 = QPushButton(self) self.btnClickDrag_1.setText('点击/拖拽') self.btnClickDrag_1.setIcon(QIcon("image/dragIcon.png")) self.btnClickDrag_1.clicked.connect(self.clickDrag_1) # 点击处的信息 self.editClickInfo = QLineEdit(self) self.editClickInfo.setReadOnly(True) self.viewer.mouse_clicked.connect(self.photoClicked) # 自建的鼠标点击的传递信号连接photoClicked函数 # 点击处的信息——第二个图片 self.editClickInfo_1 = QLineEdit(self) self.editClickInfo_1.setReadOnly(True) self.viewer_1.mouse_clicked.connect(self.photoClicked_1) # 自建的鼠标点击的传递信号连接photoClicked函数 # 轮廓生成按钮,连接轮廓生成函数 self.btnContour = QPushButton(self) self.btnContour.setText('轮廓生成') self.btnContour.clicked.connect(self.getContour) # 轮廓生成按钮,连接轮廓生成函数——第二个图片 self.btnContour_1 = QPushButton(self) self.btnContour_1.setText('轮廓生成') self.btnContour_1.clicked.connect(self.getContour_1) # 轮廓拖动按钮,连接轮廓拖动函数 self.btnDrag = QPushButton(self) self.btnDrag.setText("轮廓拖拽") self.btnDrag.clicked.connect(self.dragOutline) self.viewer.mouse_released.connect(self.mouse_releasePoint) # 自建鼠标松开信号连接了两个函数 self.viewer.mouse_released.connect(self.contourDraged) # 轮廓拖动按钮,连接轮廓拖动函数——第二个图片 self.btnDrag_1 = QPushButton(self) self.btnDrag_1.setText("轮廓拖拽") self.btnDrag_1.clicked.connect(self.dragOutline) self.viewer_1.mouse_released.connect(self.mouse_releasePoint) # 自建鼠标松开信号连接了两个函数 self.viewer_1.mouse_released.connect(self.contourDraged) # 获取轮廓信息的按钮 self.btnContourInfo = QPushButton(self) self.btnContourInfo.setText("轮廓信息") # self.btnContourInfo.setText("Contour Info") self.btnContourInfo.clicked.connect(self.contour_info) # 获取轮廓信息的按钮——第二个图片 self.btnContourInfo_1 = QPushButton(self) self.btnContourInfo_1.setText("轮廓信息") # self.btnContourInfo.setText("Contour Info") self.btnContourInfo_1.clicked.connect(self.contour_info_1) # 显示轮廓的信息 self.editcontourName = QLineEdit(self) self.editcontourName.setPlaceholderText('轮廓名称') # self.editcontourName.setPlaceholderText('Contour Name') self.editContourArea = QLineEdit(self) self.editContourArea.setReadOnly(True) self.editContourArea.setPlaceholderText("轮廓面积") # self.editContourArea.setPlaceholderText("Area") self.editContourPerimeter = QLineEdit(self) self.editContourPerimeter.setReadOnly(True) self.editContourPerimeter.setPlaceholderText("轮廓周长") self.editContourCentre = QLineEdit(self) self.editContourCentre.setPlaceholderText("轮廓重心") self.editContourCentre.setReadOnly(True) # 显示轮廓的信息——第二个图片 self.editcontourName_1 = QLineEdit(self) self.editcontourName_1.setPlaceholderText('轮廓名称') # self.editcontourName.setPlaceholderText('Contour Name') self.editContourArea_1 = QLineEdit(self) self.editContourArea_1.setReadOnly(True) self.editContourArea_1.setPlaceholderText("轮廓面积") # self.editContourArea.setPlaceholderText("Area") self.editContourPerimeter_1 = QLineEdit(self) self.editContourPerimeter_1.setReadOnly(True) self.editContourPerimeter_1.setPlaceholderText("轮廓周长") self.editContourCentre_1 = QLineEdit(self) self.editContourCentre_1.setPlaceholderText("轮廓重心") self.editContourCentre_1.setReadOnly(True) self.setStyleSheet( "QPushButton{background-color: rgb(39, 118, 148)}" "QPushButton{color:white}" "QPushButton:hover{color:yellow}" "QPushButton:pressed{color:red;}" "QPushButton{border-radius:5px}" "QPushButton{border:2px groove gray}" "QPushButton{border-style: outset}" "QPushButton{padding:2px 4px}" "QLineEdit {border:2px groove gray}" "QLineEdit {border-radius:5px}" "QLineEdit{padding: 2px 4px}" ) # Arrange layout HBlayoutAll=QHBoxLayout(self) VBlayout = QVBoxLayout() VBlayout.addWidget(self.viewer) HBlayout = QHBoxLayout() HBlayout.setAlignment(Qt.AlignLeft) HBlayout.addWidget(self.btnLoad) HBlayout.addWidget(self.editYearInfo) HBlayout.addWidget(self.btnClickDrag) HBlayout.addWidget(self.editClickInfo) HBlayout.addWidget(self.btnContour) HBlayout.addWidget(self.btnDrag) # 第二行信息 HBlayoutSecond = QHBoxLayout() HBlayoutSecond.setAlignment(Qt.AlignLeft) HBlayoutSecond.addWidget(self.btnContourInfo) HBlayoutSecond.addWidget(self.editcontourName) HBlayoutSecond.addWidget(self.editContourArea) HBlayoutSecond.addWidget(self.editContourPerimeter) HBlayoutSecond.addWidget(self.editContourCentre) VBlayout.addLayout(HBlayout) VBlayout.addLayout(HBlayoutSecond) #右边图片的布局 VBlayout_1 = QVBoxLayout() VBlayout_1.addWidget(self.viewer_1) HBlayout_1 = QHBoxLayout() HBlayout_1.setAlignment(Qt.AlignLeft) HBlayout_1.addWidget(self.btnLoad_1) HBlayout_1.addWidget(self.editYearInfo_1) HBlayout_1.addWidget(self.btnClickDrag_1) HBlayout_1.addWidget(self.editClickInfo_1) HBlayout_1.addWidget(self.btnContour_1) HBlayout_1.addWidget(self.btnDrag_1) # 第二行信息 HBlayoutSecond_1 = QHBoxLayout() HBlayoutSecond_1.setAlignment(Qt.AlignLeft) HBlayoutSecond_1.addWidget(self.btnContourInfo_1) HBlayoutSecond_1.addWidget(self.editcontourName_1) HBlayoutSecond_1.addWidget(self.editContourArea_1) HBlayoutSecond_1.addWidget(self.editContourPerimeter_1) HBlayoutSecond_1.addWidget(self.editContourCentre_1) VBlayout_1.addLayout(HBlayout_1) VBlayout_1.addLayout(HBlayoutSecond_1) HBlayoutAll.addLayout(VBlayout) HBlayoutAll.addLayout(VBlayout_1) self.setGeometry(500, 300, 800, 600) self.setWindowTitle('历史时空') self.setWindowIcon(QIcon('windowIcon.png')) self.show()
class Window(QWidget): def __init__(self): super(Window, self).__init__() self.init_WINDOW() def init_WINDOW(self): # 存储加载的图片和抽取的轮廓 self.image = array([]) self.cnt = array([]) self.color_dict = get_corlor_list() self.choosePoints = [] self.ocr_flag = False # 存储加载的图片和抽取的轮廓——第二个图片的信息 self.image_1 = array([]) self.cnt_1 = array([]) self.color_dict_1 = get_corlor_list() self.choosePoints_1 = [] self.ocr_flag_1 = False # 鼠标点击坐标和鼠标释放坐标 self.x_clicked = 0 self.y_clicked = 0 self.x_released = 0 self.y_released = 0 # 鼠标点击坐标和鼠标释放坐标——第二个图片的坐标 self.x_clicked_1 = 0 self.y_clicked_1 = 0 self.x_released_1 = 0 self.y_released_1 = 0 self.fnmae = '' # 加载的map self.baidu_api = BaiDuAPI() #第二个图片的信息 self.fnmae_1 = '' # 加载的map self.baidu_api_1 = BaiDuAPI() # 图片图元 self.viewer = PhotoViewer(self) self.viewer_1=PhotoViewer(self) # UI初始化 # 'Load image' button self.btnLoad = QPushButton(self) self.btnLoad.setText('图片加载') # self.btnLoad.setText('Img Load') self.btnLoad.clicked.connect(self.loadImage) # 'Load image' button ——第二个图片 self.btnLoad_1 = QPushButton(self) self.btnLoad_1.setText('图片加载') # self.btnLoad.setText('Img Load') self.btnLoad_1.clicked.connect(self.loadImage_1) self.editYearInfo = QLineEdit(self) self.editYearInfo.setPlaceholderText('时间') self.editYearInfo_1 = QLineEdit(self) self.editYearInfo_1.setPlaceholderText('时间') # Button to change from drag/pan to getting pixel info self.btnClickDrag = QPushButton(self) self.btnClickDrag.setText('点击/拖拽') self.btnClickDrag.setIcon(QIcon("image/dragIcon.png")) self.btnClickDrag.clicked.connect(self.clickDrag) # Button to change from drag/pan to getting pixel info self.btnClickDrag_1 = QPushButton(self) self.btnClickDrag_1.setText('点击/拖拽') self.btnClickDrag_1.setIcon(QIcon("image/dragIcon.png")) self.btnClickDrag_1.clicked.connect(self.clickDrag_1) # 点击处的信息 self.editClickInfo = QLineEdit(self) self.editClickInfo.setReadOnly(True) self.viewer.mouse_clicked.connect(self.photoClicked) # 自建的鼠标点击的传递信号连接photoClicked函数 # 点击处的信息——第二个图片 self.editClickInfo_1 = QLineEdit(self) self.editClickInfo_1.setReadOnly(True) self.viewer_1.mouse_clicked.connect(self.photoClicked_1) # 自建的鼠标点击的传递信号连接photoClicked函数 # 轮廓生成按钮,连接轮廓生成函数 self.btnContour = QPushButton(self) self.btnContour.setText('轮廓生成') self.btnContour.clicked.connect(self.getContour) # 轮廓生成按钮,连接轮廓生成函数——第二个图片 self.btnContour_1 = QPushButton(self) self.btnContour_1.setText('轮廓生成') self.btnContour_1.clicked.connect(self.getContour_1) # 轮廓拖动按钮,连接轮廓拖动函数 self.btnDrag = QPushButton(self) self.btnDrag.setText("轮廓拖拽") self.btnDrag.clicked.connect(self.dragOutline) self.viewer.mouse_released.connect(self.mouse_releasePoint) # 自建鼠标松开信号连接了两个函数 self.viewer.mouse_released.connect(self.contourDraged) # 轮廓拖动按钮,连接轮廓拖动函数——第二个图片 self.btnDrag_1 = QPushButton(self) self.btnDrag_1.setText("轮廓拖拽") self.btnDrag_1.clicked.connect(self.dragOutline) self.viewer_1.mouse_released.connect(self.mouse_releasePoint) # 自建鼠标松开信号连接了两个函数 self.viewer_1.mouse_released.connect(self.contourDraged) # 获取轮廓信息的按钮 self.btnContourInfo = QPushButton(self) self.btnContourInfo.setText("轮廓信息") # self.btnContourInfo.setText("Contour Info") self.btnContourInfo.clicked.connect(self.contour_info) # 获取轮廓信息的按钮——第二个图片 self.btnContourInfo_1 = QPushButton(self) self.btnContourInfo_1.setText("轮廓信息") # self.btnContourInfo.setText("Contour Info") self.btnContourInfo_1.clicked.connect(self.contour_info_1) # 显示轮廓的信息 self.editcontourName = QLineEdit(self) self.editcontourName.setPlaceholderText('轮廓名称') # self.editcontourName.setPlaceholderText('Contour Name') self.editContourArea = QLineEdit(self) self.editContourArea.setReadOnly(True) self.editContourArea.setPlaceholderText("轮廓面积") # self.editContourArea.setPlaceholderText("Area") self.editContourPerimeter = QLineEdit(self) self.editContourPerimeter.setReadOnly(True) self.editContourPerimeter.setPlaceholderText("轮廓周长") self.editContourCentre = QLineEdit(self) self.editContourCentre.setPlaceholderText("轮廓重心") self.editContourCentre.setReadOnly(True) # 显示轮廓的信息——第二个图片 self.editcontourName_1 = QLineEdit(self) self.editcontourName_1.setPlaceholderText('轮廓名称') # self.editcontourName.setPlaceholderText('Contour Name') self.editContourArea_1 = QLineEdit(self) self.editContourArea_1.setReadOnly(True) self.editContourArea_1.setPlaceholderText("轮廓面积") # self.editContourArea.setPlaceholderText("Area") self.editContourPerimeter_1 = QLineEdit(self) self.editContourPerimeter_1.setReadOnly(True) self.editContourPerimeter_1.setPlaceholderText("轮廓周长") self.editContourCentre_1 = QLineEdit(self) self.editContourCentre_1.setPlaceholderText("轮廓重心") self.editContourCentre_1.setReadOnly(True) self.setStyleSheet( "QPushButton{background-color: rgb(39, 118, 148)}" "QPushButton{color:white}" "QPushButton:hover{color:yellow}" "QPushButton:pressed{color:red;}" "QPushButton{border-radius:5px}" "QPushButton{border:2px groove gray}" "QPushButton{border-style: outset}" "QPushButton{padding:2px 4px}" "QLineEdit {border:2px groove gray}" "QLineEdit {border-radius:5px}" "QLineEdit{padding: 2px 4px}" ) # Arrange layout HBlayoutAll=QHBoxLayout(self) VBlayout = QVBoxLayout() VBlayout.addWidget(self.viewer) HBlayout = QHBoxLayout() HBlayout.setAlignment(Qt.AlignLeft) HBlayout.addWidget(self.btnLoad) HBlayout.addWidget(self.editYearInfo) HBlayout.addWidget(self.btnClickDrag) HBlayout.addWidget(self.editClickInfo) HBlayout.addWidget(self.btnContour) HBlayout.addWidget(self.btnDrag) # 第二行信息 HBlayoutSecond = QHBoxLayout() HBlayoutSecond.setAlignment(Qt.AlignLeft) HBlayoutSecond.addWidget(self.btnContourInfo) HBlayoutSecond.addWidget(self.editcontourName) HBlayoutSecond.addWidget(self.editContourArea) HBlayoutSecond.addWidget(self.editContourPerimeter) HBlayoutSecond.addWidget(self.editContourCentre) VBlayout.addLayout(HBlayout) VBlayout.addLayout(HBlayoutSecond) #右边图片的布局 VBlayout_1 = QVBoxLayout() VBlayout_1.addWidget(self.viewer_1) HBlayout_1 = QHBoxLayout() HBlayout_1.setAlignment(Qt.AlignLeft) HBlayout_1.addWidget(self.btnLoad_1) HBlayout_1.addWidget(self.editYearInfo_1) HBlayout_1.addWidget(self.btnClickDrag_1) HBlayout_1.addWidget(self.editClickInfo_1) HBlayout_1.addWidget(self.btnContour_1) HBlayout_1.addWidget(self.btnDrag_1) # 第二行信息 HBlayoutSecond_1 = QHBoxLayout() HBlayoutSecond_1.setAlignment(Qt.AlignLeft) HBlayoutSecond_1.addWidget(self.btnContourInfo_1) HBlayoutSecond_1.addWidget(self.editcontourName_1) HBlayoutSecond_1.addWidget(self.editContourArea_1) HBlayoutSecond_1.addWidget(self.editContourPerimeter_1) HBlayoutSecond_1.addWidget(self.editContourCentre_1) VBlayout_1.addLayout(HBlayout_1) VBlayout_1.addLayout(HBlayoutSecond_1) HBlayoutAll.addLayout(VBlayout) HBlayoutAll.addLayout(VBlayout_1) self.setGeometry(500, 300, 800, 600) self.setWindowTitle('历史时空') self.setWindowIcon(QIcon('windowIcon.png')) self.show() #图片加载 def loadImage(self): fname, _ = QFileDialog.getOpenFileName(self, "选择地图", 'D:\\0Kind of File\\Map\\中国历史地图第三版', 'Image files(*.jpg *.gif *.png)') # fi = QtCott # re.QFileInfo(fname) 获取fname的信息 if not fname: pass else: # 从新加载图片,清空变量 # 存储加载的图片和抽取的轮廓 self.image = array([]) self.cnt = array([]) # 鼠标点击坐标和鼠标释放坐标 self.x_clicked = 0 self.y_clicked = 0 self.x_released = 0 self.y_released = 0 self.image = cv.imdecode(fromfile(fname, dtype=uint8), -1) year_img = self.image[0:100, 0:500] cut_bila = cv.bilateralFilter(year_img, d=0, sigmaColor=75, sigmaSpace=15) cv.imwrite('image/year.jpg', cut_bila) year_str = self.baidu_api.picture2text('image/year.jpg') year_int = findall(r'\d+', year_str) if '前' in year_str: map_year = -int(year_int[0]) else: map_year = int(year_int[0]) myimage = open(fname, 'rb') map_img = myimage.read() #mysqlConn = MySqlConn() self.editYearInfo.setText(str(map_year)) # print(len(map_img)) #mysqlConn.img_insert(map_year=map_year, map_img=map_img) image_height, image_width, image_depth = self.image.shape QIm = cv.cvtColor(self.image, cv.COLOR_BGR2RGB) QIm = QImage(QIm.data, image_width, image_height, image_width * image_depth, QImage.Format_RGB888) self.viewer.setPhoto(QPixmap.fromImage(QIm)) self.viewer.fitInView() # 图片加载——第二个图片 def loadImage_1(self): fname, _ = QFileDialog.getOpenFileName(self, "选择地图", 'D:\\0Kind of File\\Map\\中国历史地图第三版', 'Image files(*.jpg *.gif *.png)') # fi = QtCott # re.QFileInfo(fname) 获取fname的信息 if not fname: pass else: # 从新加载图片,清空变量 # 存储加载的图片和抽取的轮廓 self.image_1 = array([]) self.cnt_1 = array([]) # 鼠标点击坐标和鼠标释放坐标 self.x_clicked_1 = 0 self.y_clicked_1 = 0 self.x_released_1 = 0 self.y_released_1 = 0 self.image_1 = cv.imdecode(fromfile(fname, dtype=uint8), -1) year_img = self.image_1[0:100, 0:500] cut_bila = cv.bilateralFilter(year_img, d=0, sigmaColor=75, sigmaSpace=15) cv.imwrite('image/year.jpg', cut_bila) year_str = self.baidu_api.picture2text('image/year.jpg') year_int = findall(r'\d+', year_str) if '前' in year_str: map_year = -int(year_int[0]) else: map_year = int(year_int[0]) myimage = open(fname, 'rb') map_img = myimage.read() #mysqlConn = MySqlConn() self.editYearInfo_1.setText(str(map_year)) # print(len(map_img)) #mysqlConn.img_insert(map_year=map_year, map_img=map_img) image_height, image_width, image_depth = self.image_1.shape QIm = cv.cvtColor(self.image_1, cv.COLOR_BGR2RGB) QIm = QImage(QIm.data, image_width, image_height, image_width * image_depth, QImage.Format_RGB888) self.viewer_1.setPhoto(QPixmap.fromImage(QIm)) self.viewer_1.fitInView() # 鼠标切换点击和拖拽模式 def clickDrag(self): if self.viewer._contour or self.viewer._ocr: pass else: if self.viewer.dragMode(): self.btnClickDrag.setIcon(QIcon("image/clickIcon.png")) else: self.btnClickDrag.setIcon(QIcon("image/dragIcon.png")) self.viewer.toggleDragMode() # 鼠标切换点击和拖拽模式-第二个图片 def clickDrag_1(self): if self.viewer_1._contour or self.viewer_1._ocr: pass else: if self.viewer_1.dragMode(): self.btnClickDrag_1.setIcon(QIcon("image/clickIcon.png")) else: self.btnClickDrag_1.setIcon(QIcon("image/dragIcon.png")) self.viewer_1.toggleDragMode() # 传递鼠标点击的坐标 def photoClicked(self, pos): if self.viewer.dragMode() == QGraphicsView.NoDrag: self.editClickInfo.setText('%d, %d' % (pos.x(), pos.y())) self.x_clicked = pos.x() self.y_clicked = pos.y() # 传递鼠标点击的坐标-第二个图片 def photoClicked_1(self, pos): if self.viewer_1.dragMode() == QGraphicsView.NoDrag: self.editClickInfo_1.setText('%d, %d' % (pos.x(), pos.y())) self.x_clicked_1 = pos.x() self.y_clicked_1 = pos.y() # 获取轮廓 def getContour(self): # 获取鼠标点击处的hsv值 hsv = cv.cvtColor(self.image, cv.COLOR_BGR2HSV) # 使用区域的hsv均值代替某一点的hsv的值 area_hsv = [0, 0, 0] for i in range(-3, 6, 3): for j in range(-3, 6, 3): area_hsv = area_hsv + hsv[self.y_clicked - i, self.x_clicked - j] color_hsv = area_hsv // 9 # 判断需要提取的hsv区间 color = False for key, value in self.color_dict.items(): if color_hsv[0] >= value[0][0] and color_hsv[0] <= value[1][0] and color_hsv[1] >= value[0][1] and color_hsv[1] <= value[1][1] and color_hsv[2] >= value[0][2] and color_hsv[2] <= value[1][2]: color = True lower_HSV = value[0] upper_HSV = value[1] self.color_dict.pop(key) self.color_dict[key] = value break if not color: print("请重新选区颜色") else: # 中值滤波去除椒盐噪声 blurImg = cv.medianBlur(self.image, 21) img = cv.cvtColor(blurImg, cv.COLOR_BGR2HSV) # 提取对应的hsv区域 mask = cv.inRange(img, lower_HSV, upper_HSV) choose_color = cv.bitwise_and(img, img, mask=mask) result = cv.cvtColor(choose_color, cv.COLOR_HSV2BGR) gray = cv.cvtColor(result, cv.COLOR_BGR2GRAY) # 图象灰度化 # 提取图象梯度(可改进) gradX = cv.Sobel(gray, ddepth=cv.CV_32F, dx=1, dy=0, ksize=-1) gradY = cv.Sobel(gray, ddepth=cv.CV_32F, dx=0, dy=1, ksize=-1) # gradX = cv.Scharr(gray, cv.CV_64F, 1, 0) # gradY = cv.Scharr(gray, cv.CV_64F, 0, 1) # 保留水平和竖直梯度大的 gradient = cv.max(gradX, gradY) # cv.namedWindow("Img0", cv.WINDOW_KEEPRATIO) # cv.imshow("Img0", gradient) # cv.imwrite("img0.jpg", gradient) gradient = cv.convertScaleAbs(gradient) # 寻找轮廓 # 采用三角形法进行二值化 # hist = cv.calcHist([gradient],[0],None,[256],[0,256]) # plt.plot(hist) # plt.show() ret, th = cv.threshold(gradient, 0, 255, cv.THRESH_BINARY | cv.THRESH_TRIANGLE) # cv.namedWindow("Img", cv.WINDOW_KEEPRATIO) # cv.imshow("Img", th) # cv.imwrite("img.jpg", th) kernel = cv.getStructuringElement(cv.MORPH_ELLIPSE, (5, 5)) # 闭运算,先腐蚀后膨胀,去除黑色小点 closed = cv.morphologyEx(th, cv.MORPH_CLOSE, kernel, iterations=6) # 轮廓生成 # cv.namedWindow("Img1", cv.WINDOW_KEEPRATIO) # cv.imshow("Img1", closed) # cv.imwrite("img1.jpg", closed) # OpenCV3 的findContours有三个返回值,OpenCV 4的有四个返回值 # 应该安装opencv-python 3.*版本。 myImage, cnts, hierarchy = cv.findContours(closed, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_NONE) num = 0 con_exit = False for cnt in cnts: mesure = cv.pointPolygonTest(cnt, (self.x_clicked, self.y_clicked), measureDist=False) if mesure == 1: con_exit = True break num = num + 1 if con_exit: self.cnt = cnts[num] # 确定轮廓 back = ones(self.image.shape, uint8) * 255 img_contour = cv.drawContours(self.image.copy(), cnts, num, (255, 255, 255), 3) # img_contour1 = cv.drawContours(back, cnts, num, (0, 0, 255), 3) # 重新渲染 # cv.namedWindow("Img2", cv.WINDOW_KEEPRATIO) # cv.imshow("Img2", img_contour1) # cv.imwrite("img2.jpg", img_contour1) image_height, image_width, image_depth = img_contour.shape QIm = cv.cvtColor(img_contour, cv.COLOR_BGR2RGB) QIm = QImage(QIm.data, image_width, image_height, image_width * image_depth, QImage.Format_RGB888) self.viewer.setPhoto(QPixmap.fromImage(QIm)) # 重新渲染后,图元回到拖拽模式 self.btnClickDrag.setIcon(QIcon("image/dragIcon.png")) else: print("重新选区颜色点") # 获取轮廓-第二个图片 def getContour_1(self): # 获取鼠标点击处的hsv值 hsv = cv.cvtColor(self.image_1, cv.COLOR_BGR2HSV) # 使用区域的hsv均值代替某一点的hsv的值 area_hsv = [0, 0, 0] for i in range(-3, 6, 3): for j in range(-3, 6, 3): area_hsv = area_hsv + hsv[self.y_clicked_1 - i, self.x_clicked_1 - j] color_hsv = area_hsv // 9 # 判断需要提取的hsv区间 color = False for key, value in self.color_dict.items(): if color_hsv[0] >= value[0][0] and color_hsv[0] <= value[1][0] and color_hsv[1] >= value[0][1] \ and color_hsv[1] <= value[1][1] and color_hsv[2] >= value[0][2] and color_hsv[2] <= value[1][2]: print("11111111111111") color = True lower_HSV = value[0] upper_HSV = value[1] self.color_dict.pop(key) self.color_dict[key] = value break print(color) if not color: print("请重新选区颜色") else: # 中值滤波去除椒盐噪声 blurImg = cv.medianBlur(self.image_1, 21) img = cv.cvtColor(blurImg, cv.COLOR_BGR2HSV) # 提取对应的hsv区域 mask = cv.inRange(img, lower_HSV, upper_HSV) choose_color = cv.bitwise_and(img, img, mask=mask) result = cv.cvtColor(choose_color, cv.COLOR_HSV2BGR) gray = cv.cvtColor(result, cv.COLOR_BGR2GRAY) # 图象灰度化 # 提取图象梯度(可改进) gradX = cv.Sobel(gray, ddepth=cv.CV_32F, dx=1, dy=0, ksize=-1) gradY = cv.Sobel(gray, ddepth=cv.CV_32F, dx=0, dy=1, ksize=-1) # gradX = cv.Scharr(gray, cv.CV_64F, 1, 0) # gradY = cv.Scharr(gray, cv.CV_64F, 0, 1) # 保留水平和竖直梯度大的 gradient = cv.max(gradX, gradY) # cv.namedWindow("Img0", cv.WINDOW_KEEPRATIO) # cv.imshow("Img0", gradient) # cv.imwrite("img0.jpg", gradient) gradient = cv.convertScaleAbs(gradient) # 寻找轮廓 # 采用三角形法进行二值化 # hist = cv.calcHist([gradient],[0],None,[256],[0,256]) # plt.plot(hist) # plt.show() ret, th = cv.threshold(gradient, 0, 255, cv.THRESH_BINARY | cv.THRESH_TRIANGLE) # cv.namedWindow("Img", cv.WINDOW_KEEPRATIO) # cv.imshow("Img", th) # cv.imwrite("img.jpg", th) kernel = cv.getStructuringElement(cv.MORPH_ELLIPSE, (5, 5)) # 闭运算,先腐蚀后膨胀,去除黑色小点 closed = cv.morphologyEx(th, cv.MORPH_CLOSE, kernel, iterations=6) # 轮廓生成 # cv.namedWindow("Img1", cv.WINDOW_KEEPRATIO) # cv.imshow("Img1", closed) # cv.imwrite("img1.jpg", closed) # OpenCV3 的findContours有三个返回值,OpenCV 4的有四个返回值 # 应该安装opencv-python 3.*版本。 myImage, cnts, hierarchy = cv.findContours(closed, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_NONE) num = 0 con_exit = False for cnt in cnts: mesure = cv.pointPolygonTest(cnt, (self.x_clicked_1, self.y_clicked_1), measureDist=False) if mesure == 1: con_exit = True break num = num + 1 if con_exit: self.cnt_1 = cnts[num] # 确定轮廓 back = ones(self.image_1.shape, uint8) * 255 img_contour = cv.drawContours(self.image_1.copy(), cnts, num, (255, 255, 255), 3) # img_contour1 = cv.drawContours(back, cnts, num, (0, 0, 255), 3) # 重新渲染 # cv.namedWindow("Img2", cv.WINDOW_KEEPRATIO) # cv.imshow("Img2", img_contour1) # cv.imwrite("img2.jpg", img_contour1) image_height, image_width, image_depth = img_contour.shape QIm = cv.cvtColor(img_contour, cv.COLOR_BGR2RGB) QIm = QImage(QIm.data, image_width, image_height, image_width * image_depth, QImage.Format_RGB888) self.viewer_1.setPhoto(QPixmap.fromImage(QIm)) # 重新渲染后,图元回到拖拽模式 self.btnClickDrag_1.setIcon(QIcon("image/dragIcon.png")) else: print("重新选区颜色点") # 轮廓拖动模式切换 def dragOutline(self): if not self.viewer._contour: self.viewer._contour = True self.viewer.toggleDragMode() self.btnClickDrag.setIcon(QIcon("image/clickIcon.png")) self.btnDrag.setStyleSheet("QPushButton{color:red}") else: self.viewer._contour = False self.btnDrag.setStyleSheet("QPushButton{color:white}") # 获取鼠标松开的位置坐标 def mouse_releasePoint(self, pos): self.x_released = pos.x() self.y_released = pos.y() # 轮廓拖动函数 def contourDraged(self, pos): if self.viewer.dragMode() == QGraphicsView.NoDrag and self.viewer._contour == True: if self.cnt.any(): for p in self.cnt: if ((p[0][0] - self.x_clicked) ** 2 + (p[0][1] - self.y_clicked) ** 2) <= 25: p[0][0] = p[0][0] + (pos.x() - self.x_clicked) * 0.9 p[0][1] = p[0][1] + (pos.y() - self.y_clicked) * 0.9 elif ((p[0][0] - self.x_clicked) ** 2 + (p[0][1] - self.y_clicked) ** 2) <= 100: p[0][0] = p[0][0] + (pos.x() - self.x_clicked) * 0.7 p[0][1] = p[0][1] + (pos.y() - self.y_clicked) * 0.7 elif ((p[0][0] - self.x_clicked) ** 2 + (p[0][1] - self.y_clicked) ** 2) <= 225: p[0][0] = p[0][0] + (pos.x() - self.x_clicked) * 0.5 p[0][1] = p[0][1] + (pos.y() - self.y_clicked) * 0.5 elif ((p[0][0] - self.x_clicked) ** 2 + (p[0][1] - self.y_clicked) ** 2) <= 400: p[0][0] = p[0][0] + (pos.x() - self.x_clicked) * 0.3 p[0][1] = p[0][1] + (pos.y() - self.y_clicked) * 0.3 elif ((p[0][0] - self.x_clicked) ** 2 + (p[0][1] - self.y_clicked) ** 2) <= 625: p[0][0] = p[0][0] + (pos.x() - self.x_clicked) * 0.1 p[0][1] = p[0][1] + (pos.y() - self.y_clicked) * 0.1 # 重新渲染图元 img_contour = cv.drawContours(self.image.copy(), self.cnt, -1, (255, 255, 255), 3) image_height, image_width, image_depth = img_contour.shape QIm = cv.cvtColor(img_contour, cv.COLOR_BGR2RGB) QIm = QImage(QIm.data, image_width, image_height, image_width * image_depth, QImage.Format_RGB888) self.viewer.setPhoto(QPixmap.fromImage(QIm)) self.viewer.curve_mode = True self.viewer.toggleDragMode() # 判断是否还处于拖拽模式 # 获取轮廓的信息 def contour_info(self): if self.cnt.any(): ROI = zeros(self.image.shape[:2], uint8) proimage = self.image.copy() roi = cv.drawContours(ROI, [self.cnt], 0, (255, 255, 255), -1) x, y, w, h = cv.boundingRect(self.cnt) imgroi = cv.bitwise_and(proimage, proimage, mask=roi) site = imgroi[y:y + h, x:x + w] median_img = cv.medianBlur(site, 9) img_gray = cv.cvtColor(median_img, cv.COLOR_BGR2GRAY) th = cv.adaptiveThreshold(img_gray, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY_INV, 65, 30) g = cv.getStructuringElement(cv.MORPH_RECT, (2, 1)) # 形态学处理,开运算 ersion = cv.erode(th, g, iterations=4) cv.imwrite('image/contour_name.jpg', ersion) contour_name = self.baidu_api.picture2text('image/contour_name.jpg') self.editcontourName.setText(contour_name) contour_area = cv.contourArea(self.cnt) contour_perimeter = cv.arcLength(self.cnt, True) M = cv.moments(self.cnt) self.contour_centre = str(int(M['m10'] / M['m00'])) + ',' + str(int(M['m01'] / M['m00'])) self.editContourArea.setText("%d" % (contour_area)) self.editContourPerimeter.setText("%d" % (contour_perimeter)) self.editContourCentre.setText("%d,%d" % (int(M['m10'] / M['m00']), int(M['m01'] / M['m00']))) # 获取轮廓的信息 def contour_info_1(self): if self.cnt_1.any(): ROI = zeros(self.image_1.shape[:2], uint8) proimage = self.image_1.copy() roi = cv.drawContours(ROI, [self.cnt_1], 0, (255, 255, 255), -1) x, y, w, h = cv.boundingRect(self.cnt_1) imgroi = cv.bitwise_and(proimage, proimage, mask=roi) site = imgroi[y:y + h, x:x + w] median_img = cv.medianBlur(site, 9) img_gray = cv.cvtColor(median_img, cv.COLOR_BGR2GRAY) th = cv.adaptiveThreshold(img_gray, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY_INV, 65, 30) g = cv.getStructuringElement(cv.MORPH_RECT, (2, 1)) # 形态学处理,开运算 ersion = cv.erode(th, g, iterations=4) cv.imwrite('image/contour_name.jpg', ersion) contour_name = self.baidu_api.picture2text('image/contour_name.jpg') self.editcontourName_1.setText(contour_name) contour_area = cv.contourArea(self.cnt_1) contour_perimeter = cv.arcLength(self.cnt_1, True) M = cv.moments(self.cnt_1) self.contour_centre_1 = str(int(M['m10'] / M['m00'])) + ',' + str(int(M['m01'] / M['m00'])) self.editContourArea_1.setText("%d" % (contour_area)) self.editContourPerimeter_1.setText("%d" % (contour_perimeter)) self.editContourCentre_1.setText("%d,%d" % (int(M['m10'] / M['m00']), int(M['m01'] / M['m00']))) def save_rele(self): contour_year = int(self.editYearInfo.text()) contour_name = contour_year+self.editcontourName.text() contour_year_1 = int(self.editYearInfo_1.text()) contour_name_1 = contour_year_1+self.editcontourName_1.text() mysqlConn = MySqlConn() mysqlConn.rele_insert(contour_year,contour_name,contour_name_1,"") mysqlConn.rele_insert(contour_year_1,contour_name_1,"",contour_name)
class BPA_App(QtGui.QMainWindow, main_window.Ui_MainWindow): def __init__(self, parent=None): super(BPA_App, self).__init__(parent) self.setupUi(self) self.setWindowTitle("Automated Blood Stain Pattern Analysis - ABPA") self.viewer = PhotoViewer(self.centralwidget) self.viewer.setMinimumSize(500, 500) self.horizontalLayout.addWidget(self.viewer) self.actionLoad.triggered.connect(self.load_image) self.actionExport.triggered.connect(self.export) self.actionSegment_Image.triggered.connect(self.show_metrics) self.actionBatch_process.triggered.connect(self.show_batch_dialog) self.file_name = "" self.folder_name = '' self.progressBar.hide() self.pixmap = None self.result = None self.annotations = {} self.pattern_metrics = True def load_image(self): self.file_name = QtGui.QFileDialog.getOpenFileName( self, 'Open file', 'c:\\', "Image files (*.jpg *.gif *.png *.tif)") if self.file_name: self.viewer.setPhoto(pixmap=QtGui.QPixmap(self.file_name)) self.setWindowTitle("ABPA - " + self.file_name) def export(self): save_path = QtGui.QFileDialog.getSaveFileName(self, 'Open file', 'c:\\') if save_path: save_path = os.path.splitext(save_path)[0] self.progressBar.show() self.progressBar.setValue(0) Seg.export_stain_data(save_path, self.progressBar) Seg.pattern.export(save_path, self.pattern_metrics) self.progressBar.setValue(100) cv2.cvtColor(self.result, cv2.COLOR_BGR2RGB, self.result) cv2.drawContours(self.result, Seg.pattern.contours, -1, (255, 0, 255), 3) for stain in Seg.pattern.stains: stain.annotate(self.result, self.annotations) cv2.imwrite(save_path + "-result.jpg", self.result) self.progressBar.hide() def show_metrics(self): Dialog = self.show_dialog(features_dialog.Ui_SegmenationMetrics(), self.segment_image) Dialog.exec_() def show_dialog(self, dialog, accept): Dialog = QtGui.QDialog() self.dialog = dialog self.dialog.setupUi(Dialog) self.dialog.scale_spin.setMinimum(1) self.dialog.scale_spin.setValue(Seg.pattern.scale) self.dialog.scale_spin.valueChanged.connect(self.update_scale) self.dialog.buttonBox.accepted.connect(accept) return Dialog def update_scale(self, value): Seg.pattern.scale = value def segment_image(self): if self.file_name != "": self.progressBar.show() self.progressBar.setValue(0) image = cv2.imread(str(self.file_name)) orginal = cv2.imread(str(self.file_name)) self.annotations = { 'id': self.dialog.id.isChecked(), 'ellipse': self.dialog.ellipse.isChecked(), 'outline': self.dialog.outline.isChecked(), 'center': self.dialog.center.isChecked(), 'directionality': self.dialog.directionality.isChecked(), 'direction_line': self.dialog.direction_line.isChecked(), 'gamma': self.dialog.gamma.isChecked() } self.pattern_metrics = { 'linearity': self.dialog.linearity_check.isChecked(), 'convergence': self.dialog.convergence_check.isChecked(), 'distribution': self.dialog.distribution_check.isChecked() } a = time.time() self.result = Seg.stain_segmentation(image, orginal) print('segment time', time.time() - a) result = self.result.copy() Seg.pattern.image = result Seg.pattern.name = self.file_name self.set_image() self.populate_tables() self.viewer.add_annotations(self.annotations, Seg.pattern) def set_image(self): height, width, byteValue = self.result.shape bytesPerLine = 3 * width cv2.cvtColor(self.result, cv2.COLOR_BGR2RGB, self.result) qImg = QtGui.QImage(self.result.data, width, height, bytesPerLine, QtGui.QImage.Format_RGB888) self.pixmap = QtGui.QPixmap.fromImage(qImg) self.viewer.setPhoto(pixmap=self.pixmap) def populate_tables(self): self.clear_tables() self.populate_stain_table() self.populate_pattern_table() self.progressBar.setValue(100) self.progressBar.hide() def populate_stain_table(self): self.tableWidget.setColumnCount(12) self.tableWidget.setRowCount(len(Seg.pattern.stains)) self.tableWidget.itemClicked.connect(self.show_stain) headers = "position x;position y;area px;area_mm;width ellipse;height ellipse;angle;gamma;direction;solidity;circularity;intensity" self.tableWidget.setHorizontalHeaderLabels(headers.split(";")) ids = [str(i) for i in range(0, len(Seg.pattern.stains))] self.tableWidget.setVerticalHeaderLabels(ids) j = 0 for stain in progressbar.progressbar(Seg.pattern.stains): percent = (j / len(Seg.pattern.stains)) * 50 self.progressBar.setValue(percent) stain_data = stain.get_summary_data() for i in range(1, 13): if stain_data[i] != None: self.tableWidget.setItem( j, i - 1, QtGui.QTableWidgetItem(str(stain_data[i]))) j += 1 self.tableWidget.show() def show_stain(self, item): position = (int(self.tableWidget.item(item.row(), 0).text()), int(self.tableWidget.item(item.row(), 1).text())) self.viewer.add_rectangle(position[0] - 50, position[1] - 50, 100, 100) def populate_pattern_table(self): metrics = [ "Linearity - Polyline fit", "R^2", "Distribution - ratio stain number to convex hull area", "ratio stain area to convex hull area", "Convergence - point of highest density", "box of %60 of intersections" ] self.pattern_table_widget.setColumnCount(2) self.pattern_table_widget.setRowCount(len(metrics)) self.pattern_table_widget.setHorizontalHeaderLabels( ["Metric", "Value"]) pattern_data = Seg.pattern.get_summary_data(self.pattern_metrics) for i in range(len(pattern_data)): self.pattern_table_widget.setItem( i, 0, QtGui.QTableWidgetItem(str(metrics[i]))) self.pattern_table_widget.setItem( i, 1, QtGui.QTableWidgetItem(str(pattern_data[i]))) def clear_tables(self): self.tableWidget.setRowCount(0) self.tableWidget.clear() self.pattern_table_widget.setRowCount(0) self.pattern_table_widget.clear() def show_batch_dialog(self): self.batch_dialog = batch_dialog.Ui_BatchProcessing() Dialog = self.show_dialog(self.batch_dialog, self.batch_process) self.batch_dialog.folder_path.clicked.connect(self.open_folder) self.batch_dialog.output_path.clicked.connect(self.output_folder) Dialog.exec_() def open_folder(self): folder_name = QtGui.QFileDialog.getExistingDirectory( None, 'Select a folder:', 'C:\\', QtGui.QFileDialog.ShowDirsOnly) self.batch_dialog.folder_path_edit.setText(folder_name) def output_folder(self): out_folder = QtGui.QFileDialog.getExistingDirectory( None, 'Select a folder:', 'C:\\', QtGui.QFileDialog.ShowDirsOnly) self.batch_dialog.output_path_edit.setText(out_folder) def batch_process(self): self.progressBar.show() self.progressBar.setValue(0) folder_name = self.batch_dialog.folder_path_edit.text() output_folder = self.batch_dialog.output_path_edit.text() scale = self.batch_dialog.scale_spin.value() if folder_name: Dialog = QtGui.QDialog() self.dialog.setupUi(Dialog) self.setWindowTitle("ABPA - " + folder_name) if folder_name[-1] != '/' and folder_name[-1] != '\\': folder_name += "/" if output_folder[-1] != '/' and output_folder[-1] != '\\': output_folder += "/" batch_process.segment_images(folder_name, output_folder, scale, self.progressBar)