我和我的团队正在大学开展机器人项目。我们有一个Rasberry Pi,我们附加网络摄像头并使用OpenCV进行图像处理。 Rasberry Pi进行一些处理并与Arduino进行通信,然后Arduino继续做一些有用的事情,比如向前移动机器人,转弯等等。
该项目还涉及制作一个Android应用程序,其中(除其他外)将用于控制机器人并显示安装在机器人上的摄像头的馈送(连接到Rasberry Pi)。我的问题涉及解决问题 - 在我的Android应用程序上显示来自该网络摄像头的图像。
对于通信,我使用了一个WSGI python框架bottlePy(https://bottlepy.org/docs/dev/)。相关代码(在Rasberry Pi上)是:
from bottle import route, run, template
from bottle import static_file
@route('/static/<filename>')
def server_static(filename):
return static_file(filename, root='./')
run(host='0.0.0.0', port=8080)
此代码只是在我打开http://ip:8080/static/image.jpg时返回图像。
现在获取并将图像保存到磁盘,以便服务器可以将其取出,我在Pi上运行另一个脚本:
import numpy as np
import cv2
import time
while 1:
cam = cv2.VideoCapture(1)
s, img = cam.read() # captures image
cv2.imwrite("image.jpg",img,[int(cv2.IMWRITE_JPEG_QUALITY),50])
time.sleep(0.1)
这个脚本既可以单独运行,也可以将此代码移动到我的服务器代码中,但是将它移动到服务器代码可以解决我的问题,我会用我的android代码解释一下。
android上的流不需要非常流畅。但这就是我的所作所为:
我创建了一个runnable并使用一个处理程序每100毫秒调用一次runnable:
runnable = object : Runnable {
override fun run() {
/* do what you need to do */
try {
val performBackgroundTask = DownloadImageTask(findViewById(R.id.imageView))
// PerformBackgroundTask this class is the class that extends AsynchTask
performBackgroundTask.execute("http://$id:8080/static/image.jpg")
} catch (e: Exception) {
// TODO Auto-generated catch block
}
/* and here comes the "trick" */
handler.postDelayed(this, 100)
}
}
我的AsyncTask的代码:
class DownloadImageTask(val bmImage: ImageView) : AsyncTask<String,Void,Bitmap>() {
val tag = "DownloadImageTask"
override fun doInBackground(vararg urls: String?): Bitmap? {
val urldisplay = urls[0]
var mIcon11: Bitmap? = null
try {
val `in` = java.net.URL(urldisplay).openStream()
if (`in` != null) {
mIcon11 = BitmapFactory.decodeStream(`in`)
}
} catch (e: Exception) {
Log.e(tag, e.message)
e.printStackTrace()
}
return mIcon11
}
override fun onPostExecute(result: Bitmap?) {
if (result != null)
bmImage.setImageBitmap(result)
}
}
所以我只是改变了我的图像视图的来源。
现在出现问题,这些东西不同步!我获得了部分图像(图像从下到上变为黑色)。我相信这是因为当服务器发送图像时,脚本会重写图像。基本上,我有一个流工作,但像波动一样,流有坏图像。
我以为我只会在发送之前通过将图像保存代码添加到server_static()函数中来保存图像。但有了这个,服务器需要时间来响应,在它发送图像之前,另一个请求被添加到队列中。然后当我在Android应用程序中停止流时,通过
handler.removeCallbacks(runnable)
服务器仍然会继续发送图像,并且其他任何请求都会被延迟。
我需要一个灵魂来完成这个或完全另一种方式来完成这个任务。请帮忙? (对不起,很长的问题)
答案 0 :(得分:1)
经过大量的反复试验,我为我找到了最佳解决方案。
首先,为了获得一致的Jpeg流,我创建了一个Mjpeg流并使其可以在本地网络(HTTP服务器)上访问。 Mjpeg确保顺序图像的流畅。它比将图像保存到本地存储然后通过HTTP服务器发送要好得多。
可以在此处找到Mjpeg服务器的一个示例代码:https://gist.github.com/n3wtron/4624820,另一个是https://hardsoftlucid.wordpress.com/2013/04/11/mjpeg-server-for-webcam-in-python-with-opencv/。
以上任一示例中的python代码都可以在Rasberry Pi上运行。我更喜欢第二种,因为它允许多次访问流。
现在,对于Android,我使用https://github.com/niqdev/ipcam-view。这使得在Android上流式传输mjpeg流非常容易。但是,我遇到了一些问题。它不适用于所有类型的相机,偶尔会使我的应用程序崩溃。但我相信我可以解决它(改变相机帮助)。此外,我需要在单击按钮时启动和停止流。我还没能做到这一点。它开始但只在我退出该活动时停止。还有其他一些项目可以让Android的mjpg流变容易,如果我找到更好的,我会更新答案。在此之前,这是一个很好的解决方案。
编辑:在Android上显示mjpeg流的另一种更简单的方法: 当我使用WebView时,我得到了最好的结果。要启动流,请使用
webView.loadUrl("http://localhost:8080/cam.mjpeg")
为了更好地适应视频,请使用onCreate功能:
// All code in Kotlin, for Java, use functions instead of preperty access syntax
webView.settings.loadWithOverviewMode = true;
webView.settings.useWideViewPort = true;