简体   繁体   English

OpenCV和网络摄像机 - 或者 - 如何监视邻居?

[英]OpenCV and Network Cameras -or- How to spy on the neighbors?


A bit of context; 一点背景; this program was built originally to work with USB cameras - but because of the setup between where the cameras needs to be and where the computer is it makes more sense to switch to cameras run over a network. 这个程序最初是为了与USB摄像头配合使用而设计的 - 但是由于摄像机需要的位置和计算机的位置之间的设置,切换到通过网络运行的摄像机更有意义。 Now I'm trying to convert the program to accomplish this, but my efforts thus far have met with poor results. 现在我正在尝试转换程序以实现这一目标,但到目前为止我的努力都遇到了糟糕的结果。 I've also asked this same question over on the OpenCV forums . 我也在OpenCV论坛上问了同样的问题。 Help me spy on my neighbors! 帮我监视我的邻居! (This is with their permission, of course!) :D (当然,这是他们的许可!):D


I'm using: 我正在使用:

  • OpenCV v2.4.6.0 OpenCV v2.4.6.0
  • C++ C ++
  • D-Link Cloud Camera 7100 (Installer is DCS-7010L, according to the instructions.) D-Link云相机7100(安装程序是DCS-7010L,根据说明。)

I am trying to access the DLink camera's video feed through OpenCV. 我试图通过OpenCV访问DLink相机的视频源。

I can access the camera through it's IP address with a browser without any issues. 我可以使用浏览器通过它的IP地址访问摄像头,没有任何问题。 Unfourtunately; Unfourtunately; my program is less cooperative. 我的计划不太合作。 When attempting to access the camera the program gives the OpenCV-generated error: 尝试访问摄像机时,程序会给出OpenCV生成的错误:

warning: Error opening file (../../modules/highgui/src/cap_ffmpeg_impl.hpp:529) 警告:打开文件时出错(../../modules/highgui/src/cap_ffmpeg_impl.hpp:529)

This error occurs with just about everything I try that doesn't somehow generate more problems. 我尝试的几乎所有事情都会发生这种错误,但不会产生更多问题。

For reference - the code in OpenCV's cap_ffmpeg_impl.hpp around line 529 is as follows: 供参考 - 第529行OpenCV的cap_ffmpeg_impl.hpp中的代码如下:

522    bool CvCapture_FFMPEG::open( const char* _filename )
523    {
524        unsigned i;
525        bool valid = false;
526
527        close();
528
529    #if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0)
530        int err = avformat_open_input(&ic, _filename, NULL, NULL);
531    #else
532        int err = av_open_input_file(&ic, _filename, NULL, 0, NULL);
533    #endif
...
616    }

...for which I have no idea what I'm looking at. ...我不明白我在看什么。 It seems to be looking for the ffmpeg version - but I've already installed the latest ffmpeg on that computer, so that shouldn't be the issue. 它似乎正在寻找ffmpeg版本 - 但我已经在该计算机上安装了最新的ffmpeg,所以这应该不是问题。

This is the edited down version I tried to use as per Sebastian Schmitz's recommendation: 根据Sebastian Schmitz的建议,这是我尝试使用的编辑版本:

 1    #include <fstream>                            // File input/output
 2    #include <iostream>                           // cout / cin / etc
 3    #include <windows.h>                      // Windows API stuff
 4    #include <stdio.h>                            // More input/output stuff
 5    #include <string>                         // "Strings" of characters strung together to form words and stuff
 6    #include <cstring>                            // "Strings" of characters strung together to form words and stuff
 7    #include <streambuf>                      // For buffering load files
 8    #include <array>                          // Functions for working with arrays
 9    #include <opencv2/imgproc/imgproc.hpp>        // Image Processor
10    #include <opencv2/core/core.hpp>          // Basic OpenCV structures (cv::Mat, Scalar)
11    #include <opencv2/highgui/highgui.hpp>        // OpenCV window I/O
12    #include "opencv2/calib3d/calib3d.hpp"
13    #include "opencv2/features2d/features2d.hpp"
14    #include "opencv2/opencv.hpp"
15    #include "resource.h"                     // Included for linking the .rc file
16    #include <conio.h>                            // For sleep()
17    #include <chrono>                         // To get start-time of program.
18    #include <algorithm>                      // For looking at whole sets.
19
20    #ifdef __BORLANDC__
21      #pragma argsused
22    #endif
23
24    using namespace std;                      // Standard operations. Needed for most basic functions.
25    using namespace std::chrono;              // Chrono operations. Needed getting starting time of program.
26    using namespace cv;                           // OpenCV operations. Needed for most OpenCV functions.
27
28    string videoFeedAddress = "";
29    VideoCapture videoFeedIP = NULL;
30    Mat clickPointStorage; //Artifact from original program.
31
32    void displayCameraViewTest()
33    {
34      VideoCapture cv_cap_IP;
35      Mat color_img_IP;
36      int capture;
37      IplImage* color_img;
38      cv_cap_IP.open(videoFeedAddress);
39      Sleep(100);
40      if(!cv_cap_IP.isOpened())
41      {
42          cout << "Video Error: Video input will not work.\n";
43          cvDestroyWindow("Camera View");
44          return;
45      }
46      clickPointStorage.create(color_img_IP.rows, color_img_IP.cols, CV_8UC3);
47      clickPointStorage.setTo(Scalar(0, 0, 0));
48      cvNamedWindow("Camera View", 0); // create window
49      IplImage* IplClickPointStorage = new IplImage(clickPointStorage);
50      IplImage* Ipl_IP_Img;
51      
52      for(;;)
53      {
54          cv_cap_IP.read(color_img_IP);
55          IplClickPointStorage = new IplImage(clickPointStorage);
56          Ipl_IP_Img = new IplImage(color_img_IP);
57          cvAdd(Ipl_IP_Img, IplClickPointStorage, color_img);
58          cvShowImage("Camera View", color_img); // show frame
59          capture = cvWaitKey(10); // wait 10 ms or for key stroke
60          if(capture == 27 || capture == 13 || capture == 32){break;} // if ESC, Return, or space; close window.
61      }
62      cv_cap_IP.release();
63      delete Ipl_IP_Img;
64      delete IplClickPointStorage;
65      cvDestroyWindow("Camera View");
66      return;
67    }
68    
69    int main()
70    {
71      while(1)
72      {
73          cout << "Please Enter Video-Feed Address: ";
74          cin >> videoFeedAddress;
75          if(videoFeedAddress == "exit"){return 0;}
76          cout << "\nvideoFeedAddress: " << videoFeedAddress << endl;
77          displayCameraViewTest();
78          if(cvWaitKey(10) == 27){return 0;}
79      }
80      return 0;
81    }

Using added 'cout's I was able to narrow it down to line 38: "cv_cap_IP.open(videoFeedAddress);" 使用添加的'cout'我可以将其缩小到第38行:“cv_cap_IP.open(videoFeedAddress);”

No value I enter for the videoFeedAddress variable seems to get a different result. 我为videoFeedAddress变量输入的值似乎没有得到不同的结果。 I found THIS site that lists a number of possible addresses to connect to it. 我发现这个站点列出了许多可能的地址来连接它。 Since there exists no 7100 anywhere in the list & considering that the install is labeled "DCS-7010L" I used the addresses found next to the DCS-7010L listings. 由于列表中没有任何地方7100并且考虑到安装标记为“DCS-7010L”,我使用了DCS-7010L列表旁边的地址。 When trying to access the camera most of them can be reached through the browser, confirming that they reach the camera - but they don't seem to affect the outcome when I use them in the videoFeedAddress variable. 当试图访问摄像机时,可以通过浏览器访问它们,确认它们到达摄像机 - 但是当我在videoFeedAddress变量中使用它们时,它们似乎不会影响结果。

I've tried many of them both with and without username:password, the port number (554), and variations on ?.mjpg (the format) at the end. 我已经尝试了很多有和没有用户名:密码,端口号(554),以及最后的。.mjpg(格式)的变体。

I searched around and came across a number of different "possible" answers - but none of them seem to work for me. 我四处搜索并发现了许多不同的“可能”答案 - 但它们似乎都不适合我。 Some of them did give me the idea for including the above username:password, etc stuff, but it doesn't seem to be making a difference. 他们中的一些确实给了我包含上述用户名:密码等内容的想法,但它似乎没有什么区别。 Of course, the number of possible combinations is certainly rather large- so I certainly have not tried all of them (more direction here would be appreciated). 当然,可能的组合数量当然相当大 - 所以我当然没有尝试过所有这些组合(这里有更多的方向值得赞赏)。 Here are some of the links I found: 以下是我发现的一些链接:

  1. This is one of the first configurations my code was in. No dice. 是我的代码所在的第一个配置之一。没有骰子。
  2. This one is talking about files - not cameras. 这个是谈论文件 - 而不是相机。 It also mentions codecs - but I wouldn't be able to watch it in a web browser if that were the problem, right? 它还提到了编解码器 - 但如果这是问题,我将无法在网络浏览器中观看它,对吧? (Correct me if I'm wrong here...) (纠正我,如果我错了......)
  3. This one has the wrong error code/points to the wrong line of code! 这个有错误的错误代码/指向错误的代码行!
  4. This one mentions compiling OpenCV with ffmpeg support - but I believe 2.4.6.0 already comes with that all set and ready! 这个提到用ffmpeg支持编译OpenCV - 但我相信2.4.6.0已经配备了所有设置并准备就绪! Otherwise it's not that different from what I've already tried. 否则它与我已经尝试过的没有什么不同。
  5. Now THIS one appears to be very similar to what I have, but the only proposed solution doesn't really help as I had already located a list of connections. 现在这个看起来与我的非常相似,但唯一提出的解决方案并没有真正帮助,因为我已经找到了一个连接列表。 I do not believe this is a duplicate, because as per THIS meta discussion I had a lot more information and so didn't feel comfortable taking over someone else's question - especially if I end up needing to add even more information later. 我不相信这是重复的,因为根据这个元讨论,我有更多的信息,因此不愿意接管别人的问题 - 特别是如果我最后需要添加更多信息。

Thank you for reading this far. 感谢您阅读此内容。 I realize that I am asking a somewhat specific question - although I would appreciate any advice you can think of regarding OpenCV & network cameras or even related topics. 我意识到我在问一个有点具体的问题 - 尽管我很感激您对OpenCV和网络摄像机甚至相关主题的任何建议。


TLDR: Network Camera and OpenCV are not cooperating. TLDR:网络摄像头和OpenCV没有合作。 I'm unsure if it's the address I'm using to direct the program to the camera or the command I'm using - but no adjustment I make seems to improve the result beyond what I've already done! 我不确定这是否是我用来将程序导向相机或我正在使用的命令的地址 - 但我做的调整似乎没有改善我已经完成的结果! Now my neighbors will go unwatched! 现在我的邻居将无人看见!

There's a number of ways to get the video. 获取视频的方法有很多种。 ffmpeg is not the only way although it's most convenient. ffmpeg不是唯一的方法,尽管它最方便。 To diagnose if ffmpeg is capable of reading the stream, you should use the standalone ffmpeg/ffplay to try to open the url. 要诊断ffmpeg是否能够读取流,您应该使用独立的ffmpeg / ffplay来尝试打开该URL。 If it can open directly, it may be some minor things like url formatting such as double slashes( rtsp://IPADDRESS:554/live1.sdp instead of rtsp://IPADDRESS:554//live1.sdp ). 如果它可以直接打开,它可能是一些小的东西,如网址格式,如双斜线( rtsp://IPADDRESS:554/live1.sdp而不是rtsp://IPADDRESS:554//live1.sdp )。 If it cannot open it directly, it may need some extra commandline switches to make it work. 如果无法直接打开它,可能需要一些额外的命令行开关才能使其正常工作。 Then you would need to modify opencv's ffmpeg implementation @ line 529 to pass options to avformat_open_input . 然后你需要修改opencv的ffmpeg实现@ line 529以将选项传递给avformat_open_input This may require quite bit of tweaking before you can get a working program. 在获得有效的程序之前,这可能需要进行一些调整。

You can also check if the camera provide a http mjpeg stream by consulting it's manual. 您还可以通过查阅相关手册来检查相机是否提供了http mjpeg流。 I do not have the camera you are using. 我没有你正在使用的相机。 So I cannot be of much help on this. 所以我对此无能为力。

Alternatively, I have two suggestions below, which might help you up and running relatively quickly since you mentioned that vlc is working. 或者,我有以下两个建议,这可能会帮助您相对快速地启动和运行,因为您提到vlc正在运行。

method 1 方法1

i assume that you can at least open mjpeg url with your existing opencv/ffmpeg combination. 我假设您至少可以使用现有的opencv / ffmpeg组合打开mjpeg url。 since vlc is working, just use vlc to transcode the video into mjpeg like 由于vlc正在工作,只需使用vlc将视频转码为mjpeg就好

vlc.exe --ignore-config -I dummy rtsp://admin:admin@10.10.204.111 --sout=#transcode"{vcodec=MJPG,vb=5000,scale=1,acodec=none}:std{access=http,m‌​ux=raw,dst=127.0.0.1:9080/frame.mjpg}"

after that use http://127.0.0.1:9080/frame.mjpg to grab the frame using opencv VideoCapture. 之后使用http://127.0.0.1:9080/frame.mjpg使用opencv VideoCapture抓取帧。 this just requires that you have a transcoder program that can convert the incoming stream into mjpeg. 这只需要你有一个转码器程序,可以将传入的流转换为mjpeg。

method 2 方法2

you can also directly use vlc api programmatically. 您也可以通过编程方式直接使用vlc api。 the following piece of code use vlc to grab the frames. 下面的代码使用vlc来抓取帧。 relevant info for compilation 相关的编译信息

  • C:\\Program Files (x86)\\VideoLAN\\VLC\\sdk\\include C:\\ Program Files(x86)\\ VideoLAN \\ VLC \\ sdk \\ include
  • C:\\Program Files (x86)\\VideoLAN\\VLC\\sdk\\lib C:\\ Program Files(x86)\\ VideoLAN \\ VLC \\ sdk \\ lib
  • libvlc.lib,libvlccore.lib libvlc.lib,libvlccore.lib

code

#include "opencv2/highgui/highgui.hpp"
#include <windows.h>
#include <vlc/vlc.h>

using namespace cv;

struct ctx
{
    Mat* image;
    HANDLE mutex;
    uchar* pixels;
};
bool isRunning=true;

Size getsize(const char* path)
{
    libvlc_instance_t *vlcInstance;
    libvlc_media_player_t *mp;
    libvlc_media_t *media;

    const char * const vlc_args[] = {
        "-R",
       "-I", "dummy",
       "--ignore-config",
       "--quiet",

    };
    vlcInstance = libvlc_new(sizeof(vlc_args) / sizeof(vlc_args[0]), vlc_args);
    media = libvlc_media_new_location(vlcInstance, path);
    mp = libvlc_media_player_new_from_media(media);

    libvlc_media_release(media);
    libvlc_video_set_callbacks(mp, NULL, NULL, NULL, NULL);
    libvlc_video_set_format(mp, "RV24",100,100, 100 * 24 / 8); // pitch = width * BitsPerPixel / 8
    libvlc_media_player_play(mp);

    Sleep(2000);//wait a while so that something get rendered so that size info is available
    unsigned int width=640,height=480;
    libvlc_video_get_size(mp,0,&width,&height);


    if(width==0 || height ==0)
    {
        width=640;
        height=480;
    }
    libvlc_media_player_stop(mp);
    libvlc_release(vlcInstance);
    libvlc_media_player_release(mp);
    return Size(width,height);
}


void *lock(void *data, void**p_pixels)
{
    struct ctx *ctx = (struct ctx*)data;
    WaitForSingleObject(ctx->mutex, INFINITE);
    *p_pixels = ctx->pixels;
    return NULL;

}

void display(void *data, void *id){
    (void) data;
    assert(id == NULL);
}

void unlock(void *data, void *id, void *const *p_pixels)
{

    struct ctx *ctx = (struct ctx*)data;
    Mat frame = *ctx->image;
    if(frame.data)
    {
        imshow("frame",frame);
        if(waitKey(1)==27)
        {
            isRunning=false;
            //exit(0);
        }
    }
    ReleaseMutex(ctx->mutex);
}


int main( )
{
    string url="rtsp://admin:admin@10.10.204.111";
    //vlc sdk does not know the video size until it is rendered, so need to play it a bit so that size is     known
    Size sz = getsize(url.c_str());

    // VLC pointers
    libvlc_instance_t *vlcInstance;
    libvlc_media_player_t *mp;
    libvlc_media_t *media;

    const char * const vlc_args[] = {
        "-R",
        "-I", "dummy",
        "--ignore-config", 
        "--quiet", 
    };
    vlcInstance = libvlc_new(sizeof(vlc_args) / sizeof(vlc_args[0]), vlc_args);
    media = libvlc_media_new_location(vlcInstance, url.c_str());
    mp = libvlc_media_player_new_from_media(media);

    libvlc_media_release(media);

    struct ctx* context = ( struct ctx* )malloc( sizeof( *context ) );
    context->mutex = CreateMutex(NULL, FALSE, NULL);
    context->image = new Mat(sz.height, sz.width, CV_8UC3);
    context->pixels = (unsigned char *)context->image->data;

    libvlc_video_set_callbacks(mp, lock, unlock, display, context);
    libvlc_video_set_format(mp, "RV24", sz.width, sz.height, sz.width * 24 / 8); // pitch = width *     BitsPerPixel / 8

    libvlc_media_player_play(mp);
    while(isRunning)
    {
        Sleep(1);
    }

    libvlc_media_player_stop(mp);
    libvlc_release(vlcInstance);
    libvlc_media_player_release(mp);
    free(context);

    return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM