OpenCV命令行应用程序无法在macOS Mojave下访问相机

时间:2019-05-10 20:17:12

标签: macos opencv macos-mojave

我无法从命令行OpenCV程序访问iMac相机。 (我正在CodeRunner而不是Xcode上编译和运行程序。)我读到Mojave在Info.plist中需要NSCameraUsageDescription,我认为我已经正确地将其嵌入二进制文件中。我在编译标志中添加了-sectcreate __TEXT __info_plist Info.plistwhich I learned about here),当我运行otool -X -s __TEXT __info_plist videotest | xxd -r(来自同一博客文章)时,它输出:

-?<?xml ve.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>NSCameraUsageDescription</key>
    <string>Uses camera to see vision targets</string>
    <key>NSMicrophoneUsageDescription</key>
    <string>This app requires to access your microphone in order to access the camera</string>
</dict>
</plist>

(我添加了NSMicrophoneUsageDescription,以防它试图与相机一起打开麦克风。)

这是我运行程序时的输出:

OpenCV version 4.1.0-dev
[ INFO:0] global /Users/steve/Documents/GitHub/ssteve-opencv/modules/videoio/src/videoio_registry.cpp (185) VideoBackendRegistry VIDEOIO: Enabled backends(5, sorted by priority): FFMPEG(1000); GSTREAMER(990); AVFOUNDATION(980); CV_IMAGES(970); CV_MJPEG(960)
[ INFO:0] global /Users/steve/Documents/GitHub/ssteve-opencv/modules/videoio/src/backend_plugin.cpp (248) getPluginCandidates VideoIO pluigin (GSTREAMER): glob is 'libopencv_videoio_gstreamer*.dylib', 1 location(s)
[ INFO:0] global /Users/steve/Documents/GitHub/ssteve-opencv/modules/videoio/src/backend_plugin.cpp (256) getPluginCandidates     - /usr/local/lib: 0
[ INFO:0] global /Users/steve/Documents/GitHub/ssteve-opencv/modules/videoio/src/backend_plugin.cpp (259) getPluginCandidates Found 0 plugin(s) for GSTREAMER
OpenCV: not authorized to capture video (status 0), requesting...
OpenCV: camera failed to properly initialize!
Unable to open camera

这意味着它正在请求访问权限,但我没有收到对话框,并且在“系统偏好设置”>“安全和隐私”>“相机”下未列出任何应用程序。

这是我正在运行的程序:

#include <iostream>

#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"

using namespace std;
using namespace cv;

int main(int argc, char *argv[]) {
    cout << "OpenCV version " << CV_VERSION << endl;
    VideoCapture cap;
    cap.open(0);
    if (!cap.isOpened()) {
        cerr << "Unable to open camera\n";
        return -1;
    }

    Mat frame;
    for (;;) {
        cap >> frame;
        if (frame.empty()) {
            cerr << "Got blank frame\n";
            return -1;
        }
        imshow("Live", frame);
        if (waitKey(5) >= 0)
        break;
    }

    return 0;
}

这是编译器调用:

xcrun clang++ -x c++ -lc++ -o "$out" -std=c++11 -I/usr/local/include/opencv4 -lopencv_core -lopencv_highgui -lopencv_imgproc -lopencv_imgcodecs -lopencv_videoio -lopencv_calib3d -lopencv_aruco -lopencv_xfeatures2d -lopencv_features2d -sectcreate __TEXT __info_plist Info.plist "${files[@]}" "${@:1}"

我错过了哪一个难题?

(我知道这类似于Cannot access camera with opencv on Mac Mojave,但这个问题永远不会超出格式错误的plist文件的范围。)


作为对确保ffmpeg看到设备的建议的回应:

$ ffmpeg -hide_banner -f avfoundation -list_devices true -i ""
[AVFoundation input device @ 0x7fed77d16dc0] AVFoundation video devices:
[AVFoundation input device @ 0x7fed77d16dc0] [0] FaceTime HD Camera (Built-in)
[AVFoundation input device @ 0x7fed77d16dc0] [1] Capture screen 0
[AVFoundation input device @ 0x7fed77d16dc0] [2] Capture screen 1
[AVFoundation input device @ 0x7fed77d16dc0] [3] Capture screen 2
[AVFoundation input device @ 0x7fed77d16dc0] AVFoundation audio devices:
[AVFoundation input device @ 0x7fed77d16dc0] [0] Built-in Microphone

8 个答案:

答案 0 :(得分:2)

问题在于,无论出于何种原因,C ++程序均未请求访问相机。我在评论中采纳了@gerwin的建议,以尝试使用Python。从Terminal运行该程序会导致Terminal要求访问摄像机。一旦我同意,从终端运行时,c ++程序便能够访问摄像机。

就CodeRunner而言,我不确定如何使CodeRunner在虚拟环境下运行Python程序,因此我无法运行Python OpenCV程序来要求其访问相机。因此,目前我无法使用CodeRunner运行访问相机的c ++程序。

答案 1 :(得分:2)

这不是最终的解决方案,但我通过安装任何要求访问您的相机的终端应用程序解决了该问题。然后,您的openCv c ++程序将随后获得对FaceTime HD摄像机的访问权限。

例如,您可以通过以下方式安装ImageSnap:

  

简单安装imagesnap

     

imagesnap -w 1 shot.png

然后通过显示的弹出窗口授予摄像机许可。

答案 2 :(得分:2)

我们可以修改TCC.db。在 10.14 或 10.15 中打开 Terminal.app:

/usr/bin/sqlite3 ~/Library/Application\ Support/com.apple.TCC/TCC.db "REPLACE INTO access VALUES('kTCCServiceCamera','com.krill.CodeRunner',0,1,1,NULL,NULL,NULL,'UNUSED',NULL,0,1602129687);"

在 11.x 中:

/usr/bin/sqlite3 ~/Library/Application\ Support/com.apple.TCC/TCC.db "REPLACE INTO access VALUES('kTCCServiceCamera','com.krill.CodeRunner',0,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,0,1608354323);"

答案 3 :(得分:0)

这里有几条评论...

尝试从MacOS开发环境运行OpenCV时看到的错误是:

  

OpenCV:无权捕获视频(状态0),请求...   OpenCV:相机无法正确初始化!打开视频时出错   流或文件程序以退出代码结束:255

我知道这些词源自OpenCV库here.。我最初的想法是这是一个OpenCV问题。经过更多测试,我认为这是另外一回事。正如其他人指出的那样,MacOS的安全性/权限问题。但是,这是摩擦。

如果我转到Mac Apple图标(左上角)->系统偏好设置->安全和隐私,我可以收集很多信息。

Mac Systems Preferences

检查“相机”图标。

Security and Privacy Camera

在我的案例中,这显示了两个需要额外权限才能访问摄像机的应用程序,即Terminal和Virtualbox(不确定浏览器Facetime会发生什么情况?),我确实注意到,Xcode并未列出该列表。

当我单击“麦克风”时,看到列出了不同的应用程序,包括Xcode。

Security and Privacy Microphone

那怎么办?我做了很多测试,包括研究修改Xcode应用程序包的Info.plist(查找程序->应用程序文件夹-> Xcode-> Rt单击,显示包内容。将Info.plist复制到其他位置,通过Xcode对其进行修改,然后重新提交。)注意:在没有保留原始Info.plist副本的情况下,请勿尝试此操作。总失败。添加NSCameraUsageDescription键/值是完全失败的。 Xcode根本不会打开。提醒不要丢失原始的Info.plist。

这整个事情令人困惑。苹果为什么允许我们通过终端而不是Xcode访问摄像机?那里的逻辑是什么?

我肯定希望能够通过代码逐步了解逐帧可能的设计问题。这只是不好玩。

有几件事需要理解。

  1. 是的,在程序成功编译为Unix Executable之后,您可以使用相机在MacOS上运行OpenCV项目。您必须确保在每张照片的安全性和隐私权中设置了终端机的权限。显然,您是在开发工具(在我的情况下为Xcode)中构建可执行文件,然后从项目的Build / Debug文件夹中打开可执行文件。该应用程序将在终端窗口中打开,并且可以正常运行,如SSteve所述。

  2. 如果您确实要进行视频/摄像机调试,则可以选择“预先录制”视频,然后在开发环境中打开该视频。此时,您可以使用调试器。你们如何进行逐帧分析?这是我知道的至少部分起作用的唯一方法。

  3. (编辑更新5/22/19 ...)哇。我刚刚意识到..您可以将调试器附加到正在运行的(终端)进程。您可以使用摄像头完全进行逐帧调试(只要程序编译为功能可执行文件即可。)现在,这非常酷,并且可以使我达到98%的功能。为此,请启动终端可执行文件,然后转到Xcode->调试->附加到进程。选择正在运行的应用程序,将Breakpoints添加到源代码中,然后调试/逐步进行。效果很好。

我使用以下命令启动OpenCV项目:

int main(int argc, char** argv){
    // Parse command line arguments
    CommandLineParser parser(argc,argv,keys);

    // Create a VideoCapture object & open the input file
    VideoCapture cap;
    if (parser.has("video")){
        cap.open(parser.get<String>("video"));
    }
    else
        cap.open(0);
   ...

这是一个变通办法,但是总比没有好。 (当然,希望苹果公司将相机包含在iOS仿真器中,这是解决该问题的另一种方法。)显然,这很大程度上取决于您要处理项目的位置。最终,我需要我的设备才能在iPad上运行;在MacOS上进行验证,然后在Swift中包装代码,等等...

作为参考,我使用的是macOS Mojave,10.14.4,MacBook 2.7GHz i7

PS。上面的安全偏好设置不会显示具有“摄像头”访问权限的Chrome。似乎很奇怪。我刚刚在Chrome的this site...上对相机进行了测试,它会征得用户的许可,并且完全可以正常工作。目前尚不清楚发生了什么。

PS2。我是这个问题上唯一file a bug report的人吗?包含链接以方便您。谢谢。

答案 4 :(得分:0)

我为此找到了解决方法

首先,重置相机的规则:

tccutil reset Camera

接下来,我运行了第3方软件来从终端访问摄像机。通过运行以下命令:

brew install imagesnap
imagesnap -w 1 snapshot.png

有人问我是否要允许终端访问我的相机。我单击“是”。现在我的C ++程序现在可以从终端访问摄像机了。

注意:ZipZit中显示的图片非常相似,除了我的相机下没有列出终端。

但是运行第三方程序后。然后将其添加到列表中。

答案 5 :(得分:0)

我们在opencv 4.1.1-pre上运行此问题。我们通过回滚到4.0.1解决了该问题。

答案 6 :(得分:0)

版本: XCode 10.3 MacOS Mohave 10.14.6 OpenCV 4.1.1_2

OpenCV项目位于C++

将此类添加到您的项目中:

标题(.h):

class CameraIssue {


public:
    CameraIssue() {}
    ~CameraIssue() {}

    bool dealWithCamera();
};

.mm文件。请注意,它不是.cpp,而是.mm,因为我们要使用AVFoundation

bool CameraIssue::dealWithCamera()
{
    AVAuthorizationStatus st = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
    if (st == AVAuthorizationStatusAuthorized) {
        return true;
    }

    dispatch_group_t group = dispatch_group_create();

    __block bool accessGranted = false;

    if (st != AVAuthorizationStatusAuthorized) {
        dispatch_group_enter(group);
        [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {

            accessGranted = granted;
            NSLog(@"Granted!");
            dispatch_group_leave(group);
        }];
    }

    dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)));

    return accessGranted;
}

在访问VideoCapture之前,请像这样调用此方法:

CameraIssue _camIssue;
_camIssue.dealWithCamera(); //do whatever you need with bool return

您可能想知道-为什么在使用C++扩展名(Objective-C++)的同时创建.mm类?

为了创建Objective-C类,我可能需要导入Foundation框架并进行导入,这给了我很多有关重复符号的错误,因为我正在使用Foundation和第3方库很多名字。因此,我创建了C++类,但扩展了.mm,因此我可以导入AVFoundation框架并授予摄像机访问权限。

方法dealWithCamera()远非完美,但完全满足我的需求。随意扩展,优化,添加回调等。

答案 7 :(得分:-1)

我终于能够通过遵循Stackoverflow和GitHub上的一系列建议来解决这个问题。这是一个痛苦的错误,即使在MacOS Mojave之前运行良好,我也无法恢复代码正常工作。

解决方案

Info.plist字段的NSCameraUsageDescription文件作为suggested放入目标的Products / Build目录中(在Xcode项目的左窗格中右键单击Product,然后单击“在Finder中显示” ”)。

  • 通过将Info.plist复制/粘贴到Build目录(此suggestion之后)的过程自动化,方法是将其添加到“目标”的Copy Files下的Build Phases列表中”,然后将目标更改为“产品目录”,将子路径更改为“。”

结果

  • 目标的Unix可执行二进制文件随后将请求访问摄像机的许可,并且在同意后,该二进制文件将被添加到System Preferences > Privacy > Camera中允许访问摄像机的应用程序列表中。
    • 仅供参考:要强制清除此列表,请在tccutil reset Camera中输入Terminal
  • 在提示您获得许可/访问摄像机之前,您可能需要运行Target几次。

问题

实例化cv::VideoCapture(0)对象以访问摄像机视频流,即使代码在Mojave之前的MacOS版本中运行正常,也会引发以下错误

  

OpenCV:无权捕获视频(状态0),请求...
  OpenCV:相机无法正确初始化!

原因

MacOS Mojave加强了隐私保护,现在要求应用程序在访问摄像头之前明确提示并寻求许可,如here所述。

建议无效

以下各种Stackoverflow posts中给出的建议未能成功地强制生成的二进制文件提示访问摄像机的权限: -将Info.plist添加到您的Project目录 -在Info.plist 下将路径设置为Build Settings > Packaging > Info.plist File -在目标的General > Identity > Choose Info.plist File...中选择它

可能有帮助的建议

opencv已关闭的GitHub issue中所述,在libopencv上进行了一些更改(19年4月左右),这也可能促进了可用{{1}在构建目录中}以提示用户许可访问摄像机。因此,我还使用Info.plistopencv升级到了最新的稳定4.1.0版本。

P.S。我正在运行MacOS Mojave brew upgrade,Xcode 10.14.5和OpenCV 10.2.1