深入学习OpenCV检测及分割图像的目标区域
准备1:OpenCV常用图片转换技巧
在进行计算机视觉模型训练前,我们经常会用到图像增强的技巧来获取更多的样本,但是有些深度学习框架中的方法对图像的变换方式可能并不满足我们的需求,所以掌握OpenCV中一些常用的图像处理技巧对我们还是有很多帮助的。
图像通道分离
我们知道每个图像是由RGB三个颜色通道构成,所以我们可以使用split函数对原图像的三个通道进行分离:
B, G, R = cv2.split(img)
注意:既然我们可以通过split()函数进行图片的通道分类的情况,那么我们就可以借助这一方法来获取img的通道数:
img_num = len(cv2.split(img)) or from PIL import Image image = Image.open(photo_path)
img_num = len(image.split())
进行通道分离的后,我们就可以在每个通道上独立的进行数值变换,变换完成后再来组合来生成新的图像,比如提升图像的亮度:
B,G,R = cv2.split(img)
for i in (B,G,R):
randint = random.randint(50,100)
limit = 255-randint
i[i>limit]=255
i[i<=limit]=randint+i[i<=limit]
img_merge = cv2.merge((B,G,R))
cv2.imshow("img_merge",img_merge)
key = cv2.waitKey()
if key==27:
cv2.destroyAllWindows()
图像旋转
还可以使用 warpAffine 函数根据我们的设定的角度完成图像的旋转:
M = cv2.getRotationMatrix2D((img.shape[1] / 2, img.shape[0] / 2), 30, 1)
img_rotate = cv2.warpAffine(img, M, (img.shape[1], img.shape[0])) cv2.imshow('img_rotate', img_rotate)
key = cv2.waitKey(0)
if key == 27:
cv2.destroyAllWindows()
这里我们不对图像进行缩放,旋转角度为30度。
仿射变换
仿射变换允许图像倾斜并且可以在任意两个方向上发生伸缩。代码如下:
def random_warp(img, row, col):
height, width, channels = img.shape random_margin = 100
x1 = random.randint(-random_margin, random_margin)
y1 = random.randint(-random_margin, random_margin)
x2 = random.randint(width - random_margin - 1, width - 1)
y2 = random.randint(-random_margin, random_margin)
x3 = random.randint(width - random_margin - 1, width - 1)
y3 = random.randint(height - random_margin - 1, height - 1)
x4 = random.randint(-random_margin, random_margin)
y4 = random.randint(height - random_margin - 1, height - 1) dx1 = random.randint(-random_margin, random_margin)
dy1 = random.randint(-random_margin, random_margin)
dx2 = random.randint(width - random_margin - 1, width - 1)
dy2 = random.randint(-random_margin, random_margin)
dx3 = random.randint(width - random_margin - 1, width - 1)
dy3 = random.randint(height - random_margin - 1, height - 1)
dx4 = random.randint(-random_margin, random_margin)
dy4 = random.randint(height - random_margin - 1, height - 1) pts1 = np.float32([[x1, y1], [x2, y2], [x3, y3], [x4, y4]])
pts2 = np.float32([[dx1, dy1], [dx2, dy2], [dx3, dy3], [dx4, dy4]]) M_warp = cv2.getPerspectiveTransform(pts1, pts2)
img_warp = cv2.warpPerspective(img, M_warp, (width, height))
return img_warp img_warp = random_warp(img, img.shape[0], img.shape[1]) cv2.imshow('img_warp', img_warp)
key = cv2.waitKey(0)
if key == 27:
cv2.destroyAllWindows()
伽马修正
伽马修正提升图像的对比度,让图像看起来更加的“明亮”。代码如下:
def adjust_gamma(image, gamma=1.0):
invGamma = 1.0/gamma
table = []
for i in range(256):
table.append(((i / 255.0) ** invGamma) * 255)
table = np.array(table).astype("uint8")
return cv2.LUT(image, table) img_gamma = adjust_gamma(img, 2)
cv2.imshow("img",img)
cv2.imshow("img_gamma",img_gamma) key = cv2.waitKey()
if key == 27:
cv2.destroyAllWindows()
准备2:下载并安装cv2
下载轮子如下:
然后直接使用pip 命令即可:
pip install opencv_python-3.4.3-cp37-cp37m-win_amd64.whl
注意:现在OpenCV for Python 就是通过Numpy 进行绑定的。所以在使用时必须掌握一些Numpy的相关知识!图像就是一个矩阵,在OpenCV for Python 中,图像就是Numpy中的数组!
1,图像的载入,显示和保存
如果读取图像,只需要imread即可。
import cv2 # 获取图片
img_path = r'1.jpg'
img = cv2.imread(img_path)
OpenCV目前支持读取bmp,jpg,png,tiff等常用格式。
我们还可以查看图像的一些基本属性:
print(img)
print(img.dtype)
print(img.shape)
接着创建一个窗口
cv2.namedWindow("Image")
然后在窗口中显示图像
cv2.imshow('Image', img)
最后还要添加一句:
cv2.waitKey(0)
如果不添加最后一句,在IDLE中执行窗口直接无响应。在命令行中执行的话,则是一闪而过。
保存图像很简单,直接cv.imwrite即可。
cv2.imwrite(save_path, crop_img)
第一个参数是保存的路径及文件名,第二个是图像矩阵。其中,imwrite()有个可选的第三个参数,如下:
cv2.imwrite("cat.jpg", img,[int(cv2.IMWRITE_JPEG_QUALITY), 5])
第三个参数针对特定的格式: 对于JPEG,其表示的是图像的质量,用0-100的整数表示,默认为95。 注意,cv2.IMWRITE_JPEG_QUALITY类型为Long,必须转换成int。下面是以不同质量存储的两幅图:
对于PNG,第三个参数表示的是压缩级别。cv2.IMWRITE_PNG_COMPRESSION,从0到9,压缩级别越高,图像尺寸越小。默认级别为3:
cv2.imwrite("./cat.png", img, [int(cv2.IMWRITE_PNG_COMPRESSION), 0]) cv2.imwrite("./cat2.png", img, [int(cv2.IMWRITE_PNG_COMPRESSION), 9])
保存的图像尺寸如下:
还有一种支持的图像,一般不常用。
完整的程序为:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import cv2 # 获取图片
img_path = r'1.jpg'
img = cv2.imread(img_path)
cv2.namedWindow("Image")
cv2.imshow('Image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
最后释放窗口是个好习惯!,查看图片效果如下:
2,转换灰度并去噪声
我们可以得到两张图片,第一张是灰度图,第二章是去噪之后的。去噪有很多种方法,均值滤波法,高斯滤波法,中值滤波器,双边滤波器等。这里展示灰度化,高斯去噪的代码:
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) blurred = cv2.GaussianBlur(gray, (9, 9),0)
下图展示效果,(这里取高斯是因为高斯去噪效果是最好的)
3,提取图像的梯度
用Sobel算子计算x,y方向上的梯度,之后在x方向上 减去 y方向上的梯度,通过这个操作,会留下具有高水平梯度和低垂直梯度的图像区域。
代码入下:
# 提取图像的梯度
gradX = cv2.Sobel(gray, ddepth=cv2.CV_32F, dx=1, dy=0)
gradY = cv2.Sobel(gray, ddepth=cv2.CV_32F, dx=0, dy=1) gradient = cv2.subtract(gradX, gradY)
gradient = cv2.convertScaleAbs(gradient)
此时我们会得到如下的图像:
4,继续去噪声
考虑到图像的孔隙,首先使用低通滤波器平滑图像,这将有助于平滑图像中的高频噪声。低通滤波器的目的是降低图像的变化率。
如果将每个像素替换为该图像周围像素的均值,这样就可以平滑并替代那些强度变化明显的区域。
对模糊图像二值化,顾名思义就是把图像数值以某一边界分成两种数值,梯度图像中不大于90的任何像素都设置为0(黑色)。否则,像素设置为255(白色):
blurred = cv2.GaussianBlur(gradient, (9, 9), 0) (_, thresh) = cv2.threshold(blurred, 90, 255, cv2.THRESH_BINARY)
此时效果如下:
其实就算手动分割,我们也是需要找到一个边界,可以看出轮廓出来了,但是我们最终要的是整个轮廓,所以内部小区域就不要了。
5,图像形态学
在上图中我们看到虫子身体区域有很多黑色的空余,我们要用白色填充这些空余,使得后面的程序更冗余识别虫子的区域,这需要一些形态学方面的操作。
在这里我们选取ELLIPSE核,采用CLOSE操作。
# 图像形态学
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (25, 25)) closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
此时,效果如下:
6,细节刻画
从上图我们可以发现和原图对比,发现有细节丢失,这会干扰之后的昆虫轮廓的检测,要把它们扩充,分别执行4次形态学腐蚀与膨胀,代码如下:
# 细节刻画,分别执行四次形态学腐蚀与膨胀
closed = cv2.erode(closed, None, iterations=4) closed = cv2.dilate(closed, None, iterations=4)
效果如下:
7,找出昆虫区域的轮廓,并画出
此时用 cv2.findContours() 函数如下:
(cnts, _) = cv2.findContours(
参数一: 二值化图像
closed.copy(),
参数二:轮廓类型
#表示只检测外轮廓
# cv2.RETR_EXTERNAL,
#建立两个等级的轮廓,上一层是边界
# cv2.RETR_CCOMP,
#检测的轮廓不建立等级关系
# cv2.RETR_LIST,
#建立一个等级树结构的轮廓
# cv2.RETR_TREE,
#存储所有的轮廓点,相邻的两个点的像素位置差不超过1
# cv2.CHAIN_APPROX_NONE,
参数三:处理近似方法
#例如一个矩形轮廓只需4个点来保存轮廓信息
# cv2.CHAIN_APPROX_SIMPLE,
# cv2.CHAIN_APPROX_TC89_L1,
# cv2.CHAIN_APPROX_TC89_KCOS
)
第一个参数是要检索的图片,必须是二值图,即黑白的(不是灰度图)。
# 这里opencv3返回的是三个参数
(cnts, _) = cv2.findContours(
# 参数一:二值化图像
closed.copy(),
# 参数二:轮廓类型
cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE
)
c = sorted(cnts, key=cv2.contourArea, reverse=True)[0]
rect = cv2.minAreaRect(c)
box = np.int0(cv2.boxPoints(rect)) draw_img = cv2.drawContours(img.copy(), [box], -1, (0, 0, 255), 3)
cv2.imshow("draw_img", draw_img)
此时,会得到:
8,裁剪
图像的裁剪最简单的方式就是获取图像数组的切片,如下:
img_crop = img[100:300,100:300]
cv2.imshow("img_crop", img_crop)
key = cv2.waitKey()
if key == 27:
cv2.destroyAllWindows()
当然,这里我们直接找到四个点,切出来就OK.
其实,box里保存的是绿色矩阵区域四个顶点的坐标。我们按照下图所示裁剪昆虫图像。
方法是找到四个顶点的x,y坐标的最大最小值。新图像的高等于 max(Y) - min(Y),宽等于 max(X) - min(X)。
Xs = [i[0] for i in box]
Ys = [i[1] for i in box]
x1 = min(Xs)
x2 = max(Xs)
y1 = min(Ys)
y2 = max(Ys)
hight = y2 - y1
width = x2 - x1
crop_img= img[y1:y1+hight, x1:x1+width]
cv2.imshow('crop_img', crop_img)
9,完整代码
#-*- coding: UTF-8 -*-
import cv2
import numpy as np def get_image(path):
#获取图片
img=cv2.imread(path)
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) return img, gray def Gaussian_Blur(gray):
# 高斯去噪
blurred = cv2.GaussianBlur(gray, (9, 9),0) return blurred def Sobel_gradient(blurred):
# 索比尔算子来计算x、y方向梯度
gradX = cv2.Sobel(blurred, ddepth=cv2.CV_32F, dx=1, dy=0)
gradY = cv2.Sobel(blurred, ddepth=cv2.CV_32F, dx=0, dy=1) gradient = cv2.subtract(gradX, gradY)
gradient = cv2.convertScaleAbs(gradient) return gradX, gradY, gradient def Thresh_and_blur(gradient): blurred = cv2.GaussianBlur(gradient, (9, 9),0)
(_, thresh) = cv2.threshold(blurred, 90, 255, cv2.THRESH_BINARY) return thresh def image_morphology(thresh):
# 建立一个椭圆核函数
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (25, 25))
# 执行图像形态学, 细节直接查文档,很简单
closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
closed = cv2.erode(closed, None, iterations=4)
closed = cv2.dilate(closed, None, iterations=4) return closed def findcnts_and_box_point(closed):
# 这里opencv3返回的是三个参数
(_, cnts, _) = cv2.findContours(closed.copy(),
cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE)
c = sorted(cnts, key=cv2.contourArea, reverse=True)[0]
# compute the rotated bounding box of the largest contour
rect = cv2.minAreaRect(c)
box = np.int0(cv2.boxPoints(rect)) return box def drawcnts_and_cut(original_img, box):
# 因为这个函数有极强的破坏性,所有需要在img.copy()上画
# draw a bounding box arounded the detected barcode and display the image
draw_img = cv2.drawContours(original_img.copy(), [box], -1, (0, 0, 255), 3) Xs = [i[0] for i in box]
Ys = [i[1] for i in box]
x1 = min(Xs)
x2 = max(Xs)
y1 = min(Ys)
y2 = max(Ys)
hight = y2 - y1
width = x2 - x1
crop_img = original_img[y1:y1+hight, x1:x1+width] return draw_img, crop_img def walk(): img_path = r'C:\Users\aixin\Desktop\chongzi.png'
save_path = r'C:\Users\aixin\Desktop\chongzi_save.png'
original_img, gray = get_image(img_path)
blurred = Gaussian_Blur(gray)
gradX, gradY, gradient = Sobel_gradient(blurred)
thresh = Thresh_and_blur(gradient)
closed = image_morphology(thresh)
box = findcnts_and_box_point(closed)
draw_img, crop_img = drawcnts_and_cut(original_img,box) # 暴力一点,把它们都显示出来看看 cv2.imshow('original_img', original_img)
cv2.imshow('blurred', blurred)
cv2.imshow('gradX', gradX)
cv2.imshow('gradY', gradY)
cv2.imshow('final', gradient)
cv2.imshow('thresh', thresh)
cv2.imshow('closed', closed)
cv2.imshow('draw_img', draw_img)
cv2.imshow('crop_img', crop_img)
cv2.waitKey(20171219)
cv2.imwrite(save_path, crop_img) walk()
附录代码:
# 用来转化图像格式的
img = cv2.cvtColor(src,
COLOR_BGR2HSV # BGR---->HSV
COLOR_HSV2BGR # HSV---->BGR
...)
# For HSV, Hue range is [0,179], Saturation range is [0,255] and Value range is [0,255] # 返回一个阈值,和二值化图像,第一个阈值是用来otsu方法时候用的
# 不过现在不用了,因为可以通过mahotas直接实现
T = ret = mahotas.threshold(blurred)
ret, thresh_img = cv2.threshold(src, # 一般是灰度图像
num1, # 图像阈值
num2, # 如果大于或者num1, 像素值将会变成 num2
# 最后一个二值化参数
cv2.THRESH_BINARY # 将大于阈值的灰度值设为最大灰度值,小于阈值的值设为0
cv2.THRESH_BINARY_INV # 将大于阈值的灰度值设为0,大于阈值的值设为最大灰度值
cv2.THRESH_TRUNC # 将大于阈值的灰度值设为阈值,小于阈值的值保持不变
cv2.THRESH_TOZERO # 将小于阈值的灰度值设为0,大于阈值的值保持不变
cv2.THRESH_TOZERO_INV # 将大于阈值的灰度值设为0,小于阈值的值保持不变
)
thresh = cv2.AdaptiveThreshold(src,
dst,
maxValue,
# adaptive_method
ADAPTIVE_THRESH_MEAN_C,
ADAPTIVE_THRESH_GAUSSIAN_C,
# thresholdType
THRESH_BINARY,
THRESH_BINARY_INV,
blockSize=3,
param1=5
) # 一般是在黑色背景中找白色物体,所以原始图像背景最好是黑色
# 在执行找边缘的时候,一般是threshold 或者是canny 边缘检测后进行的。
# warning:此函数会修改原始图像、
# 返回:坐标位置(x,y),
(_, cnts, _) = cv2.findContours(mask.copy(),
# cv2.RETR_EXTERNAL, #表示只检测外轮廓
# cv2.RETR_CCOMP, #建立两个等级的轮廓,上一层是边界
cv2.RETR_LIST, #检测的轮廓不建立等级关系
# cv2.RETR_TREE, #建立一个等级树结构的轮廓
# cv2.CHAIN_APPROX_NONE, #存储所有的轮廓点,相邻的两个点的像素位置差不超过1
cv2.CHAIN_APPROX_SIMPLE, #例如一个矩形轮廓只需4个点来保存轮廓信息
# cv2.CHAIN_APPROX_TC89_L1,
# cv2.CHAIN_APPROX_TC89_KCOS
)
img = cv2.drawContours(src, cnts, whichToDraw(-1), color, line) img = cv2.imwrite(filename, dst, # 文件路径,和目标图像文件矩阵 # 对于JPEG,其表示的是图像的质量,用0-100的整数表示,默认为95
# 注意,cv2.IMWRITE_JPEG_QUALITY类型为Long,必须转换成int
[int(cv2.IMWRITE_JPEG_QUALITY), 5]
[int(cv2.IMWRITE_JPEG_QUALITY), 95]
# 从0到9,压缩级别越高,图像尺寸越小。默认级别为3
[int(cv2.IMWRITE_PNG_COMPRESSION), 5])
[int(cv2.IMWRITE_PNG_COMPRESSION), 9]) # 如果你不知道用哪个flags,毕竟太多了哪能全记住,直接找找。
寻找某个函数或者变量
events = [i for i in dir(cv2) if 'PNG' in i]
print( events ) 寻找某个变量开头的flags
flags = [i for i in dir(cv2) if i.startswith('COLOR_')]
print flags 批量读取文件名字
import os
filename_rgb = r'C:\Users\aixin\Desktop\all_my_learning\colony\20170629'
for filename in os.listdir(filename_rgb): #listdir的参数是文件夹的路径
print (filename)
参考文献:
https://blog.csdn.net/sunny2038/article/category/904451
https://www.cnblogs.com/zangyu/p/5802142.html
深入学习OpenCV检测及分割图像的目标区域的更多相关文章
- 【OpenCV学习】计算两幅图像的重叠区域
问题描述:已知两幅图像Image1和Image2,计算出两幅图像的重叠区域,并在Image1和Image2标识出重叠区域. 算法思想: 若两幅图像存在重叠区域,则进行图像匹配后,会得到一张完整的全景图 ...
- Halcon学习之六:获取Image图像中Region区域的特征参数
area_center_gray ( Regions, Image : : : Area, Row, Column ) 计算Image图像中Region区域的面积Area和重心(Row,Colu ...
- (转) OpenCV学习笔记大集锦 与 图像视觉博客资源2之MIT斯坦福CMU
首页 视界智尚 算法技术 每日技术 来打我呀 注册 OpenCV学习笔记大集锦 整理了我所了解的有关OpenCV的学习笔记.原理分析.使用例程等相关的博文.排序不分先后,随机整理的 ...
- 深度学习 + OpenCV,Python实现实时视频目标检测
使用 OpenCV 和 Python 对实时视频流进行深度学习目标检测是非常简单的,我们只需要组合一些合适的代码,接入实时视频,随后加入原有的目标检测功能. 在本文中我们将学习如何扩展原有的目标检测项 ...
- <学习opencv>图像和大型阵列类型
OPenCV /*=========================================================================*/ // 图像和大型阵列类型 /* ...
- 【学习opencv第六篇】图像的反转操作
考试终于完了,现在终于有时间可以继续学习这个了.写这篇博客主要是因为以前一直搞不清楚图像数据到底是怎么存储的,以及这个step到底是什么,后来查了一下才知道原来step就是数据行的长度.. #incl ...
- 【学习opencv第七篇】图像的阈值化
图像阈值化的基本思想是,给定一个数组和一个阈值,然后根据数组中每个元素是低于还是高于阈值而进行一些处理. cvThreshold()函数如下: double cvThreshold( CvArr* s ...
- 【从零学习openCV】IOS7根据人脸检测
前言: 人脸检測与识别一直是计算机视觉领域一大热门研究方向,并且也从安全监控等工业级的应用扩展到了手机移动端的app.总之随着人脸识别技术获得突破,其应用前景和市场价值都是不可估量的,眼下在学习ope ...
- OpenCV学习笔记(七) 图像金字塔 阈值 边界
转自: OpenCV 教程 使用 图像金字塔 进行缩放 图像金字塔是视觉运用中广泛采用的一项技术.一个图像金字塔是一系列图像的集合 - 所有图像来源于同一张原始图像 - 通过梯次向下采样获得,直到达到 ...
随机推荐
- Java基础(三) String深度解析
String可以说是Java中使用最多最频繁.最特殊的类,因为同时也是字面常量,而字面常量包括基本类型.String类型.空类型. 一. String的使用 1. String的不可变性 /** * ...
- python列表的内置方法
list数据类型还有更多的方法.这里是list对象的所有方法: list.append(x) 添加一个元素到列表的末尾:相当于a[len(a):] = [x]. list.extend(L) 将指定列 ...
- PHP中关于时间、时区、本地时间、UTC时间、GMT时间、时间戳等知识的梳理
在PHP开发中,我们经常会在时间问题上被搞糊涂,比如我们希望显示一个北京时间,但是当我们使用date函数进行输出时,却发现少了8个小时.几乎所有的php猿类都必须对php中几个重要的时间转换等方法进行 ...
- DNS之缓存服务器部署流程
环境介绍 [root@dns ~]# cat /etc/centos-releaseCentOS release 6.6 (Final)[root@dns ~]# ifconfig eth0|sed ...
- .NET中生成水印更好的方法
.NET中生成水印更好的方法 为了保护知识产权,防止资源被盗用,水印在博客.网店等场景中非常常见. 本文首先演示了基于System.Drawing.Image做正常操作.然后基于Direct2D/WI ...
- 高并发 Nginx+Lua OpenResty系列(7)——Lua开发库json
JSON库 在进行数据传输时JSON格式目前应用广泛,因此从Lua对象与JSON字符串之间相互转换是一个非常常见的功能:目前Lua也有几个JSON库,如:cjson.dkjson.其中cjson的语法 ...
- HBase 学习之路(三)—— HBase基本环境搭建
一.安装前置条件说明 1.1 JDK版本说明 HBase 需要依赖JDK环境,同时HBase 2.0+ 以上版本不再支持JDK 1.7 ,需要安装JDK 1.8+ .JDK 安装方式见本仓库: Lin ...
- 【设计模式】行为型08状态模式(status Pattern)
状态模式(status Pattern) 定义:允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类.其别名为状态对象(Objects for States).与命令模式 ...
- Redis 密码设置和查看密码(二)
Redis 密码设置和查看密码 redis没有实现访问控制这个功能,但是它提供了一个轻量级的认证方式,可以编辑redis.conf配置来启用认证. 1.初始化Redis密码: 在配置文件中有个参数: ...
- 50道SQL练习题及答案与详细分析!!!
以前在学校还没有很认真地意识到,现在到了企业才发现sql是那么的重要,看到网上有很多的sql 练习题,特地拿来练练手! 数据表介绍 --1.学生表 Student(SId,Sname,Sage,Sse ...