使用http服务器

时间:2018-02-11 21:19:48

标签: android python opencv raspberry-pi bottle

我和我的团队正在大学开展机器人项目。我们有一个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)

服务器仍然会继续发送图像,并且其他任何请求都会被延迟。

我需要一个灵魂来完成这个或完全另一种方式来完成这个任务。请帮忙? (对不起,很长的问题)

1 个答案:

答案 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;