(一)前言
在做嵌入式系统的过程中,其实需要自己从零开始写的代码量是比较少的。可以开玩笑的说只需要ctrl+c 和ctrl+v ,但是关键是要寻找所谓一段段代码的“API”接口。特别是在树莓派和jeston nano 这种系统上,很多指令的操作其实压根就不知道干嘛,就是对着教程一顿输入,然后就好了。
实验原理
(二)题目
(三)实现过程
全部直接按照这个: opencv-python——使用mjpg-streamer实现实时视频流获取并进行远程图像处理操作
1.图像传输到网络
树莓派和jetson nano 直接按照里面的命令输入整体步骤
1.1 安装依赖
树莓派
sudo apt-get install subversion libv4l-dev libjpeg62-dev imagemagick libjpeg62-dev libjpeg8-dev
Jetson nano
sudo apt-get install libjpeg8-dev libv4l-dev
1.2. 下载mjpg-streamer包
git clone https://github.com.cnpmjs.org/jacksonliam/mjpg-streamer
1.3. 进入文件夹编译
cd mjpg-streamer/mjpg-streamer-experimental
make
sudo make install
1.4. 运行指令
命令行的参数都可以修改 :
比如设备号/dev/video0 和 设备类型 ./input_uvc.so
-f 30 应该是视频的帧率
(如果固定这个可以通过帧率的计算周期比赛的时候应该是没有输入固定帧率的)
./mjpg_streamer -i "./input_uvc.so -d /dev/video0 -f 30 -r 1280x720" -o "./output_http.so -w ./www"
2.图像接收
# -*- coding:utf-8 -*-
import cv2
import numpy as np
from urllib import request
url = "http://localhost:8080/?action=snapshot"
def downloadImg():
global url
with request.urlopen(url) as f:
data = f.read()
img1 = np.frombuffer(data, np.uint8)
img_cv = cv2.imdecode(img1, cv2.IMREAD_ANYCOLOR)
return img_cv
while True:
image = downloadImg()
cv2.imshow("frame", image)
dst = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # BGR转HSV
cv2.imshow('output', dst)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cv2.destroyAllWindows()
里面只要注意
url = "http://localhost:8080/?action=snapshot"
localhost是树莓派的ip地址
注释:
with as 语句操作上下文管理器(context manager),它能够帮助我们自动分配并且释放资源。
无论期间是否抛出异常,都能保证 with as 语句执行完毕后自动关闭已经打开的文件
with request.urlopen(url) as f:
request.urlopen(url)
首先定义好需要访问的网站网页
然后使用request.urlopen打开相应url并把相应页面作为返回
把返回结果读取出来
示例
import numpy as np
import urllib.request
import cv2
# read the image url
url = 'https:'
with urllib.request.urlopen(url) as resp:
# read image as an numpy array
image = np.asarray(bytearray(resp.read()), dtype="uint8")
# use imdecode function
image = cv2.imdecode(image, cv2.IMREAD_COLOR)
# display image
cv2.imwrite("result.jpg", image)
3.opencv图像处理
利用差帧法进行目标的动态检测,差帧法对背景以及光线的要求没那么高,并且这个代码使用的是前一帧与后一帧的差帧图像。
import cv2 as cv
import numpy as np
flag = 0
while(True):
ret, src = capture.read()
gray = cv.cvtColor(src, cv.COLOR_BGRA2GRAY)
if flag == 0:
imagelast = gray
flag = 1
continue
else:
diff = cv.absdiff(imagelast, gray)
imagelast = gray
#二值化CV_THRESH_BINARY);
res, binary = cv.threshold(diff, 40, 255, cv.THRESH_BINARY)
kernel1 = cv.getStructuringElement(cv.MORPH_RECT, (5, 5))
kernel2 = cv.getStructuringElement(cv.MORPH_RECT, (18, 18))
dst1 = cv.erode(binary, kernel1)
dst2 = cv.dilate(dst1, kernel2)
cv.namedWindow("dst2", cv.WINDOW_AUTOSIZE)
cv.imshow("dst2", dst2)
cloneimage, contours, heriachy = cv.findContours(dst2, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)。
c = max(contours, key=cv.contourArea, default=None)
if c is not None:
rect = cv.minAreaRect(c)
box = cv.boxPoints(rect)
x1 = int((np.int0(box)[0][0]+np.int0(box)[3][0])/2)
x2 = int((np.int0(box)[2][0] + np.int0(box)[1][0])/2)
x = (x1+x2)/2
cv.drawContours(src, [np.int0(box)], -1, (0, 255, 0), 2)
cv.namedWindow("input", cv.WINDOW_AUTOSIZE)
cv.imshow("input", src)
效果图
4.周期T的计算
根据论文《基于图像处理的单摆测量重力加速度实验的研究》中的思路
当时我们我们对这个问题的考虑并不是特别全面,而整个测量系统最关键的地方就是在于我们如何比较准确的计算出周期,这就是直接影响到我们测量的绳长的最关键因素。
4.1计算总时间
要加入一下
import time
定义一个空的组:
k = []
每处理完一帧图像之后将图像的中心点center存入
k.append(center)
长度为1时,是第1帧,长度为100,是第100帧
if len(k) == 1:
t0 = time.time()
if len(k) == 100:
t1 = time.time()
time = t1 - t0
这样就计算出来了,这100帧总共的时间
4.2 运动分析以及选数据点
我们近似认为物体在做简谐运动,也就是说x-t和y-t的图像都应该是类似于正弦函数。对物体进行拍照,处理后图像应该是变成离散的点了,可以理解为x-n和y-n的图像。至于是取x-n还是y-n取决于图像处理的质量,有时候x-n拟合的比较好,有时候y-n拟合的比较好。
如下图:这个图像处理后得到的点拟合出来就比较一般。
下图是当时比较理想的图像处理结果。我记得这个是y-n的图像拟合,而且当时x-n图像拟合的结果如上图类似的状况,并不好。
给出MATLAB的拟合代码
输入x=(x,y)就行了
n1=length(x(:,1));
kk=1:n1;
pp=spline(kk,x(1:n1));
kk1=linspace(0,99,100);
yy = ppval(pp,kk1);
plot(kk1,yy)
hold on
plot(x(1:100),'*')
4.3获取波峰点或波谷点
如上图所示的图像,两个波峰点或波谷点之间的时间即为运动周期T
存储:一系列波峰在第几点
k_num = []
寻找波峰
for i in range(1,99)
if k[i-1]<=k[i] and k[i+1]<=k[i]
k_num.append(i)
4.4计算时间
有几个波峰
n_top = len(k_num)
这几个波峰之间有多少个点(最后一个波峰点的序列号 - 第一个波峰点的序列号)
number_point = k_num[-1] - k_num[0]
周期 = (这N个波峰之间点的个数/100个点)*总时间/(N个波峰-1) time :4.1中已经求出来了
T = (number_point/100)*time/(n_top-1)
(四)改进思路
1.轮廓处理
出现这种情况
方法一:如果进行轮廓的合并处理,就能把这两个轮廓合成一个大的轮廓,再找外接矩形,就能极大的减小这种情况带来的误差。
方法二:找到多个轮廓时,我们会选择采取选择面积最大的轮廓或者周长最长的轮廓。而一般来说同一个物体如果经过图像处理后得到了多个轮廓,那这几个轮廓在y轴的相差量会比较大,而在x轴的相差量会毕竟小。可以结合4.2中的拟合,尽量采用x-n的图像。
2.硬件升级
做一个白色的背景板,将这套系统放置在环境光线稳定的条件下,那么我认为可以不用差帧法,而使用HSV色域的颜色识别,那么准确的将大大提高。
注意:还是找最大轮廓,比较保险。但是其实也无所谓,因为HSV色域的识别在上述条件下我觉得是十分好的方案。当时我们也是理解错题目要求了,以为不能搭建这种完美的识别条件。
import numpy
import cv2 as cv
col_black = (0,0,0,180,255,46)# black
col_red = (0,100,80,10,255,255)# red
col_blue = (90,90,90,110,255,255)# blue
col_green= (65,70,70,85,255,255)# green
col_yellow = (26,43,46,34,255,255)# yellow
capture = cv.VideoCapture(0)
while(True):
ret, src = capture.read()
hsv = cv.cvtColor(src, cv.COLOR_BGR2HSV)
# 颜色的范围 # 第二个参数:lower指的是图像中低于这个lower的值,图像值变为0
# 第三个参数:upper指的是图像中高于这个upper的值,图像值变为0
# 而在lower~upper之间的值变成255
kernel = numpy.ones((5, 5), numpy.uint8)
hsv_erode = cv.erode(hsv, kernel, iterations=1)
hsv_dilate = cv.dilate(hsv_erode, kernel, iterations=1)
lowerbH = col_red[0]
lowerbS = col_red[1]
lowerbV = col_red[2]
upperbH = col_red[3]
upperbS = col_red[4]
upperbV = col_red[5]
mask = cv.inRange(hsv_dilate, (lowerbH, lowerbS, lowerbV), (upperbH, upperbS, upperbV))
# 找轮廓只能找二值化图像
cloneimage, contours, heriachy = cv.findContours(mask, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
#cv.boundingRect计算轮廓的垂直边界最小矩形,矩形是与图像上下边界平行的
bounding_boxes = [cv.boundingRect(cnt) for cnt in contours]
#还是找最大轮廓吧 比较保险 其实也无所谓
for bbox in bounding_boxes:
[x, y, w, h] = bbox
cv.rectangle(src, (x, y), (x + w, y + h), (0, 255, 0), 2)
x1 = x + w/2
y1 = y + h/2
print(x1,y1)
cv.namedWindow("input", cv.WINDOW_AUTOSIZE)
cv.imshow("input", src)
c = cv.waitKey(0)
if c == 27:
break