使用numpy创建一张空白的画布
#10*10,3通道的全0数组(此处产生的img为纯黑色画布)
img = np.zeros([10, 10, 3],dtype = np.uint8)
#10*10,3通道的全1数组
img = np.ones([10, 10, 3],dtype = np.uint8)
#要产生一个全白色画布时,用全1数组每一项都乘以255
img = 255*np.ones([10, 10, 3],dtype = np.uint8)
获取路径中的文件的名字
os.path.basename(path)
鼠标点击-释放-移动事件
#鼠标点击事件
def mousePressEvent(self,event):
self.flag = True
self.x0 = event.x()
self.y0 = event.y()
#鼠标释放事件
def mouseReleaseEvent(self,event):
self.flag = False
#鼠标移动事件
#由于加了if self.flag,所以等于只有flag = True,即鼠标点击并移动时,才会继续执行下面的操作
def mouseMoveEvent(self,event):
if self.flag:
self.x1 = event.x()
self.y1 = event.y()
self.update()
为按钮添加快捷键
setShortcut('Ctrl+1')
例如:
#红色画笔
self.red_pen = QtWidgets.QToolButton(self.penBox)
self.red_pen.setObjectName("red_pen")
self.red_pen.setGeometry(QtCore.QRect(5,5,30,30))
self.red_pen.clicked.connect(Form.set_red_pen)
self.red_pen.setStyleSheet("QToolButton{border-image: url(pic/1.jpg)}")
self.red_pen.setAutoRaise(True)
self.red_pen.setToolTip("红色(Ctrl+1)")
self.red_pen.setShortcut('Ctrl+1') #出发按钮的快捷键Ctrl+1
这样按“Ctrl + 1”就可以启动 red_pen 按钮的功能。
软件上的完善
在label中显示图片基础上,想要在图片上进行标注,首先想到的是用opencv来改图。我所想产生的是一张白色幕布,上面充斥着我标注的内容。一开始是想能否在label上叠加一个透明label作为上一个图层,但是试了半天也不知道这该怎么弄。所以直接的,通过事件,监测鼠标的点击拖动,将拖动的坐标反映到原图像上和白色幕布上。
一开始只是打算根据鼠标拖动,根据坐标画一个个圆,估计是opencv的处理拖慢了坐标读取的速度。尴尬的是,如果鼠标画笔拖快了就会出现如下结果:
观察一下发现,由于即使拖快以后,两个点之间的距离也不是很大,所以想到在每两个点之间,再补充一条线,因为直线之间有圆作为缓冲,也不会显得有棱有角的,画笔的速度也很丝滑:
这样就平滑多了!(要注意的是这个线的宽度是圆的直径)
相比于昨天的进度,今天添加了很多很多全局变量,以确保这个功能的可用,具体添加的变量的代码不展示了。
我在鼠标移动事件中的代码:
#鼠标点击时 flag = true,判断移动
def mouseMoveEvent(self,event):
if self.flag:
self.x1 = event.x()
self.y1 = event.y()
#self.update()
if self.ui.stacked.currentIndex() == 1: #当前staked位于标注图像功能页
if self.curr_pic != "": #当前文件已获取
if self.pen_clor !="": #已选择画笔
if self.x1>5 and self.x1<5+self.ui.lflabel1.width(): #鼠标移动范围在label的显示图片区域内
if self.y1>105 and self.y1<105+self.fit_height:
if self.draw_board != "": #画布已完成初始化
#在原图上光标拖拽处画圆
cv2.circle(self.cur_img, (int((self.x1-5)/self.ui.lflabel1.width()*self.cur_lab_shape[1]), int((self.y1-100)/self.ui.lflabel1.height()*self.cur_lab_shape[0])), self.radius*self.pen_times, self.pen_color(), -1)
#在画布上光标拖拽处画圆
cv2.circle(self.draw_board, (int((self.x1-5)/self.ui.lflabel1.width()*self.cur_lab_shape[1]), int((self.y1-100)/self.ui.lflabel1.height()*self.cur_lab_shape[0])), self.radius*self.pen_times, self.pen_color(), -1)
if self.start_point != "": #指针节点是否加载?(是:第二次绘制点 否:首次绘制点,丢弃)
#在原图上以直线连接拖拽产生的各个圆
cv2.line(self.cur_img, self.start_point, (int((self.x1-5)/self.ui.lflabel1.width()*self.cur_lab_shape[1]), int((self.y1-100)/self.ui.lflabel1.height()*self.cur_lab_shape[0])), self.pen_color(), 2*self.pen_times*self.radius, 4)
#在画布上以直线连接拖拽产生的各个圆
cv2.line(self.draw_board, self.start_point, (int((self.x1-5)/self.ui.lflabel1.width()*self.cur_lab_shape[1]), int((self.y1-100)/self.ui.lflabel1.height()*self.cur_lab_shape[0])), self.pen_color(), 2*self.pen_times*self.radius, 4)
#加载结点指针
self.start_point = (int((self.x1-5)/self.ui.lflabel1.width()*self.cur_lab_shape[1]), int((self.y1-100)/self.ui.lflabel1.height()*self.cur_lab_shape[0]))
#绘制完的图片加载到label
img2 = cv2.cvtColor(self.cur_img, cv2.COLOR_BGR2RGB)
QImage = QtGui.QImage(img2, self.cur_lab_shape[1], self.cur_lab_shape[0],3*self.cur_lab_shape[1],QtGui.QImage.Format_RGB888)
pixmap = QtGui.QPixmap(QImage).scaled(self.ui.lflabel1.width(),self.fit_height)
self.ui.lflabel1.setPixmap(pixmap)
加了很多个判断,因为定义的布局结构,这些判断少一个都不行,都会引起报错。
很多变量都需要在选择文件夹和切换图片文件的时候初始化一下,比如画布,以及为了画线段而保存的上一次读取的节点等多控件并发控制的变量。否则就会出现画布没有清空,多次标注都叠在一起。还有鼠标一点击画标注,就会与上一次的节点自动生成一段连线,所以在松开鼠标时就要将这个节点销毁:
#鼠标释放事件
def mouseReleaseEvent(self,event):
self.flag = False
#销毁结点指针
self.start_point = ""
由于按钮中返回的全局变量self.pen_clor
返回的都是颜色名,所以自己定义了一个调色板,相关数值以后可能还会用到。
def pen_color(self):
#颜料板
if self.pen_clor == "red":
return (0,0,255) #数值可在画图工具中查看
if self.pen_clor == "green":
return (0,255,0)
if self.pen_clor == "l_blue":
return (232,162,0)
if self.pen_clor == "purple":
return (164,73,163)
if self.pen_clor == "black" or self.pen_clor == "": #选择黑色或未选取颜色
return (0,0,0)
if self.pen_clor == "blue":
return (204,72,63)
if self.pen_clor == "brown":
return (87,122,185)
if self.pen_clor == "yellow":
return (0,242,255)
if self.pen_clor == "grey":
return (127,127,127)
if self.pen_clor == "orange":
return (39,127,255)
if self.pen_clor == "white":
return (255,255,255)
选择文件夹功能进行了一些添加:
#选择文件夹
def select_dir(self):
self.label_path = Qt.QFileDialog.getExistingDirectory() #选择文件夹获取路径
#print(self.label_path)
if self.label_path is not None or self.label_path != "":
self.files = os.listdir(self.label_path) #所有文件
self.files.sort() #文件排序
self.curr_pic = self.label_path+"/"+self.files[0] #当前文件路径指针,初始时指向第一个文件
self.cur_img = cv2.imread(self.curr_pic) #当前读出图像指针
self.cur_lab_shape = self.cur_img.shape #当前读出图像shape指针
self.fit_height = (self.ui.lflabel1.width()/self.cur_lab_shape[1])*self.cur_lab_shape[0] #自适应窗口大小的高度
self.ui.lflabel1.setPixmap(QtGui.QPixmap(self.curr_pic).scaled(self.ui.lflabel1.width(),self.fit_height)) #label显示图片
self.draw_board = 255*np.ones([self.cur_lab_shape[0], self.cur_lab_shape[1], 3],dtype = np.uint8) #就绪时初始化全局的白色画布
#初始化文件列表
self.ui.file_list.clear() #文件列表清空
for i in range(0,len(self.files)):
self.ui.file_list.insertItem(i,self.label_path+"/"+self.files[i]) #就绪时加载文件列表
文件列表list切换文件时初始化:
def pic_display(self,i): #list列表中点击文件名,切换图片
self.curr_pic = self.label_path+"/"+self.files[i] #当前文件指针指向列表中的文件
self.cur_img = cv2.imread(self.curr_pic) #加载当前文件
self.cur_lab_shape = self.cur_img.shape
self.fit_height = (self.ui.lflabel1.width()/self.cur_lab_shape[1])*self.cur_lab_shape[0]
self.ui.lflabel1.setPixmap(QtGui.QPixmap(self.curr_pic).scaled(self.ui.lflabel1.width(),self.fit_height)) #label显示当前图片
self.draw_board = 255*np.ones([self.cur_lab_shape[0], self.cur_lab_shape[1], 3], np.uint8) #初始化画布
运行结果:
还有一些需要完善的后面再加。