12

My problem here is that when I extracting a video into a frame using opencv, sometimes the frame that I get will flip up which happened to me for both my machine(window) and VM(ubuntu) But some of the video I tested, frames are not flip. So, I wonder what factor or what should be changed/added in my code to make the extract fixed without a flip

def extract_frame(video,folder):
   global fps

   os.mkdir('./green_frame/{folder}/'.format(folder=folder))
   vidcap = cv2.VideoCapture(video)
   success,image = vidcap.read()
   fps = vidcap.get(cv2.CAP_PROP_FPS)
   count = 0
   success = True
   while success:  #os.path.join(pathOut,(name+'.png'))
      cv2.imwrite(os.path.join('./green_frame/{folder}/'.format(folder=folder),"frame%d.png" % count), image)
      success,image = vidcap.read()
      print('Read a new frame: ', success) 
      count += 1

This is the example of frame I get from this code. flip Which my orginal video that I used is upside down like this:enter image description here

So, in my case, what I have to changed to make it not flip like my first picture. Is it relate to the resolution or framerate of the video? I tested with a 1280x720 resolution video and all of the frame extracted are flipped upside down but a frame from video with a 568x320 is normal

Thank you

Edit: So, I look at the information of the video and I found out that in the metadata, it has rotate 180 for the video that extract to an upside down frame enter image description here But when I check with a normal video that produced a non upside-down frame, it does not have rotate:180 enter image description here

So from this, how can I deal with a video that has a rotation?

Christoph Rackwitz
  • 11,317
  • 4
  • 27
  • 36
emp
  • 602
  • 3
  • 11
  • 22
  • Videos have a property called ‘orientation’. I bet in your case it would be set to 180. – Dmitrii Z. Nov 01 '18 at 10:38
  • Sorry, but where is the 'orientation' property location?. I cannot find it in the properties option. @DmitriiZ. – emp Nov 01 '18 at 10:43
  • You can get it with ffmpeg CLI – Dmitrii Z. Nov 01 '18 at 10:49
  • Oh I use `ffmpeg -i videoname.mp4` and it show that it rotate 180 the same as you said. But how can I fix this from my source code? – emp Nov 01 '18 at 10:52
  • I'm not sure if you can do this with opencv (as far as I know you can't), you can try 3rd party libraries like ffmpeg or [hackoir](https://pypi.org/project/hachoir-metadata/) – Dmitrii Z. Nov 01 '18 at 11:27
  • So ideally to prevent this, I have to write a function that check wheter the input video's metadata has a rotation = 180 or not. If yes, do something that make it be as normal else continue script(there are other script that need to be process base on the video), kind of that, right? – emp Nov 01 '18 at 11:50
  • Exactly. Note that it can also be 90 and 270. – Dmitrii Z. Nov 01 '18 at 11:59
  • My solution that surprisingly work for me is that I changed the code that I wrote to write a video from `ffmpeg_extract_subclip(video, t1, t2, targetname=output)` to use write_videofile() function instead and it not produce a rotation 180 in the property. Thank you for the help – emp Nov 01 '18 at 13:42

5 Answers5

28

Update

This problem can now be solved by simply updating to OpenCV v4.5 and above.

If upgrading is a problem follow the old answer, below.


For anyone still looking into this, I was just stuck on the same problem. Turns out some Android phones and iPhones take images/frames in landscape and convert them on the fly according to the exif 'rotate' tag to display the images/frames.

Weird design choice in OpenCV is that cv2.imread(img_file) already reads the image in correct orientation by reading the image's rotate tag, but the cv2.VideoStream's read() method does not do this.

So, to fix this I used ffmpeg to read the 'rotate' tag and rotate the video frame to its correct orientation.(Big thanks to the comments above, for pointing me in the right direction )

Following is the code:

  • Make sure you have ffmpeg for python. (pip install ffmpeg-python)

  • Create a method to check if rotation is required by the video_file:

     import ffmpeg    
    
     def check_rotation(path_video_file):
         # this returns meta-data of the video file in form of a dictionary
         meta_dict = ffmpeg.probe(path_video_file)
    
         # from the dictionary, meta_dict['streams'][0]['tags']['rotate'] is the key
         # we are looking for
         rotateCode = None
         if int(meta_dict['streams'][0]['tags']['rotate']) == 90:
             rotateCode = cv2.ROTATE_90_CLOCKWISE
         elif int(meta_dict['streams'][0]['tags']['rotate']) == 180:
             rotateCode = cv2.ROTATE_180
         elif int(meta_dict['streams'][0]['tags']['rotate']) == 270:
             rotateCode = cv2.ROTATE_90_COUNTERCLOCKWISE
    
         return rotateCode
    
  • Create a method to correct the rotation of the frame in video file:

     def correct_rotation(frame, rotateCode):  
         return cv2.rotate(frame, rotateCode) 
    
  • Finally, do this in your main loop:

     # open a pointer to the video file stream
     vs = cv2.VideoCapture(video_path)
    
     # check if video requires rotation
     rotateCode = check_rotation(video_path)
    
     # loop over frames from the video file stream
     while True:
         # grab the frame from the file
         grabbed, frame = vs.read()
    
         # if frame not grabbed -> end of the video
         if not grabbed:
             break
    
         # check if the frame needs to be rotated
         if rotateCode is not None:
             frame = correct_rotation(frame, rotateCode)
    
         # now your logic can start from here
    

Hope this helps

Rafay Khan
  • 1,070
  • 13
  • 25
  • Do you have this working on Python 3.7? I'm running into some issues with ffmpeg, and it seems like maybe it's because it's meant for Python 2 – stranger Dec 31 '19 at 22:59
  • @stranger really? I came up with this when I was using Python 3.6. I'll report back in a little while – Rafay Khan Jan 01 '20 at 06:38
  • @stranger working fine on python 3.6. The `ffmpeg-python` lib was updated to version 0.2.0 a few months back with Python 3.7 support. Maybe update your package. Check out the project here: https://github.com/kkroening/ffmpeg-python – Rafay Khan Jan 01 '20 at 06:45
  • thanks for your response, I was able to get it working for mp4 videos :) I don't suppose you have a trick for .MOV files as well, do you? – stranger Jan 02 '20 at 18:34
  • 3
    I figured it out, the `rotation` information was on a different stream – stranger Jan 02 '20 at 20:01
  • @stranger Sorry for the late reply. Unfortunately, I haven't worked with .MOV files – Rafay Khan Jan 11 '20 at 16:32
  • no problem, thanks for following up. I ended up figuring it out :) Left an additional comment for posterity. – stranger Jan 13 '20 at 16:46
  • @RafayKhan I used your code but in the "meta_dict" there is no rotate flag.I am using 5.0.1 version on a mac. – Lucky Sunda Jul 14 '22 at 15:26
  • @LuckySunda In that case, it might be possible that there is no metadata in the video file. You can check EXIF data online for your file using something like https://exifmeta.com/ – Rafay Khan Jul 16 '22 at 12:38
  • Not every video has `'rotate'` tag and not every video has `'tags'`. It would be good to fix answer code with some `if`s catching this situations. @RafayKhan – s.paszko Jul 22 '22 at 17:23
  • 3
    I have a video sample that should be rotated 180 degrees but rotation info in metadata is 90 degrees so it is wrong. I have another video that has no need for rotation but metadata rotation info is also 90 degrees. – mcsahin Aug 26 '22 at 10:37
  • Using version 4.6 and I still get this error and this solution did not fix it. I get "KeyError: 'rotate'" – Frobot Nov 06 '22 at 17:03
  • Edit: I had to install v4.5 because 4.6 is broken. Kinda ridiculous but it works. – Frobot Nov 06 '22 at 17:14
  • @Frobot, check one of the comments above, rotation parameters could be in a different key depending on the video container. – Rafay Khan Nov 16 '22 at 09:34
  • @s.paszko any idea what could be the possible keys where rotate tag could be? Is there some wiki for this kinda stuff? – Rafay Khan Nov 16 '22 at 09:35
  • @mcsahin same here, I have a video that is supposed to rotate 180 but the metadata is 90. I used iPhone to capture the video. Did you solve this issue? – lqi Mar 27 '23 at 20:11
3

Sometimes the following will solve the problem of opening some videos upside-down.

cap = cv2.VideoCapture(path, apiPreference=cv2.CAP_MSMF)
YScharf
  • 1,638
  • 15
  • 20
3

When I recently ran into this issue, I found that all that was required was to update to OpenCV v4.5:

agrant3d
  • 53
  • 4
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/late-answers/31674699) – Emi OB May 04 '22 at 12:13
  • 1
    The essential part of my answer is: "update to OpenCV v4.5". Thanks! :) – agrant3d May 05 '22 at 13:24
  • 2
    NOTE: This feature is broken in opencv 4.6 downgrade to version 4.5.5.64 for frames to be auto saved according to input videos rotation. For reference: https://github.com/opencv/opencv/issues/22088. To downgrade use, pip uninstall opencv-python, pip install opencv-python==4.5.5.64. – jacktim Aug 29 '22 at 15:40
  • 1
    This should be the accepted answer given the package has taken the direction that this should be implemented by default since v4.5 – jacktim Aug 29 '22 at 15:43
1

The rotate tag is optional so the check_rotation will fail, This code fix it:

def check_rotation(path_video_file):
    # this returns meta-data of the video file in form of a dictionary
    meta_dict = ffmpeg.probe(path_video_file)
    # from the dictionary, meta_dict['streams'][0]['tags']['rotate'] is the key
    # we are looking for
    rotate_code = None
    rotate = meta_dict.get('streams', [dict(tags=dict())])[0].get('tags', dict()).get('rotate', 0)
    return round(int(rotate) / 90.0) * 90
  • ` # check if the frame needs to be rotated if rotateCode is not None: frame = correct_rotation(frame, rotateCode)` Takes care of that problem – Rafay Khan Sep 23 '19 at 07:10
0

I would just do this in your frame processing loop:

frame = cv2.flip(frame,0)

The 0 flips vertically, see Open CV documentation for more info.

Benice
  • 409
  • 3
  • 15
  • This is not the way to detect if a video is rotated or not. – Shan Ali Sep 25 '19 at 07:17
  • The question doesn't ask the way to detect if a video is rotated or not. – Benice Sep 26 '19 at 10:54
  • "So from this, how can I deal with a video that has a rotation?" literally mentioned in the OPs question – Rafay Khan Oct 01 '19 at 08:52
  • 3
    Right! And a possible answer is: you can flip it. You are reading more into the question than is there. The question did not say "How can I detect..." it said "How can I deal with ..." – Benice Oct 04 '19 at 06:49