实战篇:粘连物体分割——利用角点检测、定ROI区域、透视变换、几何分割实现硬币分割计数

  • 一、背景
  • 二、思路
  • 三、代码
  • 四、效果
  • 五、声明
  • 六、其他文章

一、背景

    前面分享过一篇文章,是利用几何分割的方法,实现了 瓶盖的分割检测瓶盖的分割检测,本文主要利用该文章算法,应用到硬币检测当中(因为图像分辨率有一些不一样,质量也有点不同,所有需要稍微调参),经过稍微调参之后,这里附上可以直接运行的代码。

图1 瓶盖检测效果 图2 硬币检测

二、思路

一、基于****Harrs****的角点检测
圆形相粘物体之间会存在凹凸区域、可以通过对相连区域进行角点检测、或者凹凸点检测。检测点是否齐全决定分割的准确度。为了保证分割效果,角点检测阶段经可能检测多一些角点。
二、均值去噪点
对所有点求最近点求最小欧式距离,通过对距离进行求均值以及中位数。其中均值指标适用于杂点较多以及距离较大的情况。对于杂点较多,距离较大的情况。通过均值指标或者中位数指标能去掉部分杂点。
三、ROI取样
为了更进一步筛选掉部分杂点,以及更好第判断两点之间是否为物体相连部分,因为通过相连部分的两个角点,进行旋转90°获得一个正方向的ROI区域,假设ROI区域为相连部分,其ROI区域中的填充率较大。因此利用本特征进行ROI取样。其中只有正方形ROI才能更好地衡量其填充率。
四、坐标变换
在对图像坐标进行运算的时候,需要进行坐标变换,将坐标原点移动到图像的中心。
五、旋转矩阵获取
对获取到两个角点,通过坐标变换、旋转矩阵的获取,使图像中任意点能绕一点进行旋转一定角度。
六、透视变换
因为需要计算填充率需要进行透视变换,求ROI轮廓面积与ROI面积之比,因为不同四边形的角度不一样,所以需要对所有点进行排序,同时又一种特殊情况就是四边形对角线如果是竖直的情况,则不需要及进行排序,不然会出现矫正错误。
七、填充率计算
通过取样的ROI进行计算二值化轮廓面积与ROI面积之比,获取填充率,设定一个值,假如大于阈值,就进行分割。

三、代码

    将下面代码放在.py文件当中,读取图片,运行之后,便会在当前路径自动创建文件夹将所有图片保存里面。

"""
作者:冯耿鑫
时间:2021/1/9
功能:对相连的圆形物体进行分割
思路:=>>创新:形态学操作的小技巧可以定义一个卷积核、然后在本卷积核上画圆,就是一个圆形的卷积了=>>基于Harris角点检测、得出dist图像,因为再拐角处会有很多个角点,为了只求一个,所以进行二值化,膨胀,求拐点的形心。=>>对角点进行x方向的排序=>>进行坐标变换、以及旋转矩阵求出垂直的另一条直线=>>进行透视变换,矫正ROI区域,需要通过透视变换来求得,其中ROI的透视变换用到了坐标排序,其中需要注意一种对角线竖直的情况,然后求包含物体的饱和率,从而进行筛选。=>>利用连通域进行颜色显示
"""# -*- coding:utf-8 -*-
import cv2 as cv
import numpy as np
import cv2
import math
import os
# 创建文件进行图片保存
def make_dir_save_img(path,img, binary,open,color,result ):if not os.path.exists(name):  # 判断是否存在os.makedirs( name)  # 不存在就创建文件夹if not os.path.exists("roi\\"+name):  # 判断是否存在os.makedirs("roi\\" +name)  # 不存在就创建文件夹cv.imwrite(name+"\\img.png",img)cv.imwrite(name+"\\binary.png",binary)cv.imwrite(name+"\\open.png",open)cv.imwrite(name+"\\color.png",color)cv.imwrite(name+"\\result.png",result)class SegmentationConnectObject(object):def __init__(self,img,binary):self.img = img          # 原图self.binary = binary    # 二值化图片self.number = 0         # 第几个轮廓self.answer = False     # 一开始默认是不是相连的self.H,self.W,self.C = img.shape# 寻找距离最小的两个点def main_find_mindist_points(self):""":function: 用来寻找两个最近的点,用来进行区域分析:return:""""""=>>圆形卷积核进行形态学操作,消除杂点噪声、以及光滑变换<<="""k2 = np.zeros((6, 6), np.uint8)                           # <==定义一个卷24x24的卷积核cv2.circle(k2, (3, 3), 3, (1, 1, 1), -1, cv2.LINE_AA)    # <==在这个卷积核上进行画一个鹃形的卷积核k1 = cv.getStructuringElement(cv.MORPH_ELLIPSE,(3,3))self.binary= cv.morphologyEx(self.binary, cv.MORPH_DILATE, k1)     # 进行开操作,也就是先腐蚀后膨胀open = cv.morphologyEx(self.binary, cv.MORPH_OPEN, k2)     # 进行开操作,也就是先腐蚀后膨胀"""=>>利用Harris进行角点检测<<= """harris = cv2.cornerHarris(open, 2, 5, 0.04)     #<== 进行角点检测,blockSize:角点检测中要考虑的领域大小||ksize - Sobel:求导中使用的窗口大小||k - Harris:角点检测方程中的自由参数, 取值参数为[0, 04, 0.06]harris = cv2.dilate(harris, None)               # 对角点进行一个简单的膨胀、不然的话轮廓会不好寻找# img[harris > 0.2 * harris.max()] = [0, 0, 255]  #<== 通过角点检测之后只有边缘像素是有值的,拐角的地方是比较大,所以利用这个条件进行显示pix_max = 0.2 * harris.max()                      # <==获取选择角点的阈值ret, binary_p = cv.threshold(harris, pix_max, 255, cv.THRESH_BINARY)  # 因为选择出来的像素太多了,又类似与轮廓,所以我们直接进行阈值分割发现轮廓binary_p = np.uint8(binary_p)                    # 二值化前需要对位数进行转换contours = cv2.findContours(binary_p, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[0]  # 发现角点的轮廓,用来发现其角点的质心"""=>>找到所有的可能坐标点<<= """points = []             # 用来储存所有的角点坐标for c in contours:      # 横向# 获取矩形框的四个参数mm = cv.moments(c)  # 几何重心的获取cx, cy = int(mm['m10'] / mm['m00']), int(mm['m01'] / mm['m00'])points.append((int(cx), int(cy)))  # 将坐标保留在points列表中cv.circle(self.img, (int(cx), int(cy)), 3, (0, 0, 255), -1)"""=>>对所有的角点排序,方向为x从小到大<<= """points_sorted_x = self.sort_x(points)# for i, p in enumerate(points_sorted_x):# cv.circle(self.img, p, 4, (255, 0, 0), -1)  # 画出点# cv.putText(self.img,str(i) , p, cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 100, 255), 2)"""=>>因为有很多角点,如何获得最小相连的两个点呢,通过遍历所有点,获取最小距离用来求平均值,用来作为指标作为阈值,筛选太远的点<<= """points_sorted_x_2 = points_sorted_x.copy()          # 因为要遍历两次,所以赋值一份方便后面更改distance_1 = []                                     # 所有点都进行遍历、储存每个点相连做近的点for p1 in points_sorted_x:                  # p1作为父点x1, y1 = p1                             # p1的坐标distance_2 = []                         # 用来储存所有子点p2到p1的距离,然后获取最小距离给diatance_1for p2 in points_sorted_x_2:            # p2作为父点x2, y2 = p2                         # p2的坐标if x1 == x2 and y1 == y2:           # 因为两个列表是一样的,所以会有遇到相同的点,需要跳过,不然distance_2中最小的都是0continue                        # 循环到原来的带点就不进行计算else:l = pow(abs(x1 - x2) ** 2 + abs(y1 - y2) ** 2, 0.5)  # 计算父点与子点的欧式距离distance_2.append(l)            # 将所有欧式距离保存在distance_2中distance_1.append(min(distance_2))      # 获取每个父点到子点的最小欧式距离mean_dist = np.mean(distance_1)             # 这里设置了两个指标,一个是平均值,适合密集点median_dist = np.median(distance_1)         # 一个是中间数,适合杂点较少情况"""=>>上面根据模型求出距离指标,下面将通过设定阈值进行求解<<= """choose = []                                 # choose列表使用来记录已经检测完毕的两个点,用来判断,如果没有检测成功,就继续检测,如果检测成功,那就跳过避免重复检测for number, p1 in enumerate(points_sorted_x):   # 遍历父点x1, y1 = p1                                # 父点坐标for p2 in points_sorted_x:                  # 遍历子点x2, y2 = p2                             # 子点坐标if x1 == x2 and y1 == y2:               # 过滤相同的点continueelse:                                   # 求欧式距离l = pow(abs(x1 - x2) ** 2 + abs(y1 - y2) ** 2, 0.5)if l > mean_dist * 0.3 and l < mean_dist * 1.8:     #<<== 设定约束条件,如果在这个阈值范围就可以进行后续的分割功能if p1 in choose or p2 in choose:                # 如果点在choose中就代表这两个点已经检测成功continueself.check_connect(x1, y1, x2, y2)  #<<==判断是否连接函数if self.answer:                          # 当分割成功的时候,用来记录p1,p2这两个点choose.append(p1)choose.append(p2)self.answer = False  # 需要重新赋值,不然边True之后就会一直默认正确color,result = self.connect_domain()return self.img,self.binary, open,color,result# 冒泡排序对角点坐标进行排序def sort_x(self,points):"""function:冒泡排序算法实现对x方向进行排序"""l = len(points)for i in range(l - 1):for j in range(l - 1 - i):# if points[j][1]>points[j+1][1]:#     temp = points[j]#     points[j] = points[j+1]#     points[j+1] = tempif points[j][0] > points[j + 1][0]:temp = points[j]points[j] = points[j + 1]points[j + 1] = tempreturn points# 通过旋转矩阵,实现任一点的旋转def rota(self,x1, y1, x2, y2):""":function:以任意点为中线,通过坐标平移,然后通过旋转,再平移回来,最终完成旋转。:return:"""# 获取直线的中点cx, cy = (x1 + x2) / 2, (y1 + y2) / 2# 偏移矩阵C = np.array([[cx], [cy]])# 旋转角度degree = math.radians(90)# 旋转矩阵A = np.array([[math.cos(degree), -math.sin(degree)],[math.sin(degree), math.cos(degree)]])# 输入坐标X1 = np.array([[x1], [y1]])X2 = np.array([[x2], [y2]])# 进行偏移,将中间点转换为中间坐标X1 = X1 - CX2 = X2 - C# 利用矩阵的乘积求出旋转坐标Y1 = np.dot(A, X1)Y2 = np.dot(A, X2)# 转换绝对坐标的形式Y1 = Y1 + CY2 = Y2 + Cout_x1, out_y1, out_x2, out_y2 = int(Y1.ravel()[0]), int(Y1.ravel()[1]), int(Y2.ravel()[0]), int(Y2.ravel()[1])return out_x1, out_y1, out_x2, out_y2# 检查是否为相连物体def check_connect(self,x1, y1, x2, y2):""":function:检查这两个点是否为相连接的两个点""""""==>>因为后面需要用到图像坐标的各种运算,所以需要先进行坐标变换<<=="""x1, y1 = self.change_coordinate_lt_center(x1, y1)   # 将第一个点也就是父点转换为笛卡尔坐标系x2, y2 = self.change_coordinate_lt_center(x2, y2)   # 将第二个点也就是子点转换为笛卡尔坐标系"""==>>进行旋转90°,分别获得父、子的旋转坐标<<=="""x3, y3, x4, y4 = self.rota(x1, y1, x2, y2)       #x3,y3是父点的逆时针旋转点、x4,y4是子点的旋转坐标点"""==>>转为图像坐标系<<=="""x1, y1 = self.change_coordinate_center_lt(x1, y1)   # 将父点坐标转为图像坐标x2, y2 = self.change_coordinate_center_lt(x2, y2)   # 将子点坐标转为图像坐标x3, y3 = self.change_coordinate_center_lt(x3, y3)   # 将父点坐标旋转坐标转为图像坐标x4, y4 = self.change_coordinate_center_lt(x4, y4)   # 将子点坐标旋转坐标转为图像坐标"""==>>进行透视变换、因为是倾斜的矩形,必须透视变换,不然的话没办法求比例<<=="""pts = [(x1, y1), (x3, y3), (x2, y2), (x4, y4)]"""==>>进行透视变换、因为是倾斜的矩形,必须透视变换,不然的话没办法求比例<<=="""self.Perspective_transformation(pts)"""==>>answer表示的是,检测区域为相连接部分,<<=="""if self.answer:arrPt = np.array(pts, np.int32).reshape((-1, 1, 2))  # 将坐标转换为n行两列的形式cv.polylines(img, [arrPt], True, (255, 0, 255), 1)# 图片坐标系转为笛卡尔坐标系def change_coordinate_lt_center(self,x_in, y_in):"""x_out = x_in-1/2Wy_out = -(y_in-1/2H) = 1/2H-y_in"""x_out = x_in - 1 / 2 * self.Wy_out = 1 / 2 * self.H - y_inreturn x_out, y_out# 笛卡尔坐标系转为图片的坐标系def change_coordinate_center_lt(self,x_in, y_in):"""x_out = x_in+1/2Wy_out = -(y_in-1/2H) = 1/2H-y_in =>>y_in = 1/2H-y_out ==>> y_out = 1/2H-yin"""x_out = x_in + 1 / 2 * self.Wy_out = 1 / 2 * self.H - y_inreturn int(x_out), int(y_out)def order_points(self,pts):# initialzie a list of coordinates that will be ordered# such that the first entry in the list is the top-left,# the second entry is the top-right, the third is the# bottom-right, and the fourth is the bottom-leftrect = np.zeros((4, 2), dtype="float32")# the top-left point will have the smallest sum, whereas# the bottom-right point will have the largest sums = pts.sum(axis=1)rect[0] = pts[np.argmin(s)]rect[2] = pts[np.argmax(s)]# now, compute the difference between the points, the# top-right point will have the smallest difference,# whereas the bottom-left will have the largest differencediff = np.diff(pts, axis=1)rect[1] = pts[np.argmin(diff)]rect[3] = pts[np.argmax(diff)]# return the ordered coordinatesreturn rect# 进行透视变换def Perspective_transformation(self,pts):"""==>>获取四个点的坐标,依次是父点、父点旋转点、子点、子点旋转点、同时对对角线竖直的情况进行单独分析<<=="""(x1, y1), (x2, y2), (x3, y3), (x4, y4) = pts[0], pts[1], pts[2], pts[3]pts1 = np.float32(pts)  # 透视变换前坐标需要转换为32位if x1 == x3 or x2 == x4:  # 有一张特殊情况,就是对角线是竖直线,这样的经过排序之后就会出现变形,漏检测,rect = pts1else:rect = self.order_points(pts1)(tl, tr, br, bl) = rect"""==>>计算ROI区域的长宽<<=="""widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))maxWidth = max(int(widthA), int(widthB))heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))maxHeight = max(int(heightA), int(heightB))"""==>>获取变换后图片的坐标点、获得旋转矩阵、同时进行透视变换<<=="""dst = np.array([[0, 0],[maxWidth - 1, 0],[maxWidth - 1, maxHeight - 1],[0, maxHeight - 1]], dtype="float32")     # in the top-left, top-right, bottom-right, and bottom-leftmatrix = cv2.getPerspectiveTransform(rect, dst)     # 获得旋转矩阵roi = cv.warpPerspective(self.binary, matrix, (maxHeight, maxWidth))    # 获取roi区域"""==>>对获取到的目标区域进行面积统计<<=="""contours = cv2.findContours(roi, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[0]  # 发现最外边轮廓area_list = []                              #定义一个列表用来储存所有的面积for cnt in contours:area_list.append(cv.contourArea(cnt))if len(area_list) == 0:max_cnts = 0                            # 如果区域没有面积,sum会报错,所以需要单独赋值为0else:max_cnts = sum(area_list)               # 获取面积综合area = maxWidth * maxHeight                 # ROI的一个面积ratio = max_cnts / area                     # 二值化面积比if ratio > 0.8:self.number +=1cv.circle(self.img, ((x1 + x3) // 2, int(y1 + y3) // 2), 4, (0, 0, 255), -1)cv.circle(self.img, (x1, y1), 3, (255, 0, 0), -1)cv.circle(self.img, (x2, y2), 3, (255, 0, 0), -1)cv.circle(self.img, (x3, y3), 3, (255, 0, 0), -1)cv.circle(self.img, (x4, y4), 3, (255, 0, 0), -1)cv.line(self.img, (x1, y1), (x2, y2), (0, 255, 255), 1)cv.line(self.img, (x3, y3), (x4, y4), (0, 255, 255), 1)cv.line(self.binary, (x1, y1), (x3, y3), (0, 0, 0),2)cv.putText(self.img, str(self.number), ((x1 + x3) // 2, int(y1 + y3) // 2 - 10), cv.FONT_HERSHEY_SIMPLEX, 0.8,(255, 0, 0), 1)roi_img = cv.warpPerspective(self.img, matrix, (maxHeight, maxWidth))cv.imwrite(".\\roi\\" +name+"\\"+ str(self.number) + ".png", roi_img)self.answer = Truedef connect_domain(self):# # 连通域分析num_labels, labels, stats, centers = cv2.connectedComponentsWithStats(self.binary, connectivity=8)# 利用连通域进行不同轮廓画出不同颜色color = np.zeros((self.H, self.W, 3), np.uint8)for i in range(1, num_labels):mask = labels == icolor[:, :, 0][mask] = np.random.randint(0, 255)color[:, :, 1][mask] = np.random.randint(0, 255)color[:, :, 2][mask] = np.random.randint(0, 255)result = cv2.addWeighted(img, 0.8, color, 0.5, 0)  # 图像权重叠加for i in range(1, len(centers)):cv2.drawMarker(result, (int(centers[i][0]), int(centers[i][1])), (255, 0, 0), 1, 10, 2)return color,resultdef get_binary(img):# # 图像预处理# # blurred = cv.pyrMeanShiftFiltering(img, 10, 100)  # 边缘保留滤波能够进行去噪是同时有效地保留边缘# # gray = cv.cvtColor(blurred, cv.COLOR_BGR2GRAY)  # 进行灰度化为二值化做准备# # ret, binary = cv.threshold(gray, thresh=70, maxval=255, type=cv.THRESH_BINARY)  # 固定阈值二值化,将大于thresh得像素点设置为maxval,# ## blurred = cv.pyrMeanShiftFiltering(img, 5, 50)  # 边缘保留滤波能够进行去噪是同时有效地保留边缘#           #获取灰度图#### # h, s, v = cv.split(hsv)# ret, binary = cv.threshold(blurred, 200, 255, cv.THRESH_BINAR)blurred = cv.pyrMeanShiftFiltering(img, 5, 80)  # 边缘保留滤波能够进行去噪是同时有效地保留边缘gray = cv.cvtColor(blurred, cv.COLOR_BGR2GRAY)  # 进行灰度化为二值化做准备ret, binary = cv.threshold(gray, thresh=0, maxval=255, type=cv.THRESH_BINARY|cv.THRESH_OTSU)  # 固定阈值二值化,将大于thresh得像素点设置为maxval,# imgHsv = cv.cvtColor(blurred, cv.COLOR_BGR2HSV)              #转换为HSV色彩空间# lower = np.array([0, 79, 0])# upper = np.array([179, 255, 255])# binary = cv2.inRange(imgHsv,lower,upper)                   #获取灰度图return  binaryif __name__ == '__main__':path = "3.jpg"name = os.path.splitext(path)[0]            # 文件名img  = cv.imread(path)   # 读取图片# 获取二值化图片binary = get_binary(img)# 创建实例seg = SegmentationConnectObject(img,binary)# 调用第一个函数开始执行功能,返回二值化、开操作、黑底颜色、结果、原图img, binary,open,color,result = seg.main_find_mindist_points()# 进行图片保存make_dir_save_img(path,img, binary,open,color,result )cv.namedWindow("img",0)cv.imshow("img",img)cv.namedWindow("binary",0)cv.imshow("binary",binary)cv.namedWindow("open",0)cv.imshow("open",open)cv.namedWindow("color",0)cv.imshow("color",color)cv.namedWindow("result",0)cv.imshow("result",result)cv.waitKey(0)cv.destroyAllWindows()

四、效果

图3.1 二值化操作(分割线画在了上面) 图3.2 开操作

图3.3 粘连位置角点ROI提取

图3.4 分割效果图 图3. 5 连通域分析染色图 图3. 6 图片融合

五、声明

本次分享内容主要根据前面瓶盖分割项目进行调参,应用硬币识别当中,代码以及算法仍有不少需要优化的地方,可在评论区留下您宝贵的意见,大家一起讨论进步,谢谢大家的支持!!!

六、其他文章


1.理论系列:

第一章:pycharm、anaconda、opencv、pytorch、tensorflow、paddlex等环境配置大全总结【图像处理py版本】

第二章:OpenCv算法的基本介绍与应用

第三章:OpenCv图片、视频读写操作与基本应用

第四章:OpenCv阈值分割/二值化(单通道、多通道图片)总结


2.项目系列:

项目一:四六级改卷系统
项目二:实战篇:粘连物体分割——利用几何分割实现瓶盖分割检测
==》项目三:实战篇:粘连物体分割——利用几何分割实现硬币分割检测
项目四:实战篇:粘连物体分割——利用几何分割实现细胞分割检测
项目五:实战篇:粘连物体分割——利用分水岭算法实现糖豆分割检测

【实战篇:粘连物体分割——利用几何分割实现硬币分割检测】相关推荐

  1. 【实战篇:粘连物体分割——利用几何分割实现瓶盖分割检测】

    实战篇:粘连物体分割--利用角点检测.定ROI区域.透视变换.几何分割实现瓶盖分割计数 一.背景 二.思路 三.代码 四.效果 五.声明 六. 其他文章 一.背景     在去年学习opencv的过程 ...

  2. 实战 | 粘连物体分割与计数应用(三)--密集粘连药片分割+计数案例

    点击下方卡片,关注"OpenCV与AI深度学习"公众号 视觉/图像重磅干货,第一时间送达! 导读 本文主要介绍一个密集粘连药片分割计数综合实例的实现方法和总结. 背景介绍 在实际的 ...

  3. Halcon 物体分割 粘连物体(分水岭)

    资源 链接:https://pan.baidu.com/s/1-CfaeAycuV3YsUJhg5st_A 提取码:7lov 图片 代码 * 1.读取并显示图片 ******************* ...

  4. 翻译英文文献----利用MAP-MRF模型改进三维网格分割算法

    利用MAP-MRF模型改进三维网格分割算法 摘要:本文提出了一种改进三维网格分割算法性能的新方法.我们的方法以一个三维网格对象和一个分割算法作为输入.我们的方法使用马尔可夫随机场(MRF)估计,对于每 ...

  5. 立体相机开发|几何感知的实例分割

    点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 论文下载: http://www-scf.usc.edu/~choyingw/works/GAIS-N ...

  6. matlab水果定位与分割,利用Matlab软件和BP神经网络快速识别水果实现分拣

    我国是水果生产与消费大国,我国水果不但品种丰富,而且以水果为原料的食品如罐头.果冻等加工产业也颇具规模.然而,在水果果料的加工过程中可能会不经意地混入诸如毛发.纤维丝.纸屑.金属.油漆等异物,从而对产 ...

  7. 【Pytorch神经网络理论篇】 33 基于图片内容处理的机器视觉:目标检测+图片分割+非极大值抑制+Mask R-CNN模型

    基于图片内容的处理任务,主要包括目标检测.图片分割两大任务. 1 目标检测 目标检测任务的精度相对较高,主要是以检测框的方式,找出图片中目标物体所在的位置.目标检测任务的模型运算量相对较小,速度相对较 ...

  8. 图像语义分割python_图像语义分割 —利用Deeplab v3+训练VOC2012数据集

    原标题:图像语义分割 -利用Deeplab v3+训练VOC2012数据集 前言: 配置:windows10 + Tensorflow1.6.0 + Python3.6.4(笔记本无GPU) 源码: ...

  9. OpenCV-Python实战(番外篇)——OpenCV中利用鼠标事件动态绘制图形

    OpenCV-Python实战(番外篇)--OpenCV中利用鼠标事件动态绘制图形 使用鼠标事件动态绘制 动态绘制图形 动态绘制图形和文本 相关链接 使用鼠标事件动态绘制 我们已经在<OpenC ...

最新文章

  1. UbuntuKylin技巧
  2. JDK8新特性之Stream流
  3. 基于 MVP 的 Android 组件化开发框架实践
  4. MySQL跨机房集群方案_Mysql跨机房同步方案
  5. 机器学习与Scikit Learn学习库
  6. Flex弹性布局_思维导图
  7. Cordova webapp实战开发:(2)认识一下Cordova
  8. mybatisplus报的mysql错误归纳
  9. 我的世界java致命错误_《我的世界》六个“致命错误”,最后一个“不作死就不会死”...
  10. vc 模拟按键 模拟windows消息方式_PC微信电脑端WeChat点击脚本(按键精灵)2020小工具...
  11. 使用lex与yacc词法语法工具进行简单的SQL语义检查
  12. 安装SQL Server 2016
  13. Srm32 dwm1000 tdoa定位源码
  14. Unity【HTC Vive Noitom】- 关于动作捕捉的两种解决方案
  15. 电脑卡住了怎么保存excel_win7系统遇到死机没及时保存excel文件该怎么办
  16. MBE清新风格毕业答辩PPT模板
  17. 天津师范大学计算机与信息工程学院研究生院,天津师范大学计算机与信息工程学院2020考研调剂信息...
  18. c4d语言包英文,Maxon Cinema 4D R23(C4D R23)中英文安装及设置详细教程(附下载)
  19. hdu2859Phalanx
  20. 微信公众号Makrdown编辑器,语法你懂吗?

热门文章

  1. PostgreSQL资料汇总
  2. ubuntu 误删boot文件夹 grub CD光盘恢复
  3. 全面拥抱 K8s,ApacheDolphinScheduler 应用与支持 K8s 任务的探索
  4. 正点原子delay函数移植到rt thread操作系统(HAL库)
  5. Example类用法example.createCriteria
  6. 做课黄金4要素,手把手教你做出第一门课
  7. JavaScript学习笔记——firstchild的问题
  8. strcat函数用法
  9. 基础——DB9九孔母头、九针公头 (RS232)接口定义
  10. RDKit|化学特征、药效团提取与2D药效团指纹计算