mirror of
https://github.com/opencv/opencv.git
synced 2025-08-06 06:26:29 +08:00
MP4 autorotation tests and various fixes for Windows
- Added test for automated rotation for MP4 videos with metadata - Fix 180 degrees rotation bug - Moved rotation logic to cv::VideoCapture implementation for FFmpeg and restore binary compatibility with FFmpeg wrapper.
This commit is contained in:
parent
f0271e54d9
commit
7ed37b3fa5
@ -229,12 +229,17 @@ public:
|
||||
}
|
||||
virtual bool retrieveFrame(int, cv::OutputArray frame) CV_OVERRIDE
|
||||
{
|
||||
cv::Mat mat;
|
||||
unsigned char* data = 0;
|
||||
int step=0, width=0, height=0, cn=0;
|
||||
|
||||
if (!ffmpegCapture ||
|
||||
!icvRetrieveFrame_FFMPEG_p(ffmpegCapture, mat))
|
||||
!icvRetrieveFrame_FFMPEG_p(ffmpegCapture, &data, &step, &width, &height, &cn))
|
||||
return false;
|
||||
|
||||
mat.copyTo(frame);
|
||||
cv::Mat tmp(height, width, CV_MAKETYPE(CV_8U, cn), data, step);
|
||||
this->rotateFrame(tmp);
|
||||
tmp.copyTo(frame);
|
||||
|
||||
return true;
|
||||
}
|
||||
virtual bool open( const cv::String& filename )
|
||||
@ -261,8 +266,30 @@ public:
|
||||
|
||||
protected:
|
||||
CvCapture_FFMPEG* ffmpegCapture;
|
||||
private:
|
||||
|
||||
void rotateFrame(cv::Mat &mat) const
|
||||
{
|
||||
bool rotation_auto = 0 != getProperty(CV_FFMPEG_CAP_PROP_ORIENTATION_AUTO);
|
||||
int rotation_angle = static_cast<int>(getProperty(CV_FFMPEG_CAP_PROP_ORIENTATION_META));
|
||||
|
||||
if(!rotation_auto || rotation_angle%360 == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
cv::RotateFlags flag;
|
||||
if(rotation_angle == 90 || rotation_angle == -270) { // Rotate clockwise 90 degrees
|
||||
flag = cv::ROTATE_90_CLOCKWISE;
|
||||
} else if(rotation_angle == 270 || rotation_angle == -90) { // Rotate clockwise 270 degrees
|
||||
flag = cv::ROTATE_90_COUNTERCLOCKWISE;
|
||||
} else if(rotation_angle == 180 || rotation_angle == -180) { // Rotate clockwise 180 degrees
|
||||
flag = cv::ROTATE_180;
|
||||
} else { // Unsupported rotation
|
||||
return;
|
||||
}
|
||||
|
||||
cv::rotate(mat, mat, flag);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
@ -41,7 +41,8 @@ OPENCV_FFMPEG_API int cvSetCaptureProperty_FFMPEG(struct CvCapture_FFMPEG* cap,
|
||||
int prop, double value);
|
||||
OPENCV_FFMPEG_API double cvGetCaptureProperty_FFMPEG(struct CvCapture_FFMPEG* cap, int prop);
|
||||
OPENCV_FFMPEG_API int cvGrabFrame_FFMPEG(struct CvCapture_FFMPEG* cap);
|
||||
OPENCV_FFMPEG_API int cvRetrieveFrame_FFMPEG(struct CvCapture_FFMPEG* capture, cv::Mat &mat);
|
||||
OPENCV_FFMPEG_API int cvRetrieveFrame_FFMPEG(struct CvCapture_FFMPEG* capture, unsigned char** data,
|
||||
int* step, int* width, int* height, int* cn);
|
||||
OPENCV_FFMPEG_API void cvReleaseCapture_FFMPEG(struct CvCapture_FFMPEG** cap);
|
||||
|
||||
OPENCV_FFMPEG_API struct CvVideoWriter_FFMPEG* cvCreateVideoWriter_FFMPEG(const char* filename,
|
||||
@ -53,7 +54,8 @@ OPENCV_FFMPEG_API void cvReleaseVideoWriter_FFMPEG(struct CvVideoWriter_FFMPEG**
|
||||
typedef CvCapture_FFMPEG* (*CvCreateFileCapture_Plugin)( const char* filename );
|
||||
typedef CvCapture_FFMPEG* (*CvCreateCameraCapture_Plugin)( int index );
|
||||
typedef int (*CvGrabFrame_Plugin)( CvCapture_FFMPEG* capture_handle );
|
||||
typedef int (*CvRetrieveFrame_Plugin)( CvCapture_FFMPEG* capture_handle, cv::Mat &mat);
|
||||
typedef int (*CvRetrieveFrame_Plugin)( CvCapture_FFMPEG* capture_handle, unsigned char** data, int* step,
|
||||
int* width, int* height, int* cn );
|
||||
typedef int (*CvSetCaptureProperty_Plugin)( CvCapture_FFMPEG* capture_handle, int prop_id, double value );
|
||||
typedef double (*CvGetCaptureProperty_Plugin)( CvCapture_FFMPEG* capture_handle, int prop_id );
|
||||
typedef void (*CvReleaseCapture_Plugin)( CvCapture_FFMPEG** capture_handle );
|
||||
|
@ -481,7 +481,7 @@ struct CvCapture_FFMPEG
|
||||
double getProperty(int) const;
|
||||
bool setProperty(int, double);
|
||||
bool grabFrame();
|
||||
bool retrieveFrame(int, cv::Mat &mat);
|
||||
bool retrieveFrame(int, unsigned char** data, int* step, int* width, int* height, int* cn);
|
||||
void rotateFrame(cv::Mat &mat) const;
|
||||
|
||||
void init();
|
||||
@ -564,11 +564,17 @@ void CvCapture_FFMPEG::init()
|
||||
frame_number = 0;
|
||||
eps_zero = 0.000025;
|
||||
|
||||
rotation_auto = true;
|
||||
rotation_angle = 0;
|
||||
|
||||
#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0)
|
||||
#if (LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0))
|
||||
#if (LIBAVUTIL_BUILD >= CALC_FFMPEG_VERSION(52, 92, 100))
|
||||
rotation_auto = true;
|
||||
#else
|
||||
rotation_auto = false;
|
||||
#endif
|
||||
dict = NULL;
|
||||
#else
|
||||
rotation_auto = false;
|
||||
#endif
|
||||
|
||||
rawMode = false;
|
||||
@ -1287,7 +1293,7 @@ bool CvCapture_FFMPEG::grabFrame()
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool CvCapture_FFMPEG::retrieveFrame(int, cv::Mat &mat)
|
||||
bool CvCapture_FFMPEG::retrieveFrame(int, unsigned char** data, int* step, int* width, int* height, int* cn)
|
||||
{
|
||||
if (!video_st)
|
||||
return false;
|
||||
@ -1295,11 +1301,12 @@ bool CvCapture_FFMPEG::retrieveFrame(int, cv::Mat &mat)
|
||||
if (rawMode)
|
||||
{
|
||||
AVPacket& p = bsfc ? packet_filtered : packet;
|
||||
if (p.data == NULL)
|
||||
return false;
|
||||
|
||||
mat = cv::Mat(1, p.size, CV_MAKETYPE(CV_8U, 1), p.data, p.size);
|
||||
return true;
|
||||
*data = p.data;
|
||||
*step = p.size;
|
||||
*width = p.size;
|
||||
*height = 1;
|
||||
*cn = 1;
|
||||
return p.data != NULL;
|
||||
}
|
||||
|
||||
if (!picture->data[0])
|
||||
@ -1362,30 +1369,15 @@ bool CvCapture_FFMPEG::retrieveFrame(int, cv::Mat &mat)
|
||||
rgb_picture.linesize
|
||||
);
|
||||
|
||||
mat = cv::Mat(frame.height, frame.width, CV_MAKETYPE(CV_8U, frame.cn), frame.data, frame.step);
|
||||
rotateFrame(mat);
|
||||
*data = frame.data;
|
||||
*step = frame.step;
|
||||
*width = frame.width;
|
||||
*height = frame.height;
|
||||
*cn = frame.cn;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CvCapture_FFMPEG::rotateFrame(cv::Mat &mat) const {
|
||||
if(!rotation_auto || rotation_angle%360 == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
cv::RotateFlags flag;
|
||||
if(rotation_angle == 90 || rotation_angle == -270) { // Rotate clockwise 90 degrees
|
||||
flag = cv::ROTATE_90_CLOCKWISE;
|
||||
} else if(rotation_angle == 270 || rotation_angle == -90) { // Rotate clockwise 270 degrees
|
||||
flag = cv::ROTATE_90_COUNTERCLOCKWISE;
|
||||
} else if(rotation_angle == 180 || rotation_angle == -180) { // Rotate clockwise 180 degrees
|
||||
flag = cv::ROTATE_180;
|
||||
} else { // Unsupported rotation
|
||||
return;
|
||||
}
|
||||
|
||||
cv::rotate(mat, mat, flag);
|
||||
}
|
||||
|
||||
double CvCapture_FFMPEG::getProperty( int property_id ) const
|
||||
{
|
||||
if( !video_st ) return 0;
|
||||
@ -1409,9 +1401,9 @@ double CvCapture_FFMPEG::getProperty( int property_id ) const
|
||||
case CV_FFMPEG_CAP_PROP_FRAME_COUNT:
|
||||
return (double)get_total_frames();
|
||||
case CV_FFMPEG_CAP_PROP_FRAME_WIDTH:
|
||||
return (double)((rotation_auto && rotation_angle%360) ? frame.height : frame.width);
|
||||
return (double)((rotation_auto && rotation_angle%180) ? frame.height : frame.width);
|
||||
case CV_FFMPEG_CAP_PROP_FRAME_HEIGHT:
|
||||
return (double)((rotation_auto && rotation_angle%360) ? frame.width : frame.height);
|
||||
return (double)((rotation_auto && rotation_angle%180) ? frame.width : frame.height);
|
||||
case CV_FFMPEG_CAP_PROP_FPS:
|
||||
return get_fps();
|
||||
case CV_FFMPEG_CAP_PROP_FOURCC:
|
||||
@ -1458,7 +1450,12 @@ double CvCapture_FFMPEG::getProperty( int property_id ) const
|
||||
case CV_FFMPEG_CAP_PROP_ORIENTATION_META:
|
||||
return static_cast<double>(rotation_angle);
|
||||
case CV_FFMPEG_CAP_PROP_ORIENTATION_AUTO:
|
||||
#if ((LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0)) && \
|
||||
(LIBAVUTIL_BUILD >= CALC_FFMPEG_VERSION(52, 94, 100)))
|
||||
return static_cast<double>(rotation_auto);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -1540,9 +1537,12 @@ double CvCapture_FFMPEG::dts_to_sec(int64_t dts) const
|
||||
void CvCapture_FFMPEG::get_rotation_angle()
|
||||
{
|
||||
rotation_angle = 0;
|
||||
#if ((LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0)) && \
|
||||
(LIBAVUTIL_BUILD >= CALC_FFMPEG_VERSION(52, 94, 100)))
|
||||
AVDictionaryEntry *rotate_tag = av_dict_get(video_st->metadata, "rotate", NULL, 0);
|
||||
if (rotate_tag != NULL)
|
||||
rotation_angle = atoi(rotate_tag->value);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CvCapture_FFMPEG::seek(int64_t _frame_number)
|
||||
@ -1641,7 +1641,14 @@ bool CvCapture_FFMPEG::setProperty( int property_id, double value )
|
||||
return setRaw();
|
||||
return false;
|
||||
case CV_FFMPEG_CAP_PROP_ORIENTATION_AUTO:
|
||||
#if ((LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0)) && \
|
||||
(LIBAVUTIL_BUILD >= CALC_FFMPEG_VERSION(52, 94, 100)))
|
||||
rotation_auto = static_cast<bool>(value);
|
||||
return true;
|
||||
#else
|
||||
rotation_auto = 0;
|
||||
return false;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
@ -2670,9 +2677,9 @@ int cvGrabFrame_FFMPEG(CvCapture_FFMPEG* capture)
|
||||
return capture->grabFrame();
|
||||
}
|
||||
|
||||
int cvRetrieveFrame_FFMPEG(CvCapture_FFMPEG* capture, cv::Mat &mat)
|
||||
int cvRetrieveFrame_FFMPEG(CvCapture_FFMPEG* capture, unsigned char** data, int* step, int* width, int* height, int* cn)
|
||||
{
|
||||
return capture->retrieveFrame(0, mat);
|
||||
return capture->retrieveFrame(0, data, step, width, height, cn);
|
||||
}
|
||||
|
||||
CvVideoWriter_FFMPEG* cvCreateVideoWriter_FFMPEG( const char* filename, int fourcc, double fps,
|
||||
|
@ -636,5 +636,57 @@ const ffmpeg_cap_properties_param_t videoio_ffmpeg_properties[] = {
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(videoio, ffmpeg_cap_properties, testing::ValuesIn(videoio_ffmpeg_properties));
|
||||
|
||||
// related issue: https://github.com/opencv/opencv/issues/15499
|
||||
TEST(videoio, mp4_orientation_meta_auto)
|
||||
{
|
||||
string video_file = string(cvtest::TS::ptr()->get_data_path()) + "video/big_buck_bunny_rotated.mp4";
|
||||
|
||||
VideoCapture cap;
|
||||
EXPECT_NO_THROW(cap.open(video_file, CAP_FFMPEG));
|
||||
ASSERT_TRUE(cap.isOpened()) << "Can't open the video: " << video_file << " with backend " << CAP_FFMPEG << std::endl;
|
||||
|
||||
cap.set(CAP_PROP_ORIENTATION_AUTO, true);
|
||||
if (cap.get(CAP_PROP_ORIENTATION_AUTO) == 0)
|
||||
throw SkipTestException("FFmpeg frame rotation metadata is not supported");
|
||||
|
||||
Size actual;
|
||||
EXPECT_NO_THROW(actual = Size((int)cap.get(CAP_PROP_FRAME_WIDTH),
|
||||
(int)cap.get(CAP_PROP_FRAME_HEIGHT)));
|
||||
EXPECT_EQ(384, actual.width);
|
||||
EXPECT_EQ(672, actual.height);
|
||||
|
||||
Mat frame;
|
||||
|
||||
cap >> frame;
|
||||
|
||||
ASSERT_EQ(384, frame.cols);
|
||||
ASSERT_EQ(672, frame.rows);
|
||||
}
|
||||
|
||||
// related issue: https://github.com/opencv/opencv/issues/15499
|
||||
TEST(videoio, mp4_orientation_no_rotation)
|
||||
{
|
||||
string video_file = string(cvtest::TS::ptr()->get_data_path()) + "video/big_buck_bunny_rotated.mp4";
|
||||
|
||||
VideoCapture cap;
|
||||
EXPECT_NO_THROW(cap.open(video_file, CAP_FFMPEG));
|
||||
cap.set(CAP_PROP_ORIENTATION_AUTO, 0);
|
||||
ASSERT_TRUE(cap.isOpened()) << "Can't open the video: " << video_file << " with backend " << CAP_FFMPEG << std::endl;
|
||||
ASSERT_FALSE(cap.get(CAP_PROP_ORIENTATION_AUTO));
|
||||
|
||||
Size actual;
|
||||
EXPECT_NO_THROW(actual = Size((int)cap.get(CAP_PROP_FRAME_WIDTH),
|
||||
(int)cap.get(CAP_PROP_FRAME_HEIGHT)));
|
||||
EXPECT_EQ(672, actual.width);
|
||||
EXPECT_EQ(384, actual.height);
|
||||
|
||||
Mat frame;
|
||||
|
||||
cap >> frame;
|
||||
|
||||
ASSERT_EQ(672, frame.cols);
|
||||
ASSERT_EQ(384, frame.rows);
|
||||
}
|
||||
|
||||
#endif
|
||||
}} // namespace
|
||||
|
Loading…
Reference in New Issue
Block a user