图像轮廓
Contours:轮廓
轮廓是将没有连着一起的边缘连着一起。
边缘检测检测出边缘,边缘有些未连接在一起。
注意问题
1.对象为二值图像,首先进行阈值分割或者边缘检测。
2.查找轮廓需要更改原始图像,通常使用原始图像的一份进行拷贝。
3.在opencv里,是从黑色背景里找白色。因此对象必须是白色,背景为黑色。
方法
- cv2.findContours()
- cv2.drawContours()
通过cv2.findContours() 查找轮廓在哪里,再通过 cv2.drawContours()将查找的轮廓绘制出来。
contours,hierarchy=cv2.findContours(image,mode,method)
contours:轮廓
hierarchy:图像的拓扑信息(轮廓层次)(存储上一个轮廓,父轮廓…)
image:原始图像
mode:轮廓检索方式
method:轮廓的近似方法
r=cv2.drawContours(image, contours, contourIdx, color[, thickness])
r:目标图像
image:原始图像
contours: 所有的输入轮廓边缘数组
contourIdx :需要绘制的边缘索引,如果全部绘制为-1。如果有多个目标,可以绘制第一个目标0,第二个目标1,第三个目标2.。。
color:绘制的颜色,为BGR格式的SCalar
thickness:可选,绘制的密度,即轮廓的画笔粗细
import cv2 import numpy as np o = cv2.imread('lena256.bmp') gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)#BGR-灰度 ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)#二值图像 contours, hierarchy = cv2.findContours(binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) co=o.copy()#对原始图像进行绘制 r=cv2.drawContours(co,contours,-1,(0,127,127),4)#co为复制图像,轮廓会修改原始图像 cv2.imshow("original",o) cv2.imshow("contours",r) cv2.waitKey()
cv2.cvtColor(input_image, flag)用于颜色空间转换。
input_image:需要转换的图像
flag:转换类型
cv2.COLOR_BGR2GRAY : BGR -灰度
cv2.COLOR_BGR2RGB:BGR-RGB
cv2.COLOR_BGR2HSV:BGR-HSV
最小外接圆
函数cv2.minEnclosingCircle() 可以帮我们找到一个对象的外切圆。它是所有能够包括对象的圆中面积最小的一个。
案例:现有下面这样一张图片,要求将图片中心的花朵标记出来。
代码:
import numpy as np import cv2 as cv img=cv.imread("image.jpg",0) #为了显示方便,这里将图片进行缩放 x,y=img.shape img=cv.resize(img,(y//2,x//2)) #将图片二值化,由于前景物体是黑色的,因此在二值化时采用cv.THRESH_TOZERO_INV这种方式 ret,thresh=cv.threshold(img,127,255,cv.THRESH_TOZERO_INV) #寻找图片中的轮廓,mode=cv.RETR_EXTERNAL,这是为了寻找最外层的轮廓 im,contour,hierarchy=cv.findContours(thresh,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE) #cv.minEnclosingCircle函数的参数要求是ndarray类型,因此这里将找到的 # 轮廓中的所有的点存放在一个列表中,然后使用这个列表创建数组 point_list=[] for i in contour: for j in i: point_list.append(j[0]) point_array=np.array(point_list) #使用最小外接圆函数,返回值为这个圆的圆心坐标和圆半径长度 (x,y),radius=cv.minEnclosingCircle(point_array) #图片上的坐标均为整数,圆的半径也要求是整数,因此将它们强制转换为int类型 center=(int(x),int(y)) color=cv.cvtColor(img,cv.COLOR_GRAY2BGR) color=cv.circle(color,center,radius=int(radius),color=(0,0,255),thickness=2) #显示图片 cv.imshow("color",color) cv.waitKey(0) cv.destroyAllWindows()
程序结果:
凸包
凸包与轮廓近似相似,但不同,虽然有些情况下它们给出的结果是一样的。函数cv2.convexHull() 可以用来检测一个曲线是否具有凸性缺陷,并能纠正缺陷。一般来说,凸性曲线总是凸出来的,至少是平的。在opencv中使用函数cv.convexhull来寻找轮廓的凸包,该函数的定义为:
hull=cv.convexHull( points[, hull[, clockwise[, returnPoints]]])
这个函数的参数如下:
Points:我们需要传入的轮廓
Hull:输出,通常不需要
clockwise: 取向标志,如果为True,凸包的方向是顺时针方向,否则为逆时针方向;
returnPoints: 默认为True. 它会返回凸包上点的坐标。如果设置为False,就会返回与凸包点对应的轮廓上的点。
还是上面的这副图片,我们对上面的代码稍加修改,可以得到凸包的形状,代码如下:
import numpy as np import cv2 as cv img=cv.imread("image.jpg",0) #为了显示方便,这里将图片进行缩放 x,y=img.shape img=cv.resize(img,(y//2,x//2)) #将图片二值化,由于前景物体是黑色的,因此在二值化时采用cv.THRESH_TOZERO_INV这种方式 ret,thresh=cv.threshold(img,127,255,cv.THRESH_TOZERO_INV) #寻找图片中的轮廓,mode=cv.RETR_EXTERNAL,这是为了寻找最外层的轮廓 im,contour,hierarchy=cv.findContours(thresh,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE) #cv.minEnclosingCircle函数的参数要求是ndarray类型,因此这里将找到的 # 轮廓中的所有的点存放在一个列表中,然后使用这个列表创建数组 point_list=[] for i in contour: for j in i: point_list.append(j[0]) point_array=np.array(point_list) #寻找凸包,返回值是凸包上的点 hull=cv.convexHull(point_array,returnPoints=True) color=cv.cvtColor(img,cv.COLOR_GRAY2BGR) #将凸包绘制出来,需要注意的是:这里需要将凸包上点的坐标写成一个 #列表传入函数cv.ploylines,否则绘制出来的只是凸包上的一系列点 color=cv.polylines(color,[hull],True,(0,0,255),2) #显示图片 cv.imshow("color",color) cv.waitKey(0) cv.destroyAllWindows()
程序运行结果为:
图像掩模和像素点
有时我们需要构成对象的所有像素点,我们可以将图像的所有轮廓提取出来,然后使用函数cv.drawContours()将轮廓内的区域填充为指定的颜色。然后使用cv.findNonZeros()函数将非零像素点的坐标提取出来,这样就得到了构成对象的像素点。我们还是在上面的图片上进行操作,代码如下:
import numpy as np import cv2 as cv img=cv.imread("image.jpg",0) #为了显示方便,这里将图片进行缩放 x,y=img.shape img=cv.resize(img,(y//2,x//2)) #将图片二值化,由于前景物体是黑色的,因此在二值化时采用cv.THRESH_TOZERO_INV这种方式 ret,thresh=cv.threshold(img,127,255,cv.THRESH_TOZERO_INV) #寻找图片中的轮廓,mode=cv.RETR_EXTERNAL,这是为了寻找最外层的轮廓 im,contour,hierarchy=cv.findContours(thresh,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE) #创建一个填充轮廓内像素点的画板,背景颜色为黑色,这里我们使用numpy创建一个全零的二维数组 mask=np.zeros(img.shape,dtype=np.uint8) #将参数thickness设置为-1,这样cv.drawContours函数就会将轮廓内的像素点填充为指定的颜色 mask=cv.drawContours(mask,contour,contourIdx=-1,color=(255,255,255),thickness=-1) #寻找mask内非零像素点,将其存放为一个numpy数组 NonZeroPoints=np.array(cv.findNonZero(mask)) #形状变换,将其改变为一个二维数组,数组的每一行存放一个非零像素点的坐标 NonZeroPoints=NonZeroPoints.reshape((-1,2)) #验证我们提取出来的像素点坐标是否正确,我们使用变量 #column和row分别存放非零像素点在图像中坐标的列数和行数 column=NonZeroPoints[:,0] row=NonZeroPoints[:,1] #在新的画板上将这些点绘制出来,将这些坐标对应的像素点的值设为255 mask1=np.zeros(img.shape) mask1[row,column]=255 #显示结果 cv.imshow("mask",mask) cv.imshow("mask1",mask1) cv.waitKey(0) cv.destroyAllWindows()
程序运行结果:
通过上面两幅图的对比结果,我们可以看到:对象的组成像素点被正确地提取出来了。
opencv,图像轮廓
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新动态
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]