【问题标题】:OpenCV command line app can't access camera under macOS MojaveOpenCV 命令行应用程序无法在 macOS Mojave 下访问相机
【发布时间】:2019-09-28 18:12:33
【问题描述】:

我无法从命令行 OpenCV 程序访问 iMac 相机。 (我在 CodeRunner 下编译和运行程序,而不是 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(来自同一篇博文)时,它输出:

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

【问题讨论】:

  • 不知道它是否有帮助,但如果你想抓住一根稻草......你可以尝试使用brew install ffmpeg安装ffmpeghomebrew,然后检查可以使用此命令找到相机stackoverflow.com/a/46768069/2836621
  • @MarkSetchell 感谢您的建议。 ffmpeg 似乎正在寻找相机。它确实适用于 Photo Booth。 (TIL:千万不要在咀嚼食物时启动 Photo Booth。)
  • 你可以试试 sudo 吗?值得一试。 AFAIK 你根本不需要包含 plist,但我不确定。你也可以试试 Python 吗?
  • sudo 是个好主意。我没有尝试过。但它没有用。但后来我尝试了 Python 的建议。第一次运行它时,我得到一个对话框,说终端正在请求访问相机,所以我授予了访问权限。之后,Python 程序开始工作。然后我尝试了命令行程序,它也工作了! 成功!!

标签: macos opencv macos-mojave


【解决方案1】:

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

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

brew install imagesnap

imagesnap -w 1 shot.png

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

【讨论】:

    【解决方案2】:

    问题在于,无论出于何种原因,c++ 程序都没有请求摄像头访问权限。我在 cmets 中听取了@gerwin 的建议,尝试使用 Python。从终端运行该程序导致终端要求访问相机。一旦我同意,当从终端运行时,c++ 程序就能够访问相机。

    就 CodeRunner 而言,我不确定如何让 CodeRunner 在虚拟环境下运行 Python 程序,因此我无法运行 Python OpenCV 程序来让它请求摄像头访问权限。所以目前我无法使用 CodeRunner 运行访问相机的 c++ 程序。

    【讨论】:

    • 从终端运行?真的吗?这只是解决方案的 1/3d。你不可能那样逐帧调试,叹息。我认为 OpenCV 有问题...我知道错误消息来自 exactly here. 。因为相机是通过终端工作的,所以我们无法在开发工具中到达那里是没有意义的。一定是openCV和许可时间相关的问题吗?你的想法?
    【解决方案3】:

    这里有几个cmets...

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

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

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

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

    检查相机图标。

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

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

    它是如何工作的?我确实做了很多测试,包括研究修改 Xcode 应用程序包的 Info.plist (Finder --> Applications Folder --> Xcode --> Rt click, Show Package Contents. Copy Info.plist save it else,通过 Xcode 修改它,重新提交。)注意:如果没有保留原始 Info.plist 的副本,请勿尝试此操作。完全失败。添加NSCameraUsageDescription 键/值完全失败。 Xcode 根本打不开。提醒不要丢失原始 Info.plist。

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

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

    所以有几件事要理解。

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

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

    3. (编辑更新 5/22/19...)哇。我刚刚意识到..您可以将调试器附加到正在运行的(终端)进程。您完全可以使用相机进行逐帧调试(只要程序编译为功能可执行文件)。现在这非常酷,让我获得了 98% 的功能。为此,启动终端可执行文件,然后转到 Xcode --> Debug --> Attach to Process。选择正在运行的应用程序,将断点添加到源代码并调试/单步执行。效果很好。

    我开始我的 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 模拟器中,这将是解决此问题的另一种方法,叹息。)显然,很大程度上取决于您的项目将在哪里进行。最终我需要我的在 iPad 上运行;在 MacOS 上进行 Proveout,然后在 Swift 中包装代码等...

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

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

    PS2。我是唯一一个在这个问题上file a bug report 的人吗?为方便起见,包含链接。谢谢。

    【讨论】:

    • 看来,对于修改 Xcode plist 方法现在无法在 macOS 10.15 上执行。我无法让它工作,即使修改。感谢您每次尝试并向 Apple 报告问题。有任何进展请告诉我们,谢谢!
    【解决方案4】:

    版本:XCode 10.3MacOS Mohave 10.14.6OpenCV 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
    

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

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

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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

        通过 Stackoverflow 和 GitHub 上的一系列建议,我终于能够解决这个问题。这是一个痛苦的错误,虽然在 MacOS Mojave 之前它运行良好,但我试图让我的代码再次运行,这让我一天都白费了。

        解决方案

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

        • 通过将Copy Files 添加到“目标”的Build Phases 下的Copy Files 列表并将目标更改为“产品”,自动将Info.plist 复制/粘贴到您的构建目录(在此suggestion 之后)的过程目录”和“。”的子路径

        结果

        • Target 的 Unix 可执行二进制文件随后将请求访问摄像头的权限,并且在获得同意后,该二进制文件将被添加到允许访问摄像头的应用程序列表中,该应用程序位于 System Preferences &gt; Privacy &gt; Camera 中。
          • 仅供参考:要强制清除此列表,请在 Terminal 中键入 tccutil reset Camera
        • 您可能需要运行 Target 几次,然后系统会提示您授予权限/访问相机。

        问题

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

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

        原因

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

        无效的建议

        以下各种 Stackoverflow posts 中给出的建议没有成功强制构建的二进制文件提示访问相机的权限: - 将Info.plist 添加到您的项目目录 - 在Build Settings &gt; Packaging &gt; Info.plist File下设置Info.plist的路径 - 在目标的General &gt; Identity &gt; Choose Info.plist File... 中选择它

        可能有帮助的建议

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

        附:我正在运行 MacOS Mojave 10.14.5、Xcode 10.2.1 和 OpenCV 4.1.0

        【讨论】:

          【解决方案7】:

          我找到了解决方法:

          首先,重置你的相机规则:

          tccutil reset Camera
          

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

          brew install imagesnap
          imagesnap -w 1 snapshot.png
          

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

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

          但在运行 3rd 方程序后,。然后将其添加到列表中。

          【讨论】:

            【解决方案8】:

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

            【讨论】:

            • 请提供更多细节。 4.1.1-pre 是什么导致了 4.0.1 修复的问题?
            • 很遗憾,由于时间限制,我们无法确定原因。
            【解决方案9】:

            您的 OpenCV 包可能已损坏。以上解决方案都没有帮助我,我把我的解决方案放在here。也许它对你也有帮助。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-05-29
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-07-31
              • 1970-01-01
              相关资源
              最近更新 更多