OpenCV 命令行应用在 macOS Mojave 下无法访问摄像头。

15

我无法从命令行 OpenCV 程序访问 iMac 摄像头。(我在 CodeRunner 下编译和运行程序,而不是 Xcode。) 我读到 Mojave 要求 Info.plist 中包含 NSCameraUsageDescription,我认为我已经正确地将它嵌入到二进制文件中了。我添加了 -sectcreate __TEXT __info_plist Info.plist (我在这里了解到) 到编译标志中,当我运行 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,以防它试图与相机一起打开麦克风。这是我运行程序时的输出结果:

(I added NSMicrophoneUsageDescription in case it was trying to open the microphone along with the camera.)

当我运行程序时,这是输出结果:

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}"

我缺少的拼图是什么?

(我知道这与Mac Mojave上无法使用opencv访问摄像头类似,但该问题从未超出格式错误的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安装ffmpeg,然后使用此命令检查相机是否被找到:https://stackoverflow.com/a/46768069/2836621 - Mark Setchell
@MarkSetchell 感谢您的建议。 ffmpeg 似乎找到了相机。它也可以在照片展位中使用。 (今日所学:不要在嘴里含食物时启动照片展位。) - SSteve
你能试试用sudo吗?值得一试。据我所知,你根本不需要包含plist文件,但我不确定。你也可以尝试使用Python。 - gerwin
sudo 是个好主意。我之前没想过尝试它。但是它没起作用。然后我尝试了 Python 的建议。第一次运行时,我收到一个对话框,说终端正在请求访问相机,所以我授权访问。之后 Python 程序就可以工作了。然后我尝试了命令行程序,它也可以工作!成功了!! - SSteve
10个回答

6
问题在于C++程序无法请求摄像头访问权限。我遵循评论区@gerwin的建议,尝试使用Python运行该程序,并在终端中运行。这导致终端提示请求摄像头权限。一旦我授权,从终端运行C++程序就能够访问摄像头。
至于CodeRunner,我不确定如何使它在虚拟环境下运行Python程序,因此我无法运行Python OpenCV程序以请求摄像头访问权限。所以目前我无法使用CodeRunner运行访问摄像头的C++程序。

1
从终端运行?真的吗?那只是解决方案的三分之一。无法逐帧调试,叹气。我想OpenCV可能有问题...我知道错误消息是从这里开始的。因为相机可以通过终端工作,所以我们无法在开发工具中找到它是没有意义的。必须是一个OpenCV和权限时间相关的问题?你怎么看? - zipzit

6

这可能不是最好的解决方案,但我通过安装任何要求访问相机的终端应用程序来解决了它。然后你的openCV C++程序将在之后获得对FaceTime HD相机的访问权限。

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

brew install imagesnap

imagesnap -w 1 shot.png

然后通过弹出的窗口给予相机权限即可。


3

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

这里有几个评论...

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

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

我知道这些词源自OpenCV库这里。我最初的想法是这是一个OpenCV问题。经过更多的测试,我认为这是其他问题。正如其他人所指出的,这可能是MacOS安全/权限问题。但这是问题所在。

如果我去Mac苹果图标(左上角)-->系统偏好设置-->安全性与隐私,我可以获取很多信息。

Mac Systems Preferences

点击相机图标。

Security and Privacy Camera

在我的情况下,这显示了需要额外权限才能访问相机的两个应用程序,终端和Virtualbox(不确定浏览器、Facetime会发生什么?)我注意到,Xcode没有出现在这个列表中。
当我切换到麦克风时,我看到不同的应用程序列表,包括Xcode。

Security and Privacy Microphone

我该怎么做?我进行了大量测试,包括研究修改Xcode应用程序包的Info.plist(Finder -> 应用程序文件夹 -> Xcode -> 右键单击,显示包内容。复制Info.plist并将其保存在其他地方,通过Xcode进行修改,重新提交。)注意:不要在没有保留原始Info.plist的情况下尝试此操作。完全失败。添加NSCameraUsageDescription键/值是完全失败的。Xcode根本无法打开。提醒:不要丢失原始的Info.plist。

这整个事情都令人困惑。为什么苹果允许我们通过终端访问相机,但不能在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);
   ...

这只是一个折中的解决方案,但总比没有好。 (真希望苹果在iOS模拟器中包含相机,那将是另一种解决方法,叹气。)显然,很多事情取决于您的项目目标。最终我需要我的项目在iPad上运行;在macOS上验证,然后用Swift包装代码等等...
供参考,我使用macOS Mojave 10.14.4,MacBook 2.7GHz i7
附注:上述安全偏好设置未显示允许Chrome使用相机。看起来很奇怪。我刚在Chrome中测试了this site...的相机,它要求获得许可并且正常工作。不清楚这里发生了什么。
附注2:我是唯一报告此问题的人吗?附带链接方便您使用。谢谢。

似乎在macOS 10.15上,修改Xcode plist的方法现在无法执行。即使进行了修改,我也无法让它工作。感谢您的尝试并向Apple报告问题。如果有任何进展,请告诉我们,谢谢! - malajisi

3
版本: 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和我正在使用的第三方库共享很多名称。所以我创建了C++类,但使用.mm扩展名,这样我就可以导入AVFoundation框架并授予相机访问权限。
dealWithCamera()方法远非完美,但它完全满足我的需求。请随意扩展、优化、添加回调等。

1

我最终通过在Stackoverflow和GitHub上的一系列建议中找到解决方法。这是一个痛苦的错误,花费了我一整天的时间来让我的代码重新运行,尽管在MacOS Mojave之前它表现得很好。

解决方案

将带有NSCameraUsageDescription字段的Info.plist文件作为suggested放置在您的目标的Products/Build目录中(在Xcode项目的左窗格中右键单击Product并单击“Show in Finder”)。

  • 通过将其添加到“目标”的Copy Files列表并将Destination更改为“Products Directory”,Subpath更改为“。”,自动化此过程将Info.plist复制/粘贴到您的Build目录中(按照此suggestion)。

结果

  • 然后,目标的Unix可执行二进制文件将要求访问相机,并在同意后将该二进制文件添加到“系统偏好设置>隐私>相机”中允许访问相机的应用程序列表中。
    • FYI:要强制清除此列表,请在“终端”中键入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已加强了隐私保护,现在需要应用程序明确提示并请求许可才能访问相机,如此处所述。

未成功的建议

以下建议来自不同的Stackoverflow 帖子,但未能成功地强制生成的二进制文件提示访问相机的权限: - 将Info.plist添加到您的项目目录中 - 在Build Settings > Packaging > Info.plist File下设置Info.plist的路径或者 - 在您的目标General > Identity > Choose Info.plist File...中选择它

可能有所帮助的建议

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

附言:我正在使用 MacOS Mojave 10.14.5、Xcode 10.2.1 和 OpenCV 4.1.0


1
我找到了一个解决方法:
首先,重置您相机的规则:
tccutil reset Camera

接下来,我运行了一个第三方软件来从终端访问相机。通过运行以下命令:
brew install imagesnap
imagesnap -w 1 snapshot.png

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

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

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


0

我们在运行opencv 4.1.1-pre时遇到了完全相同的问题。我们通过回滚到4.0.1来解决了这个问题。


请提供更多细节。是什么导致了4.1.1-pre中的问题,而这个问题在4.0.1中得到了解决? - ben3000
很遗憾,由于时间限制,我们无法确定原因。 - Liam

0
当我运行OpenCV代码时,遇到了类似的问题。通过进入“设置”->“安全性与隐私”->“隐私”,并在此处将Xcode(您可以在此处添加自己的程序)添加到开发人员工具权限中,我能够非常可靠地解决它。显示此选项卡的图像 在此处添加程序可以绕过大多数macOS版本(Mojave之后)的加强安全限制。(我个人在macOS Monterey上执行此操作)。希望这可以帮助您!

0

有可能您的OpenCV软件包已损坏。上述解决方案都没有帮助到我,我在这里提供了我的解决方案。也许对您有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接