虽说前面已经有了一些经验,但是这里几乎是从零开始代码编写。先是写了一个简单的多线程实时定位物体坐标并显示帧率的代码(esc退出),执行效果不好,还在改进当中;之后又参考一些文章写了一个实时检测和识别物体并显示帧率的代码(q键退出)。
摄像头读取到的有很多帧,但是如果每一帧我们都对其做各种滤波、腐蚀、膨胀等操作,那显然计算是跟不上摄像头捕捉的速率的。
代码目前并不完善,只能实时检测单个物体并输出坐标。
思路就是把摄像头显示的部分放在主线程,然后开启一个线程去处理图像。
from tkinter import Y
import cv2
import threading
import numpy as np
from cv2 import getTickCount, getTickFrequency
#
#from matplotlib import pyplot as plot
from matplotlib import image
cap = cv2.VideoCapture(0) # 创建一个 VideoCapture 对象
# 新线程执行的代码:
def deal(frame):
# 高斯去噪
image = cv2.GaussianBlur(frame, (3, 3), 0)
# 灰度处理
garyimage = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
#图像二值化处理
ret,image = cv2.threshold(garyimage,100,255,cv2.THRESH_BINARY_INV)
# 自适应阈值处理(与上二选一)
#ret,image = cv2.threshold(image, 0, 255, cv2.THRESH_OTSU)
# canny边缘化(与下二选一)
image = cv2.Canny(image, 25, 75);
# sobel算子边缘检测(做了一个y方向的检测)
#Sobel_x = cv2.Sobel(image, cv2.CV_16S, 1, 0)
#Sobel_y = cv2.Sobel(image, cv2.CV_16S, 0, 1)
#absX = cv2.convertScaleAbs(Sobel_x) # 转回uint8
#image = absX
# 闭运算,是白色部分练成整体
kernelX = cv2.getStructuringElement(cv2.MORPH_RECT, (17, 5))
image = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernelX)
# 去除一些小的白点
kernelX = cv2.getStructuringElement(cv2.MORPH_RECT, (20, 1))
kernelY = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 19))
# 膨胀,腐蚀
image = cv2.dilate(image, kernelX)
image = cv2.erode(image, kernelX)
# 腐蚀,膨胀
image = cv2.erode(image, kernelY)
image = cv2.dilate(image, kernelY)
# 中值滤波去除噪点
image = cv2.medianBlur(image, 15)
contours, hierarchy = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 绘制轮廓
image1 = frame.copy()
cv2.drawContours(image1, contours, -1, (0, 255, 0), 5)
#绘制最小外接圆(坐标和半径)
(x,y), radius = cv2.minEnclosingCircle(contours[0])
X = float(x)
Y = float(y)
center = np.int0((x,y))
cv2.putText(frame, '%.2f'%X, (50,50), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA)
cv2.putText(frame, '%.2f'%Y, (50,80), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA)
cv2.circle(frame, tuple(center), int(radius), (255, 255, 255), 2)
cv2.imshow('rawImage', frame)
while cap.isOpened(): # 循环读取每一帧
ret_flag, frame = cap.read() # 返回两个参数,第一个是bool是否正常打开,第二个是照片数组,如果只设置一个则变成一个tuple包含bool和图片
# 主线程
cv2.imshow('rawImage', frame)
t = threading.Thread(target=deal, name='funThread', args=(frame,))
# 子线程
t.start() # 启动线程
t.join() # 调用join方法,主线程等待t线程结束
if cv2.waitKey(1) == 27: # Esc退出
break
cap.release() # 释放摄像头
cv2.destroyAllWindows() # 删除建立的全部窗口
需要安装opencv和imutils
from imutils.video import VideoStream
from imutils.video import FPS
import numpy as np
import argparse
import imutils
import time
import cv2
# 构造参数解析并解析参数
ap = argparse.ArgumentParser()
ap.add_argument("-p", "--prototxt", required=True,
help="path to Caffe 'deploy' prototxt file")
ap.add_argument("-m", "--model", required=True,
help="path to Caffe pre-trained model")
ap.add_argument("-c", "--confidence", type=float, default=0.2,
help="minimum probability to filter weak detections")
args = vars(ap.parse_args())
# 初始化 MobileNet SSD 训练检测的类标签列表,然后为每个类生成一组边界框颜色
CLASSES = ["background", "aeroplane", "bicycle", "bird", "boat",
"bottle", "bus", "car", "cat", "chair", "cow", "diningtable",
"dog", "horse", "motorbike", "person", "pottedplant", "sheep",
"sofa", "train", "tvmonitor"]
COLORS = np.random.uniform(0, 255, size=(len(CLASSES), 3))
# 从磁盘加载我们的序列化模型
print("[INFO] loading model...")
net = cv2.dnn.readNetFromCaffe(args["prototxt"], args["model"])
# 初始化视频流,允许(摄像头)传感器预热,并初始化 FPS 计数器
print("[INFO] starting video stream...")
vs = VideoStream(src=0).start()
time.sleep(2.0)
fps = FPS().start()
# 循环访问视频流中的帧
while True:
#从串接视频流中抓取帧,并将其大小调整为最大宽度为 400 像素
frame = vs.read()
frame = imutils.resize(frame, width=400)
# 抓取框架尺寸并将其转换为 blob
(h, w) = frame.shape[:2]
blob = cv2.dnn.blobFromImage(cv2.resize(frame, (300, 300)),
0.007843, (300, 300), 127.5)
# 通过网络传递 blob 并获得检测和预测
net.setInput(blob)
detections = net.forward()
# 循环访问检测
for i in np.arange(0, detections.shape[2]):
# 提取与预测相关的置信度(即概率)
confidence = detections[0, 0, i, 2]
# 通过确保“置信度”大于最小置信度来过滤掉弱检测
if confidence > args["confidence"]:
# 从“检测”中提取类标签的索引,然后计算对象边界框的 (x, y) 坐标
idx = int(detections[0, 0, i, 1])
box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
(startX, startY, endX, endY) = box.astype("int")
# 画出预测框
label = "{}: {:.2f}%".format(CLASSES[idx],
confidence * 100)
cv2.rectangle(frame, (startX, startY), (endX, endY),
COLORS[idx], 2)
y = startY - 15 if startY - 15 > 15 else startY + 15
cv2.putText(frame, label, (startX, y),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, COLORS[idx], 2)
# 显示窗口
cv2.imshow("Frame", frame)
key = cv2.waitKey(1) & 0xFF
# q退出
if key == ord("q"):
break
# 更新帧率
fps.update()
# 停止计时并输出帧率
fps.stop()
print("[INFO] elapsed time: {:.2f}".format(fps.elapsed()))
print("[INFO] approx. FPS: {:.2f}".format(fps.fps()))
# 清理
cv2.destroyAllWindows()
vs.stop()
之后在终端输入
python real_time_object_detection.py --prototxt MobileNetSSD_deploy.prototxt.txt --model MobileNetSSD_deploy.caffemodel
这是在摄像头上实时显示帧率或者显示视频帧率的代码。
(基于opencv—c/c++)
#include
#include
#include
#include
#include "opencv2/imgproc/imgproc.hpp"
int main(int argc, const char** argv)
{
cv::Mat frame;
// 可从摄像头输入视频流或直接播放视频文件
cv::VideoCapture capture(0);
//cv::VideoCapture capture("vedio1.avi");
double fps;
char string[10]; // 帧率字符串
cv::namedWindow("Camera FPS");
double t = 0;
while (1)
{
t = (double)cv::getTickCount();
if (cv::waitKey(1) == 1) { break; }
if (capture.isOpened())
{
capture >> frame;
// getTickcount函数:返回从操作系统启动到当前所经过的毫秒数
// getTickFrequency函数:返回每秒的计时周期数
// t为该处代码执行所耗的时间,单位为秒,fps为其倒数
t = ((double)cv::getTickCount() - t) / cv::getTickFrequency();
fps = 1.0 / t;
sprintf_s(string, "%.2f", fps); // 帧率保留两位小数
std::string fpsString("FPS:");
fpsString += string; // 在"FPS:"后加入帧率数值字符串
printf("fps: %.2f width:%d height:%d fps:%.2f\n", fps, frame.cols, frame.rows, capture.get(cv::CAP_PROP_FPS));
// 将帧率信息写在输出帧上
cv::putText(frame, // 图像矩阵
fpsString, // string型文字内容
cv::Point(5, 20), // 文字坐标,以左下角为原点
cv::FONT_HERSHEY_SIMPLEX, // 字体类型
0.5, // 字体大小
cv::Scalar(0, 0, 0)); // 字体颜色
cv::imshow("Camera FPS", frame);
char c = cv::waitKey(30); //延时30毫秒
// 注:waitKey延时越长 fps越小 出现跳帧 摄像头显示变卡
if (c == 27) //按ESC键退出
break;
}
else
{
std::cout << "No Camera Input!" << std::endl;
break;
}
}