Repository: opencv
Issue: VideoWriter produces distorted outputs for certain aspect ratios
Labels: category: videoio,platform: ios/osx,incomplete,needs investigation

OpenCV version 3.4.2 on OSX Sierra 10.13.6
I wrote a simple test where I read an mp4 video and write a cropped version out. 
For certain widths, the output appears distorted. Changing the height is fine. Changing the width produces distortion.

![distortion](https://user-images.githubusercontent.com/7737286/45237552-e2cfb580-b293-11e8-9a86-c00e6f5435b0.png)

I've included a zip file with everything needed to reproduce this issue.
[VidWriter_test.zip](https://github.com/opencv/opencv/files/2362166/VidWriter_test.zip)


The following works fine:
`python test_video.py drift_short.mp4 -w 960 -h 540` 

The following produces a distorted output:
`python test_video.py drift_short.mp4 -w 900 -h 540`


Thanks -

------------------------
```
"""
Test video reading and writing. Reads a video, writes the same video out, cropped to the specified width and height.

Usage:
    test_video.py VID [--width=W] [--height=H]

Arguments:
    VID                         Path to video file

Options:
    -w --width=W                Width of the crop
    -h --height=H               Height of the crop

Example:
    python test_video.py video.mp4 -w 960 -h 540
"""


import os
import pdb
import time
import cv2
import numpy as np
import docopt
from imutils.video import FileVideoStream
from imutils.video import FPS


class VideoReader(object):
    def __init__(self, video_path, verbose=True):
        assert isinstance(video_path, str)
        assert os.path.exists(video_path), "{} does not exist".format(video_path)

        self.video_path = os.path.expanduser(video_path)
        self.verbose = verbose
        if self.verbose:
            print("[INFO] starting video file thread...")
        self.fvs = FileVideoStream(self.video_path).start()
        time.sleep(1.0)
        self.fps = FPS().start()

    def get_video_props(self):
        """Collect dictionary containing video properties
        in order to set up the video writer
        """
        cam = cv2.VideoCapture(self.video_path)
        props = {}
        props["width"] = int(cam.get(cv2.CAP_PROP_FRAME_WIDTH))
        props["height"] = int(cam.get(cv2.CAP_PROP_FRAME_HEIGHT))
        props["fourcc"] = int(cam.get(cv2.CAP_PROP_FOURCC))
        props["fps"] = cam.get(cv2.CAP_PROP_FPS)
        props["number_of_frames"] = cam.get(cv2.CAP_PROP_FRAME_COUNT)
        return props

    def get_frame(self):
        """Retrieve a frame from video. If no more, return None
        """
        frame = None
        if self.fvs.more():
            frame = self.fvs.read()
            self.fps.update()
        return frame

    def more(self):
        """Return True if there are remaining frames
        """
        return self.fvs.more()

    def __del__(self):
        self.fps.stop()
        if self.verbose:
            print("[INFO] elapsed time: {:.2f}".format(self.fps.elapsed()))
            print("[INFO] approx. FPS: {:.2f}".format(self.fps.fps()))
        self.fvs.stop()


class VideoWriter(object):
    def __init__(self, output_name, verbose=True):
        """output_name can be None, in which case this class
        does nothing.
        """
        assert isinstance(verbose, bool)
        self.vid_writer = None
        self.output_name = output_name
        self.setup_done = False
        self.verbose = verbose
        if self.verbose and self.output_name is None:
            print("Setting up VideoWriter as dummy class")

    def set_props_similar_to(self, video_path):
        """Set up the video writer properties based on the properties
        of the input video: fps, codec, etc
        :video_path:        Path to input video whose props we want to replicate (str)
        """
        assert os.path.exists(os.path.expanduser(video_path))

        if self.output_name is not None:
            if self.verbose:
                print("VideoWriter: Properties replicated from {}".format(video_path))
            cam = cv2.VideoCapture(video_path)
            fourcc = int(cam.get(cv2.CAP_PROP_FOURCC))
            fps = cam.get(cv2.CAP_PROP_FPS)
            size = (int(cam.get(cv2.CAP_PROP_FRAME_WIDTH)),
                    int(cam.get(cv2.CAP_PROP_FRAME_HEIGHT)))
            self.vid_writer = cv2.VideoWriter(self.output_name, fourcc, fps, size)
        self.setup_done = True

    def set_video_props(self, props):
        """Set video writer properties from props dictionary
        :props:     Dictionary containing video properties (dict)
        """
        assert "fourcc" in props.keys()
        assert "fps" in props.keys()
        assert "width" in props.keys()
        assert "height" in props.keys()

        if self.output_name is not None:
            self.vid_writer = cv2.VideoWriter(
                self.output_name,
                props["fourcc"],
                props["fps"],
                (props["width"], props["height"]))
        self.setup_done = True

    def write(self, frame):
        """Write a frame out.
        :frame:     An frame image (np.ndarray)
        """
        assert self.setup_done, "Call set_props_similar_to() or set_video_props() first"
        assert isinstance(frame, np.ndarray)

        if self.output_name is not None:
            self.vid_writer.write(frame)

    def __del__(self):
        if self.output_name is not None:
            self.vid_writer.release()


def main():
    try:
        # Parse arguments
        arguments = docopt.docopt(__doc__)
        video_path = os.path.expanduser(arguments["VID"])
        crop_width = int(arguments["--width"])
        crop_height = int(arguments["--height"])

        # Create Video reader, get its video properties
        vid_reader = VideoReader(video_path)
        vid_props = vid_reader.get_video_props()

        # Modify those properties for the resized outptut
        assert crop_width > 0
        assert crop_width <= vid_props["width"]
        assert crop_height > 0
        assert crop_height <= vid_props["height"]

        vid_props["width"] = crop_width
        vid_props["height"] = crop_height
        print("New vid_props: {}".format(vid_props))

        # Create Video writer, set its properties
        vid_writer = VideoWriter("out.mp4")
        vid_writer.set_video_props(vid_props)

        # Read a frame, crop it, write it out
        while vid_reader.more():
            frame = vid_reader.get_frame()
            frame_out = frame[:vid_props["height"], :vid_props["width"], :]
            vid_writer.write(frame_out)

    except docopt.DocoptExit as err:
        print(err)

if __name__ == "__main__":
    main()
```