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

OpenCV command line app can't access camera under macOS Mojave

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

-?<?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

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

这是我正在 运行ning 的程序:

#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

问题是 c++ 程序出于某种原因没有请求访问摄像头。我在评论中听取了@gerwin 的建议,尝试使用 Python。 运行 来自终端的程序导致终端要求访问相机。一旦我承认这一点,当 运行 来自终端时,c++ 程序就能够访问相机。

至于 CodeRunner,我不确定如何让 CodeRunner 在虚拟环境下 运行 Python 程序,所以我无法 运行 Python OpenCV 程序让它请求相机访问。所以目前我无法使用 CodeRunner 运行 访问相机的 c++ 程序。

这里有几条评论...

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

OpenCV: not authorized to capture video (status 0), requesting... OpenCV: camera failed to properly initialize! Error opening video stream or file Program ended with exit code: 255

我知道这些词来自 OpenCV 库 here. 我最初的想法是这是一个 OpenCV 问题。通过更多的测试,我认为这是另一回事。正如其他人所指出的,MacOS 安全/权限问题。但这就是问题所在。

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

检查相机图标。

在我的例子中,这显示了两个需要额外权限才能访问相机、终端和 Virtualbox 的应用程序(不确定浏览器、Facetime 会发生什么?)我确实注意到,Xcode 没有这个列表。

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

这到底是怎么回事?我确实做了很多测试,包括研究修改 Xcode 应用程序包的 Info.plist (Finder --> Applications Folder --> Xcode --> Rt click, Show Package内容。复制 Info.plist 将其保存在别处,通过 Xcode 修改它,重新提交。)注意:如果没有保留原始 Info.plist 的副本,请不要尝试此操作。完全失败。添加 NSCameraUsageDescription key/value 完全失败了。 Xcode 根本打不开。提醒不要丢失原件 Info.plist。

这整件事令人费解。为什么 Apple 允许我们通过终端访问相机而不是 Xcode?这是什么逻辑?

我当然希望能够通过代码逐帧理解可能的设计问题。这一点都不好玩。

所以有几件事需要理解。

  1. 是的,在程序成功编译为 Unix 可执行文件后,您可以 运行 使用相机在 MacOS 上创建一个 OpenCV 项目。您必须确保根据上面的照片在安全和隐私中设置了终端的权限。显然,您在开发工具中构建可执行文件(在我的例子中是 Xcode),然后从项目 Build/Debug 文件夹中打开可执行文件。该应用程序在终端 window 中打开并且工作正常,正如 SSteve 所指出的那样。

  2. 如果您真的想进行一些视频/相机调试,您可以选择 "pre-record" 一个视频,然后在您的开发环境中打开该视频。此时您可以使用调试器。你们是怎么逐帧分析的?这是我所知道的至少部分有效的唯一方法。

  3. (编辑更新 5/22/19...)哇。我刚刚意识到.. 您可以将调试器附加到 运行ning(终端)进程。你完全可以逐帧调试,使用相机(只要程序编译成一个功能可执行文件。)现在这很酷,让我达到 98% 的功能。为此,启动终端可执行文件,然后转至 Xcode --> Debug --> Attach to Process。 Select 运行ning 应用程序,向源代码和 debug/step 添加断点。效果很好。

我开始我的 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);
   ...

这是一个变通办法,但聊胜于无。 (当然希望 Apple 在 iOS 模拟器中包含摄像头,这将是解决此问题的另一种方法,叹息。)显然,很大程度上取决于您的项目进展情况。最终我需要我的 运行 on an iPad;在 MacOS 上证明,然后在 Swift 中包装代码,等等...

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

PS。上面的安全首选项不显示 Chrome 具有相机访问权限。似乎很奇怪。我刚刚在 Chrome 中的 this site... 测试了相机,它请求许可并按预期工作。目前尚不清楚这里发生了什么。

PS2。在这个问题上我是唯一 file a bug report 的人吗? Link 包含在内是为了您的方便。谢谢。

通过遵循 Whosebug 和 GitHub 中的一系列建议,我终于能够解决这个问题。这是一个痛苦的错误,让我的一天都在试图让我的代码再次工作,即使它在 MacOS Mojave 之前运行良好。

解决方案

将带有 NSCameraUsageDescription 字段的 Info.plist 文件作为 suggested 放入 Target 的 Products/Build 目录中(右键单击左窗格中的产品 Xcode 项目并单击 "Show in Finder")。

  • 将 copy/pasting Info.plist 的这个过程自动添加到你的 Build 目录(在这个 之后),方法是将它添加到 Build Phases 下的 Copy Files 列表中您的 "Target" 并将目标更改为 "Products Directory" 并将子路径更改为“.”

结果

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

问题

实例化 cv::VideoCapture(0) 对象以访问相机视频流会抛出以下错误,即使代码在 Mojave

之前的 MacOS 版本中 运行 没问题

OpenCV: not authorized to capture video (status 0), requesting...
OpenCV: camera failed to properly initialize!

原因

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

无效的建议

以下各种 Whosebug 中给出的建议 未能成功强制构建的二进制文件提示访问相机的权限: - 将 Info.plist 添加到您的项目目录 - 在 Build Settings > Packaging > Info.plist File 下将路径设置为 Info.plist - 在目标

General > Identity > Choose Info.plist File... 中选择它

可能有帮助的建议

opencv 已关闭 GitHub issue 所示,libopencv 在 2019 年 4 月左右进行了一些更改,这也可能促进了使用构建目录中的可用 Info.plist 以提示用户访问相机的权限。因此,我还使用 brew upgrade 将我的 opencv 升级到最新的稳定 4.1.0 版本。

P.S。我正在 运行宁 MacOS Mojave 10.14.5、Xcode 10.2.1 和 OpenCV 4.1.0

我找到了解决此问题的方法:

首先,重新设置您的相机规则:

tccutil reset Camera

接下来,我 运行 一个 3rd 方软件从终端访问相机。通过 运行 以下内容:

brew install imagesnap
imagesnap -w 1 snapshot.png

有人问我是否要允许终端访问我的相机。我点击了 "Yes"。现在我的 C++ 程序现在可以从终端访问相机了。

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

但是在运行第3方程序之后,。然后它被添加到列表中。

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

版本:XCode10.3MacOS Mohave 10.14.6OpenCV 4.1。1_2

OpenCV 项目在 C++

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

Header (.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

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

为了创建 Objective-C class,我可能需要导入 Foundation 框架,而导入会给我带来很多关于重复符号的错误,因为 Foundation 和我正在使用的第 3 方库共享很多名称。所以我创建了 C++ class,但带有 .mm 扩展名,因此我可以导入 AVFoundation 框架并授予相机访问权限。

方法dealWithCamera()远非完美,但它完全符合我的需要。随意对其进行扩展、优化、添加回调等。

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

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

brew install imagesnap

imagesnap -w 1 shot.png

然后通过将出现的弹出窗口授予相机权限。

我们可以修改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);"

您的 OpenCV 包可能已损坏。 None 以上解决方案帮助了我,我将我的解决方案 here。或许对你也有帮助。

我在 运行 OpenCV 代码时遇到了类似的问题。通过进入设置->安全和隐私->隐私并在此处添加 Xcode(您可以在此处添加您自己的程序)到开发人员工具权限,我能够非常可靠地修复它。 image showing this tab 在此处添加程序可绕过 Mojave 之后大多数 macOS 版本的严格安全限制。 (我个人是在 macOS Monterey 中这样做的)。希望这对您有所帮助!