cocoa-xu / evision

Evision: An OpenCV-Erlang/Elixir binding
https://evision.app
Apache License 2.0
322 stars 22 forks source link

Evision.VideoCapture.waitAny not loaded #237

Closed gworkman closed 3 months ago

gworkman commented 3 months ago

Just discovered the waitAny function, which looks perfect for me, as I am dealing with multiple cameras and would like to have a timeout when attempting to read frames. However, I'm running into this error:

** (ErlangError) Erlang error: "cv::VideoCapture::waitAny not loaded"
    :erlang.nif_error("cv::VideoCapture::waitAny not loaded")
    (evision 0.1.34) lib/generated/evision_nif.ex:1346: :evision_nif.videoCapture_waitAny_static/1
    (evision 0.1.34) lib/generated/evision_videocapture.ex:925: Evision.VideoCapture.waitAny/2
    iex:4: (file)

Not sure if this is because I have something configured incorrectly, or if this is a bug. Any pointers?

cocoa-xu commented 3 months ago

Hi @gworkman, thanks for reporting this issue! I've fix it in https://github.com/cocoa-xu/evision/commit/62f60d6bd1975f09ba36f5e2d471275c2a57a341, and I'll release v0.1.38 soon.

But sadly I found that this function only supports the V4L (Video 4 Linux) video backend when I test it on my Mac. So you may have to use Linux if you really want to use this function.

iex> video = Evision.VideoCapture.videoCapture("test_video.mp4")
%Evision.VideoCapture{
  fps: 29.97002997002997,
  frame_count: 815.0,
  frame_width: 1280.0,
  frame_height: 720.0,
  isOpened: true,
  ref: #Reference<0.2140648639.4142530584.6526>
}
iex> Evision.VideoCapture.waitAny([video])
{:error,
 "OpenCV(4.9.0) /Users/cocoa/Workspace/Git/evision/3rd_party/opencv/opencv-4.9.0/modules/videoio/src/cap.cpp:519: error: (-213:The function/feature is not implemented) VideoCapture::waitAny() is supported by V4L backend only in function 'waitAny'\n"}
cocoa-xu commented 3 months ago

v0.1.38 is shipped :)

gworkman commented 2 months ago

Thanks Cocoa!

I was able to test it recently, and I ran into a little bit of an error. I think I have cameras with a bad firmware - they don't like to work when both of them are plugged into the device at the same time. What that looks like for Evision is that my calls to VideoCapture.read(camera1) will sometimes for a long time before returning false, as shown below

I'm hoping to use waitAny to mitigate that behavior, but now with the following error messages I'm wondering if this is Evision, or something further down the V4L2 stack. Posting this here to see what you think :)

Thanks!

iex(1)> cam1 = Evision.VideoCapture.videoCapture(0)
%Evision.VideoCapture{
  fps: 30.0,
  frame_count: -1.0,
  frame_width: 640.0,
  frame_height: 480.0,
  isOpened: true,
  ref: #Reference<0.617984166.806223880.84111>
}

iex(2)> Evision.VideoCapture.read(cam1)
%Evision.Mat{
  channels: 3,
  dims: 2,
  type: {:u, 8},
  raw_type: 16,
  shape: {480, 640, 3},
  ref: #Reference<0.617984166.806223887.84378>
}

iex(3)> cam2 = Evision.VideoCapture.videoCapture(2)
%Evision.VideoCapture{
  fps: 30.0,
  frame_count: -1.0,
  frame_width: 640.0,
  frame_height: 480.0,
  isOpened: true,
  ref: #Reference<0.617984166.806223880.84112>
}

iex(4)> Evision.VideoCapture.read(cam2)
false

iex(5)> Evision.VideoCapture.waitAny([cam1, cam2], timeoutNs: 100)
{:error,
 "OpenCV(4.9.0) /home/runner/work/evision/evision/3rd_party/opencv/opencv-4.9.0/modules/videoio/src/cap_v4l.cpp:2447: error: (-215:Assertion failed) ptr->havePendingFrame in function 'VideoCapture_V4L_waitAny'\n"}

iex(6)> Evision.VideoCapture.waitAny([cam1], timeoutNs: 100)
[0]

iex(7)> Evision.VideoCapture.waitAny([cam2], timeoutNs: 100)
{:error,
 "OpenCV(4.9.0) /home/runner/work/evision/evision/3rd_party/opencv/opencv-4.9.0/modules/videoio/src/cap_v4l.cpp:2447: error: (-215:Assertion failed) ptr->havePendingFrame in function 'VideoCapture_V4L_waitAny'\n"}

Basically, anytime the misbehaving camera is in the waitAny array, it will throw an error instead of returning an index of a camera with valid frames.

cocoa-xu commented 2 months ago

Hi @gworkman, I think this is probably an issue either in the V4L2 backend or the camera firmware(or drivers maybe?). I wonder if you could try the equivalent code using opencv-python and see how it behaves? It should be pretty similar to the Elixir code you posted above

import cv2

cam1 = cv2.VideoCapture(0)
ok, frame = cam1.read()

# also I wasn't quite sure if the index 2 corresponds to the second camera on your setup
# or it might be a typo and thus the reason why V4L2 cannot read images from it
cam2 = cv2.VideoCapture(2) 
ok, frame = cam2.read()

cam1.waitAny([cam1, cam2], 100)