第八节,Opencv的基本使用------存取图像、视频功能、简单信息标注工具
1、存取图像
import cv2
img=cv2.imread('test.jpg')
cv2.imwrite('test1.jpg',img)
2、图像的仿射变换
图像的仿射变换涉及图像的形状位置角度的变化,是深度学习预处理中常用的功能,仿射变换具体到图像中的应用,主要是对图像的缩放、旋转、剪切、翻转和平移的组合。
3、视频功能
两个模块:一个是VideoCapture,用于获取相机设备并捕获图像和视频,或是从文件中捕获
一个是VideoWriter,用于生成视频。
import cv2
import time
interval=60 #捕获图像的间隔,单位:秒
num_frames=500 #捕获图像的总帧数
out_fps=24 #输出文件的帧率
# 打开默认的相机
cap=cv2.VideoCapture(0)
#捕获分辨率
size=(int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
# 设置要保存视频的编码,分辨率和帧率
video=cv2.VideoWriter(
"time_lapse.avi",
cv2.VideoWriter_fourcc('M','P','',''),
out_fps,
size
)
#对于一些低画质的摄像头,前面的帧可能不稳定,略过
for i in range(42):
cap.read()
#开始捕获,通过read函数获取捕获的帧
try:
for i in range(num_frames):
_,frame=cap.read()
video.write(frame)
#如果希望每一帧也存成文件,比如制作GIF,则取消下面的注释
filename='{:0>6d}.png'.format(i)
cv2.imwrite(filename,frame)
print('frame {} is captured.'.format(i))
time.sleep(interval)
except KeyboardInterrupt:
#提前停止捕获
print('Stopped! {}/{} frames captures!'.format(i,num_frames))
#释放资源并写入视频文件
video.release()
cap.release()
KeyboardInterrupt是一个常用的异常,用来获取用户Ctrl+C的中止,捕获这个异常后直接结束循环并释放VideoCapture和VideoWriter的资源,使已经捕获好的部分视频可以顺利生成。
4、物体检测标注
- 输入使一个文件夹的路径,下面包含了所有要标注物体框的图片,如果图片中标注了物体,则生成一个名称相同加额外后缀名的文件保存标注信息。
- 标注的方式是按下鼠标左键选择物体框的左上角,松开鼠标左键选择物体框的右下角,按下鼠标右键删除上一个标注好的物体框。所有待标注物体的类别和标注框颜色由用户自定义,如果没有定义则默认只标注一种物体,定义该物体名称为Object
- 方向键《——和——》用来遍历图片,上下键用来选择当前要标注的物体,DELETE键删除一张图片和对应的标注信息。
import os
import cv2 # tkinter是Python内置的简单GUI库,实现一些比如打开文件夹,确认删除等操作十分方便
from tkinter.filedialog import askdirectory
from tkinter.messagebox import askyesno # 定义标注窗口的默认名称
WINDOW_NAME = 'Simple Bounding Box Labeling Tool' # 定义画面刷新的大概帧率(是否能达到取决于电脑性能)
FPS = 24 # 定义支持的图像格式
SUPPOTED_FORMATS = ['jpg', 'jpeg', 'png'] # 定义默认物体框的名字为Object,颜色蓝色,当没有用户自定义物体时用默认物体
DEFAULT_COLOR = {'Object': (255, 0, 0)} # 定义灰色,用于信息显示的背景和未定义物体框的显示
COLOR_GRAY = (192, 192, 192) # 在图像下方多出BAR_HEIGHT这么多像素的区域用于显示文件名和当前标注物体等信息
BAR_HEIGHT = 16 # 上下左右,ESC及删除键对应的cv.waitKey()的返回值
# 注意这个值根据操作系统不同有不同,可以通过6.4.2中的代码获取
KEY_UP = 65362
KEY_DOWN = 65364
KEY_LEFT = 65361
KEY_RIGHT = 65363
KEY_ESC = 27
KEY_DELETE = 65535 # 空键用于默认循环
KEY_EMPTY = 0 get_bbox_name = '{}.bbox'.format # 定义物体框标注工具类
class SimpleBBoxLabeling: def __init__(self, data_dir, fps=FPS, window_name=None):
self._data_dir = data_dir
self.fps = fps
self.window_name = window_name if window_name else WINDOW_NAME # pt0是正在画的左上角坐标,pt1是鼠标所在坐标
self._pt0 = None
self._pt1 = None # 表明当前是否正在画框的状态标记
self._drawing = False # 当前标注物体的名称
self._cur_label = None # 当前图像对应的所有已标注框
self._bboxes = [] # 如果有用户自定义的标注信息则读取,否则用默认的物体和颜色
label_path = '{}.txt'.format(self._data_dir)
if not os.path.exists(label_path):
self.label_colors=DEFAULT_COLOR
else:
self.label_colors=self.load_labels(label_path)
#self.label_colors = DEFAULT_COLOR if not os.path.exists(label_path) else self.load_labels(label_path+'.labels') # 获取已经标注的文件列表和还未标注的文件列表
imagefiles = [x for x in os.listdir(self._data_dir) if x[x.rfind('.') + 1:].lower() in SUPPOTED_FORMATS]
labeled = [x for x in imagefiles if os.path.exists(get_bbox_name(x))]
to_be_labeled = [x for x in imagefiles if x not in labeled] # 每次打开一个文件夹,都自动从还未标注的第一张开始
self._filelist = labeled + to_be_labeled
self._index = len(labeled)
if self._index > len(self._filelist) - 1:
self._index = len(self._filelist) - 1 # 鼠标回调函数
def _mouse_ops(self, event, x, y, flags, param): # 按下左键时,坐标为左上角,同时表明开始画框,改变drawing标记为True
if event == cv2.EVENT_LBUTTONDOWN:
self._drawing = True
self._pt0 = (x, y) # 左键抬起,表明当前框画完了,坐标记为右下角,并保存,同时改变drawing标记为False
elif event == cv2.EVENT_LBUTTONUP:
self._drawing = False
self._pt1 = (x, y)
self._bboxes.append((self._cur_label, (self._pt0, self._pt1))) # 实时更新右下角坐标方便画框
elif event == cv2.EVENT_MOUSEMOVE:
self._pt1 = (x, y) # 鼠标右键删除最近画好的框
elif event == cv2.EVENT_RBUTTONUP:
if self._bboxes:
self._bboxes.pop() # 清除所有标注框和当前状态
def _clean_bbox(self):
self._pt0 = None
self._pt1 = None
self._drawing = False
self._bboxes = [] # 画标注框和当前信息的函数
def _draw_bbox(self, img): # 在图像下方多出BAR_HEIGHT这么多像素的区域用于显示文件名和当前标注物体等信息
h, w = img.shape[:2]
canvas = cv2.copyMakeBorder(img, 0, BAR_HEIGHT, 0, 0, cv2.BORDER_CONSTANT, value=COLOR_GRAY) # 正在标注的物体信息,如果鼠标左键已经按下,则显示两个点坐标,否则显示当前待标注物体的名称
label_msg = '{}: {}, {}'.format(self._cur_label, self._pt0, self._pt1) \
if self._drawing \
else 'Current label: {}'.format(self._cur_label) # 显示当前文件名,文件个数信息
msg = '{}/{}: {} | {}'.format(self._index + 1, len(self._filelist), self._filelist[self._index], label_msg)
cv2.putText(canvas, msg, (1, h + 12),
cv2.FONT_HERSHEY_SIMPLEX,
0.5, (0, 0, 0), 1) # 画出已经标好的框和对应名字
for label, (bpt0, bpt1) in self._bboxes:
label_color = self.label_colors[label] if label in self.label_colors else COLOR_GRAY
cv2.rectangle(canvas, bpt0, bpt1, label_color, thickness=2)
cv2.putText(canvas, label, (bpt0[0] + 3, bpt0[1] + 15),
cv2.FONT_HERSHEY_SIMPLEX,
0.5, label_color, 2) # 画正在标注的框和对应名字
if self._drawing:
label_color = self.label_colors[self._cur_label] if self._cur_label in self.label_colors else COLOR_GRAY
if self._pt1[0] >= self._pt0[0] and self._pt1[1] >= self._pt0[1]:
cv2.rectangle(canvas, self._pt0, self._pt1, label_color, thickness=2)
cv2.putText(canvas, self._cur_label, (self._pt0[0] + 3, self._pt0[1] + 15),
cv2.FONT_HERSHEY_SIMPLEX,
0.5, label_color, 2)
return canvas # 利用repr()导出标注框数据到文件
@staticmethod
def export_bbox(filepath, bboxes):
if bboxes:
with open(filepath, 'w') as f:
for bbox in bboxes:
line = repr(bbox) + '\n'
f.write(line)
elif os.path.exists(filepath):
os.remove(filepath) # 利用eval()读取标注框字符串到数据
@staticmethod
def load_bbox(filepath):
bboxes = []
with open(filepath, 'r') as f:
line = f.readline().rstrip()
while line:
bboxes.append(eval(line))
line = f.readline().rstrip()
return bboxes # 利用eval()读取物体及对应颜色信息到数据
@staticmethod
def load_labels(filepath):
label_colors = {}
with open(filepath, 'r') as f:
line = f.readline().rstrip()
while line:
label, color = eval(line)
label_colors[label] = color
line = f.readline().rstrip()
return label_colors # 读取图像文件和对应标注框信息(如果有的话)
@staticmethod
def load_sample(filepath):
img = cv2.imread(filepath)
bbox_filepath = get_bbox_name(filepath)
bboxes = []
if os.path.exists(bbox_filepath):
bboxes = SimpleBBoxLabeling.load_bbox(bbox_filepath)
return img, bboxes # 导出当前标注框信息并清空
def _export_n_clean_bbox(self):
bbox_filepath = os.sep.join([self._data_dir, get_bbox_name(self._filelist[self._index])])
self.export_bbox(bbox_filepath, self._bboxes)
self._clean_bbox() # 删除当前样本和对应的标注框信息
def _delete_current_sample(self):
filename = self._filelist[self._index]
filepath = os.sep.join([self._data_dir, filename])
if os.path.exists(filepath):
os.remove(filepath)
filepath = get_bbox_name(filepath)
if os.path.exists(filepath):
os.remove(filepath)
self._filelist.pop(self._index)
print('{} is deleted!'.format(filename)) # 开始OpenCV窗口循环的方法,定义了程序的主逻辑
def start(self): # 之前标注的文件名,用于程序判断是否需要执行一次图像读取
last_filename = '' # 标注物体在列表中的下标
label_index = 0 # 所有标注物体名称的列表
labels = list(self.label_colors.keys()) # 待标注物体的种类数
n_labels = len(labels) # 定义窗口和鼠标回调
cv2.namedWindow(self.window_name)
cv2.setMouseCallback(self.window_name, self._mouse_ops)
key = KEY_EMPTY # 定义每次循环的持续时间
delay = int(1000 / FPS) # 只要没有按下Esc键,就持续循环
while key != KEY_ESC: # 上下键用于选择当前标注物体
if key == KEY_UP:
if label_index == 0:
pass
else:
label_index -= 1 elif key == KEY_DOWN:
if label_index == n_labels - 1:
pass
else:
label_index += 1 # 左右键切换当前标注的图片
elif key == KEY_LEFT:
# 已经到了第一张图片的话就不需要清空上一张
if self._index > 0:
self._export_n_clean_bbox() self._index -= 1
if self._index < 0:
self._index = 0 elif key == KEY_RIGHT:
# 已经到了最后一张图片的话就不需要清空上一张
if self._index < len(self._filelist) - 1:
self._export_n_clean_bbox() self._index += 1
if self._index > len(self._filelist) - 1:
self._index = len(self._filelist) - 1 # 删除当前图片和对应标注信息
elif key == KEY_DELETE:
if askyesno('Delete Sample', 'Are you sure?'):
self._delete_current_sample()
key = KEY_EMPTY
continue # 如果键盘操作执行了换图片,则重新读取,更新图片
filename = self._filelist[self._index]
if filename != last_filename:
filepath = os.sep.join([self._data_dir, filename])
img, self._bboxes = self.load_sample(filepath) # 更新当前标注物体名称
self._cur_label = labels[label_index] # 把标注和相关信息画在图片上并显示指定的时间
canvas = self._draw_bbox(img)
cv2.imshow(self.window_name, canvas)
key = cv2.waitKey(delay) # 当前文件名就是下次循环的老文件名
last_filename = filename print('Finished!') cv2.destroyAllWindows()
# 如果退出程序,需要对当前进行保存
self.export_bbox(os.sep.join([self._data_dir, get_bbox_name(filename)]), self._bboxes) print('Labels updated!') if __name__ == '__main__':
dir_with_images = askdirectory(title='Where are the images?')
labeling_task = SimpleBBoxLabeling(dir_with_images)
labeling_task.start()
第八节,Opencv的基本使用------存取图像、视频功能、简单信息标注工具的更多相关文章
- 【OpenCV入门教程之三】 图像的载入,显示和输出 一站式完全解析(转)
本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/20537737 作者:毛星云(浅墨) ...
- (原)使用opencv的warpAffine函数对图像进行旋转
转载请注明出处: http://www.cnblogs.com/darkknightzh/p/5070576.html 参考网址: http://stackoverflow.com/questions ...
- OpenCV 编程简单介绍(矩阵/图像/视频的基本读写操作)
PS. 因为csdn博客文章长度有限制,本文有部分内容被截掉了.在OpenCV中文站点的wiki上有可读性更好.而且是完整的版本号,欢迎浏览. OpenCV Wiki :<OpenCV 编程简单 ...
- OpenCV成长之路:图像直方图的应用
OpenCV成长之路:图像直方图的应用 2014-04-11 13:57:03 标签:opencv 图像 直方图 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否 ...
- OpenCV成长之路:图像滤波
http://ronny.blog.51cto.com/8801997/1394138 OpenCV成长之路:图像滤波 2014-04-11 14:28:44 标签:opencv 边缘检测 sobel ...
- 基于Python & Opencv 图像-视频-处理算法
Alg1:图像数据格式之间相互转换.png to .jpg(其他的请举一反三) import cv2 import glob def png2jpg(): images = glob.glob('*. ...
- Delphi存取图像完整解决方案
http://blog.sina.com.cn/s/blog_693cf1cf0100plkq.html 对于涉及图像数据的数据库应用程序,图像数据的存取技术是一个关键.由于缺少技术文档及DEMO例程 ...
- OpenCV 鼠标手动绘制掩码图像
OpenCV 鼠标手动绘制掩码图像 完整的代码: #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui ...
- 【opencv学习笔记六】图像的ROI区域选择与复制
图像的数据量还是比较大的,对整张图片进行处理会影响我们的处理效率,因此常常只对图像中我们需要的部分进行处理,也就是感兴趣区域ROI.今天我们来看一下如何设置图像的感兴趣区域ROI.以及对ROI区域图像 ...
随机推荐
- eclipse换工作空间站快捷键失效解决
1.找到你可以用快捷键的eclipse的空间所在目录.2.复制.metadata文件.3.找到不可用快捷键的空间目录,把之前复制的文件夹覆盖到现在的.4.重启eclipse.
- flask(一)之路由和视图
01-介绍 Flask 是一个 Python 实现的 Web 开发微框架,同时具有很强的扩展能力. 02-第一个flask程序 # 初始化 from flask import Flask, url_f ...
- JShell脚本工具
JShell脚本工具是JDK9的新特性 什么时候会用到 JShell 工具呢,当我们编写的代码非常少的时候,而又不愿意编写类,main方法,也不愿意去编译和运行,这个时候可以使用JShell工具.启动 ...
- 解决Jenkins中执行jmeter脚本后不能发报告(原报告被覆盖、新报告无法保存)的问题
我没有找到根本原因,但是我用了个取巧的办法: 先将原来的报告移到别的文件夹,执行完jmeter脚本后,再把那些旧报告移回来(也可以不移回来,我这里是为了能从jenkins页面上看).
- 访问docker仓库
仓库(Repositiry)是集中存放镜像的地方,分为公共仓库和私有仓库.一个容易与之混淆的概念是注册服务器(Registry).实际上注册服务器是存放仓库的具体服务器,一个注册服务器上可以有多个仓库 ...
- Linux 学习 (六) 关机与重启命令
Linux达人养成计划 I 学习笔记 shutdown [选项] 时间 -c:取消前一个关机命令 -h:关机 -r:重启 shutdown命令会在关机或重启时自动保存系统中正在运行的服务,最安全的关机 ...
- 一个很变态的SQL
select max(s.operat_time) as pzTime from ws_state_record s where s.status = (select p1.node_id from ...
- jQUERY中的属性获取
jQuery获取Select选择的Text和Value:语法解释:1. $("#select_id").change(function(){//code...}); //为Se ...
- jzoj6101. 【GDOI2019模拟2019.4.2】Path
题目链接:https://jzoj.net/senior/#main/show/6101 记\(f_i\)为从\(i\)号点走到\(n\)号点所花天数的期望 那么根据\(m\)条边等可能的出现一条和一 ...
- jzoj6099. 【GDOI2019模拟2019.4.1】Dist
题目链接:https://jzoj.net/senior/#main/show/6099 考虑直接统计某个点到其它所有点的距离和 我们先把整个团当成一个点建图,处理出任意两个团之间的距离\(dis(i ...