3
votes

I just installed OpenCV 2.4 from source on Ubuntu 12.04. I'm trying to use a Python script to write the first frame of a video to a PNG image, but I'm getting some bizarre results. Here's the code:

import numpy as np
import cv
import cv2
import sys

video = cv.CaptureFromFile(sys.argv[1])
frame = cv.QueryFrame(video)
proxy = cv.CreateImage(cv.GetSize(frame), 8, 1)
cv.CvtColor(frame, proxy, cv.CV_BGR2GRAY)
a = np.asarray(cv.GetMat(proxy))
cv2.imwrite('image.png', a)

Problem is, the image comes out looking like this:

half complete something or other

These are AVI files and otherwise seem to be fine. Any ideas?

Edit #1: Apologies, here is the ffmpeg version information:

ffmpeg version 0.10.2-4:0.10.2-0ubuntu0jon1
built on Mar 18 2012 09:59:38 with gcc 4.6.3
configuration: --extra-version='4:0.10.2-0ubuntu0jon1' --arch=amd64 --prefix=/usr --libdir=/usr/lib/x86_64-linux-gnu --disable-stripping --enable-vdpau --enable-bzlib --enable-libgsm --enable-libschroedinger --enable-libspeex --enable-libtheora --enable-libvorbis --enable-pthreads --enable-zlib --enable-libvpx --enable-runtime-cpudetect --enable-libfreetype --enable-vaapi --enable-frei0r --enable-gpl --enable-postproc --enable-x11grab --enable-librtmp --enable-libvo-aacenc --enable-version3 --enable-libvo-amrwbenc --enable-version3 --enable-libdc1394 --shlibdir=/usr/lib/x86_64-linux-gnu --enable-shared --disable-static
libavutil      51. 35.100 / 51. 35.100
libavcodec     53. 61.100 / 53. 61.100
libavformat    53. 32.100 / 53. 32.100
libavdevice    53.  4.100 / 53.  4.100
libavfilter     2. 61.100 /  2. 61.100
libswscale      2.  1.100 /  2.  1.100
libswresample   0.  6.100 /  0.  6.100
libpostproc    52.  0.100 / 52.  0.100

Edit #2: In my own troubleshooting, I upgraded ffmpeg from the default 12.04 ubuntu version to the one you see in Edit #1 above. This seems to have changed things a little bit: the video which generated the frame in this question now seems to work fine, but larger videos still present with corrupted bottom halves (or bottom thirds, or fourths). Even larger videos actually segfault entirely. I'm not really sure what to make of this, other than--yet again--faulty or missing codecs. It segfaults right on the QueryFrame step.

Edit #3: I changed the code to make exclusive use of the cv2 interface (as per a link in one of the comments below). Now, video.retrieve() always returns False and no image is written.

Edit #4: I ran the following command on the video before using the new cv2 interface to read the video frames:

ffmpeg -sameq -i normal.avi p_normal.avi

The output of the command looked ok except for this one line following the initialization of ffmpeg and its description of the input:

Incompatible pixel format 'pal8' for codec 'mpeg4', auto-selecting format 'yuv420p'

Here's the full output of the command:

Input #0, avi, from 'normal.avi':
  Duration: 00:01:37.60, start: 0.000000, bitrate: 1312 kb/s
    Stream #0:0: Video: rawvideo, pal8, 128x256, 5 tbr, 5 tbn, 5 tbc
Incompatible pixel format 'pal8' for codec 'mpeg4', auto-selecting format 'yuv420p'
[buffer @ 0x11a0f80] w:128 h:256 pixfmt:pal8 tb:1/1000000 sar:0/1 sws_param:
[buffersink @ 0x11a1380] auto-inserting filter 'auto-inserted scale 0' between the filter 'src' and the filter 'out'
[scale @ 0x1197da0] w:128 h:256 fmt:pal8 -> w:128 h:256 fmt:yuv420p flags:0x4
Output #0, avi, to 'p_normal.avi':
  Metadata:
    ISFT            : Lavf53.32.100
    Stream #0:0: Video: mpeg4 (FMP4 / 0x34504D46), yuv420p, 128x256, q=2-31, 200 kb/s, 5 tbn, 5 tbc
Stream mapping:
  Stream #0:0 -> #0:0 (rawvideo -> mpeg4)
Press [q] to stop, [?] for help
frame=  488 fps=  0 q=0.0 Lsize=    1497kB time=00:01:37.60 bitrate= 125.6kbits/s    
video:1480kB audio:0kB global headers:0kB muxing overhead 1.165352%

Most importantly, the Python OpenCV code to read the frame (using the cv2 interface) still returns False (same behavior as before).

Edit #5: I have so far followed the instructions found here for installing ffmpeg and its dependencies from source, and that went smoothly. Without reinstalling OpenCV from source, I am still encountering the same problem as before where video.retrieve() returns False. In attempting to recompile OpenCV 2.4 from source, I am getting the following error during compilation:

Linking CXX shared library ../../lib/libopencv_highgui.so
/usr/bin/ld: /usr/local/lib/libavcodec.a(avpacket.o): relocation R_X86_64_32S against `av_destruct_packet' can not be used when making a shared object; recompile with -fPIC
/usr/local/lib/libavcodec.a: could not read symbols: Bad value

If I recompile x264, libvpx, and ffmpeg with the --enable-pic flag, OpenCV compilation still fails, this time with (kdbwin.o, .rodata) instead of (avpacket.o, av_destruct_packet) respectively in the snippet above.

Edit #6: Fixed the above error by adding --enable-shared to the configuration options of libvpx and ffmpeg. OpenCV recompiled and built successfully, and ffmpeg worked just fine. Sadly, after running the previous command (ffmpeg -sameq -i normal.avi p_normal.avi), my script still couldn't retrieve any frames; the flag returned was still False. Any further ideas?

Edit #7: Here's the latest script I'm using.

import numpy as np
import cv2
import sys

video = cv2.VideoCapture(sys.argv[1])
flag, frame = video.retrieve()
if not flag:
  print 'Error'
  quit()
proxy = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imwrite('image.png', proxy)

Edit #8: Got it! Here's what the code should be:

import numpy as np
import cv2
import sys

video = cv2.VideoCapture(sys.argv[1])
if video.grab():
  flag, frame = video.retrieve()
  if not flag:
    print 'Error'
    quit()
  proxy = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
  cv2.imwrite('image.png', proxy)
1

1 Answers

2
votes

Most likely the problem is with ffmpeg and the used video codec. Could you share one of these videos? then we can check the codec and see what's wrong. Also we don't know what ffmpeg version you've got on your system.

Just side note, why are you mixing the cv and cv2 interfaces? the code would look better by using just the cv2 interface.

Edit: I forgot to mention, I tested the code, so certainly the problem isn't in the code.

Edit 2: It's likely the problem is with ffmpeg not being able to handle your videos. Without testing the videos in question it's hard to say. You can give the cv2 interface a try, cv2.VideoCapture is what you should use to grab frames from a video file.

Edit 3: I had a look at the video, it seems the way OpenCV uses ffmpeg has a problem with your video. A quick workaround is to process your videos with ffmpeg before opening by OpenCV. ffmpeg -sameq -i normal.avi p_norma.avi gives you a video, p_norma.avi, that you can process by OpenCV without problem. For normal.avi:

Selected video codec: [rawbgr8flip] vfm: raw (RAW BGR8)

and for the generated video p_norma.avi:

Selected video codec: [ffodivx] vfm: ffmpeg (FFmpeg MPEG-4)

Hence, the generated video is about 10 times smaller. Would that be a possible solution for you?