1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 //  By downloading, copying, installing or using the software you agree to this license.
6 //  If you do not agree to this license, do not download, install,
7 //  copy or use the software.
8 //
9 //
10 //                           License Agreement
11 //                For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
14 // Copyright (C) 2009, Willow Garage Inc., all rights reserved.
15 // Third party copyrights are property of their respective owners.
16 //
17 // Redistribution and use in source and binary forms, with or without modification,
18 // are permitted provided that the following conditions are met:
19 //
20 //   * Redistribution's of source code must retain the above copyright notice,
21 //     this list of conditions and the following disclaimer.
22 //
23 //   * Redistribution's in binary form must reproduce the above copyright notice,
24 //     this list of conditions and the following disclaimer in the documentation
25 //     and/or other materials provided with the distribution.
26 //
27 //   * The name of the copyright holders may not be used to endorse or promote products
28 //     derived from this software without specific prior written permission.
29 //
30 // This software is provided by the copyright holders and contributors "as is" and
31 // any express or implied warranties, including, but not limited to, the implied
32 // warranties of merchantability and fitness for a particular purpose are disclaimed.
33 // In no event shall the Intel Corporation or contributors be liable for any direct,
34 // indirect, incidental, special, exemplary, or consequential damages
35 // (including, but not limited to, procurement of substitute goods or services;
36 // loss of use, data, or profits; or business interruption) however caused
37 // and on any theory of liability, whether in contract, strict liability,
38 // or tort (including negligence or otherwise) arising in any way out of
39 // the use of this software, even if advised of the possibility of such damage.
40 //
41 //M*/
42 
43 #include "test_precomp.hpp"
44 #include "opencv2/videoio/videoio_c.h"
45 
46 using namespace cv;
47 using namespace std;
48 
49 namespace cvtest
50 {
51 
fourccToString(int fourcc)52 string fourccToString(int fourcc)
53 {
54     return format("%c%c%c%c", fourcc & 255, (fourcc >> 8) & 255, (fourcc >> 16) & 255, (fourcc >> 24) & 255);
55 }
56 
57 #ifdef HAVE_MSMF
58 const VideoFormat g_specific_fmt_list[] =
59 {
60         /*VideoFormat("wmv", CV_FOURCC_MACRO('d', 'v', '2', '5')),
61         VideoFormat("wmv", CV_FOURCC_MACRO('d', 'v', '5', '0')),
62         VideoFormat("wmv", CV_FOURCC_MACRO('d', 'v', 'c', ' ')),
63         VideoFormat("wmv", CV_FOURCC_MACRO('d', 'v', 'h', '1')),
64         VideoFormat("wmv", CV_FOURCC_MACRO('d', 'v', 'h', 'd')),
65         VideoFormat("wmv", CV_FOURCC_MACRO('d', 'v', 's', 'd')),
66         VideoFormat("wmv", CV_FOURCC_MACRO('d', 'v', 's', 'l')),
67         VideoFormat("wmv", CV_FOURCC_MACRO('H', '2', '6', '3')),
68         VideoFormat("wmv", CV_FOURCC_MACRO('M', '4', 'S', '2')),
69         VideoFormat("avi", CV_FOURCC_MACRO('M', 'J', 'P', 'G')),
70         VideoFormat("mp4", CV_FOURCC_MACRO('M', 'P', '4', 'S')),
71         VideoFormat("mp4", CV_FOURCC_MACRO('M', 'P', '4', 'V')),
72         VideoFormat("wmv", CV_FOURCC_MACRO('M', 'P', '4', '3')),
73         VideoFormat("wmv", CV_FOURCC_MACRO('M', 'P', 'G', '1')),
74         VideoFormat("wmv", CV_FOURCC_MACRO('M', 'S', 'S', '1')),
75         VideoFormat("wmv", CV_FOURCC_MACRO('M', 'S', 'S', '2')),*/
76 #if !defined(_M_ARM)
77         VideoFormat("wmv", CV_FOURCC_MACRO('W', 'M', 'V', '1')),
78         VideoFormat("wmv", CV_FOURCC_MACRO('W', 'M', 'V', '2')),
79 #endif
80         VideoFormat("wmv", CV_FOURCC_MACRO('W', 'M', 'V', '3')),
81         VideoFormat("avi", CV_FOURCC_MACRO('H', '2', '6', '4')),
82         //VideoFormat("wmv", CV_FOURCC_MACRO('W', 'V', 'C', '1')),
83         VideoFormat()
84 };
85 #else
86 const VideoFormat g_specific_fmt_list[] =
87 {
88     VideoFormat("avi", VideoWriter::fourcc('X', 'V', 'I', 'D')),
89     VideoFormat("avi", VideoWriter::fourcc('M', 'P', 'E', 'G')),
90     VideoFormat("avi", VideoWriter::fourcc('M', 'J', 'P', 'G')),
91     //VideoFormat("avi", VideoWriter::fourcc('I', 'Y', 'U', 'V')),
92     VideoFormat("mkv", VideoWriter::fourcc('X', 'V', 'I', 'D')),
93     VideoFormat("mkv", VideoWriter::fourcc('M', 'P', 'E', 'G')),
94     VideoFormat("mkv", VideoWriter::fourcc('M', 'J', 'P', 'G')),
95 #ifndef HAVE_GSTREAMER
96     VideoFormat("mov", VideoWriter::fourcc('m', 'p', '4', 'v')),
97 #endif
98     VideoFormat()
99 };
100 #endif
101 
102 }
103 
104 class CV_VideoIOTest : public cvtest::BaseTest
105 {
106 protected:
107     void ImageTest (const string& dir);
108     void VideoTest (const string& dir, const cvtest::VideoFormat& fmt);
109     void SpecificImageTest (const string& dir);
110     void SpecificVideoTest (const string& dir, const cvtest::VideoFormat& fmt);
111 
CV_VideoIOTest()112     CV_VideoIOTest() {}
~CV_VideoIOTest()113     ~CV_VideoIOTest() {}
114     virtual void run(int) = 0;
115 };
116 
117 class CV_ImageTest : public CV_VideoIOTest
118 {
119 public:
CV_ImageTest()120     CV_ImageTest() {}
~CV_ImageTest()121     ~CV_ImageTest() {}
122     void run(int);
123 };
124 
125 class CV_SpecificImageTest : public CV_VideoIOTest
126 {
127 public:
CV_SpecificImageTest()128     CV_SpecificImageTest() {}
~CV_SpecificImageTest()129     ~CV_SpecificImageTest() {}
130     void run(int);
131 };
132 
133 class CV_VideoTest : public CV_VideoIOTest
134 {
135 public:
CV_VideoTest()136     CV_VideoTest() {}
~CV_VideoTest()137     ~CV_VideoTest() {}
138     void run(int);
139 };
140 
141 class CV_SpecificVideoTest : public CV_VideoIOTest
142 {
143 public:
CV_SpecificVideoTest()144     CV_SpecificVideoTest() {}
~CV_SpecificVideoTest()145     ~CV_SpecificVideoTest() {}
146     void run(int);
147 };
148 
149 
ImageTest(const string & dir)150 void CV_VideoIOTest::ImageTest(const string& dir)
151 {
152     string _name = dir + string("../cv/shared/baboon.png");
153     ts->printf(ts->LOG, "reading image : %s\n", _name.c_str());
154 
155     Mat image = imread(_name);
156     image.convertTo(image, CV_8UC3);
157 
158     if (image.empty())
159     {
160         ts->set_failed_test_info(ts->FAIL_MISSING_TEST_DATA);
161         return;
162     }
163 
164     const string exts[] = {
165 #ifdef HAVE_PNG
166         "png",
167 #endif
168 #ifdef HAVE_TIFF
169         "tiff",
170 #endif
171 #ifdef HAVE_JPEG
172         "jpg",
173 #endif
174 #ifdef HAVE_JASPER
175         "jp2",
176 #endif
177 #if 0 /*defined HAVE_OPENEXR && !defined __APPLE__*/
178         "exr",
179 #endif
180         "bmp",
181         "ppm",
182         "ras"
183         };
184     const size_t ext_num = sizeof(exts)/sizeof(exts[0]);
185 
186     for(size_t i = 0; i < ext_num; ++i)
187     {
188         string ext = exts[i];
189         string full_name = cv::tempfile(ext.c_str());
190         ts->printf(ts->LOG, " full_name : %s\n", full_name.c_str());
191 
192         imwrite(full_name, image);
193 
194         Mat loaded = imread(full_name);
195         if (loaded.empty())
196         {
197             ts->printf(ts->LOG, "Reading failed at fmt=%s\n", ext.c_str());
198             ts->set_failed_test_info(ts->FAIL_MISMATCH);
199             continue;
200         }
201 
202         const double thresDbell = 20;
203         double psnr = cvtest::PSNR(loaded, image);
204         if (psnr < thresDbell)
205         {
206             ts->printf(ts->LOG, "Reading image from file: too big difference (=%g) with fmt=%s\n", psnr, ext.c_str());
207             ts->set_failed_test_info(ts->FAIL_BAD_ACCURACY);
208             continue;
209         }
210 
211         vector<uchar> from_file;
212 
213         FILE *f = fopen(full_name.c_str(), "rb");
214         fseek(f, 0, SEEK_END);
215         long len = ftell(f);
216         from_file.resize((size_t)len);
217         fseek(f, 0, SEEK_SET);
218         from_file.resize(fread(&from_file[0], 1, from_file.size(), f));
219         fclose(f);
220 
221         vector<uchar> buf;
222         imencode("." + exts[i], image, buf);
223 
224         if (buf != from_file)
225         {
226             ts->printf(ts->LOG, "Encoding failed with fmt=%s\n", ext.c_str());
227             ts->set_failed_test_info(ts->FAIL_MISMATCH);
228             continue;
229         }
230 
231         Mat buf_loaded = imdecode(Mat(buf), 1);
232 
233         if (buf_loaded.empty())
234         {
235             ts->printf(ts->LOG, "Decoding failed with fmt=%s\n", ext.c_str());
236             ts->set_failed_test_info(ts->FAIL_MISMATCH);
237             continue;
238         }
239 
240         psnr = cvtest::PSNR(buf_loaded, image);
241 
242         if (psnr < thresDbell)
243         {
244             ts->printf(ts->LOG, "Decoding image from memory: too small PSNR (=%gdb) with fmt=%s\n", psnr, ext.c_str());
245             ts->set_failed_test_info(ts->FAIL_MISMATCH);
246             continue;
247         }
248 
249     }
250 
251     ts->printf(ts->LOG, "end test function : ImagesTest \n");
252     ts->set_failed_test_info(ts->OK);
253 }
254 
255 
VideoTest(const string & dir,const cvtest::VideoFormat & fmt)256 void CV_VideoIOTest::VideoTest(const string& dir, const cvtest::VideoFormat& fmt)
257 {
258     string src_file = dir + "../cv/shared/video_for_test.avi";
259     string tmp_name = cv::tempfile((cvtest::fourccToString(fmt.fourcc) + "."  + fmt.ext).c_str());
260 
261     ts->printf(ts->LOG, "reading video : %s and converting it to %s\n", src_file.c_str(), tmp_name.c_str());
262 
263     CvCapture* cap = cvCaptureFromFile(src_file.c_str());
264 
265     if (!cap)
266     {
267         ts->set_failed_test_info(ts->FAIL_MISMATCH);
268         return;
269     }
270 
271     CvVideoWriter* writer = 0;
272     vector<Mat> frames;
273 
274     for(;;)
275     {
276         IplImage* img = cvQueryFrame( cap );
277 
278         if (!img)
279             break;
280 
281         frames.push_back(cv::cvarrToMat(img, true));
282 
283         if (writer == NULL)
284         {
285             writer = cvCreateVideoWriter(tmp_name.c_str(), fmt.fourcc, 24, cvGetSize(img));
286             if (writer == NULL)
287             {
288                 ts->printf(ts->LOG, "can't create writer (with fourcc : %s)\n",
289                            cvtest::fourccToString(fmt.fourcc).c_str());
290                 cvReleaseCapture( &cap );
291                 ts->set_failed_test_info(ts->FAIL_MISMATCH);
292                 return;
293             }
294         }
295 
296         cvWriteFrame(writer, img);
297     }
298 
299     cvReleaseVideoWriter( &writer );
300     cvReleaseCapture( &cap );
301 
302     CvCapture *saved = cvCaptureFromFile(tmp_name.c_str());
303     if (!saved)
304     {
305         ts->set_failed_test_info(ts->FAIL_MISMATCH);
306         return;
307     }
308 
309     const double thresDbell = 20;
310 
311     for(int i = 0;; i++)
312     {
313         IplImage* ipl1 = cvQueryFrame( saved );
314 
315         if (!ipl1)
316             break;
317 
318         Mat img = frames[i];
319         Mat img1 = cv::cvarrToMat(ipl1);
320 
321         double psnr = cvtest::PSNR(img1, img);
322         if (psnr < thresDbell)
323         {
324             ts->printf(ts->LOG, "Too low frame %d psnr = %gdb\n", i, psnr);
325             ts->set_failed_test_info(ts->FAIL_MISMATCH);
326 
327             //imwrite("original.png", img);
328             //imwrite("after_test.png", img1);
329             //Mat diff;
330             //absdiff(img, img1, diff);
331             //imwrite("diff.png", diff);
332 
333             break;
334         }
335     }
336 
337     cvReleaseCapture( &saved );
338 
339     ts->printf(ts->LOG, "end test function : ImagesVideo \n");
340 }
341 
SpecificImageTest(const string & dir)342 void CV_VideoIOTest::SpecificImageTest(const string& dir)
343 {
344     const size_t IMAGE_COUNT = 10;
345 
346     for (size_t i = 0; i < IMAGE_COUNT; ++i)
347     {
348         stringstream s; s << i;
349         string file_path = dir+"../python/images/QCIF_0"+s.str()+".bmp";
350         Mat image = imread(file_path);
351 
352         if (image.empty())
353         {
354             ts->set_failed_test_info(ts->FAIL_MISSING_TEST_DATA);
355             return;
356         }
357 
358         resize(image, image, Size(968, 757), 0.0, 0.0, INTER_CUBIC);
359 
360         stringstream s_digit; s_digit << i;
361 
362         string full_name = cv::tempfile((s_digit.str() + ".bmp").c_str());
363         ts->printf(ts->LOG, " full_name : %s\n", full_name.c_str());
364 
365         imwrite(full_name, image);
366 
367         Mat loaded = imread(full_name);
368         if (loaded.empty())
369         {
370             ts->printf(ts->LOG, "Reading failed at fmt=bmp\n");
371             ts->set_failed_test_info(ts->FAIL_MISMATCH);
372             continue;
373         }
374 
375         const double thresDbell = 20;
376         double psnr = cvtest::PSNR(loaded, image);
377         if (psnr < thresDbell)
378         {
379             ts->printf(ts->LOG, "Reading image from file: too big difference (=%g) with fmt=bmp\n", psnr);
380             ts->set_failed_test_info(ts->FAIL_BAD_ACCURACY);
381             continue;
382         }
383 
384         vector<uchar> from_file;
385 
386         FILE *f = fopen(full_name.c_str(), "rb");
387         fseek(f, 0, SEEK_END);
388         long len = ftell(f);
389         from_file.resize((size_t)len);
390         fseek(f, 0, SEEK_SET);
391         from_file.resize(fread(&from_file[0], 1, from_file.size(), f));
392         fclose(f);
393 
394         vector<uchar> buf;
395         imencode(".bmp", image, buf);
396 
397         if (buf != from_file)
398         {
399             ts->printf(ts->LOG, "Encoding failed with fmt=bmp\n");
400             ts->set_failed_test_info(ts->FAIL_MISMATCH);
401             continue;
402         }
403 
404         Mat buf_loaded = imdecode(Mat(buf), 1);
405 
406         if (buf_loaded.empty())
407         {
408             ts->printf(ts->LOG, "Decoding failed with fmt=bmp\n");
409             ts->set_failed_test_info(ts->FAIL_MISMATCH);
410             continue;
411         }
412 
413         psnr = cvtest::PSNR(buf_loaded, image);
414 
415         if (psnr < thresDbell)
416         {
417             ts->printf(ts->LOG, "Decoding image from memory: too small PSNR (=%gdb) with fmt=bmp\n", psnr);
418             ts->set_failed_test_info(ts->FAIL_MISMATCH);
419             continue;
420         }
421     }
422 
423     ts->printf(ts->LOG, "end test function : SpecificImageTest \n");
424     ts->set_failed_test_info(ts->OK);
425 }
426 
427 
SpecificVideoTest(const string & dir,const cvtest::VideoFormat & fmt)428 void CV_VideoIOTest::SpecificVideoTest(const string& dir, const cvtest::VideoFormat& fmt)
429 {
430     string ext = fmt.ext;
431     int fourcc = fmt.fourcc;
432 
433     string fourcc_str = cvtest::fourccToString(fourcc);
434     const string video_file = cv::tempfile((fourcc_str + "." + ext).c_str());
435 
436     Size frame_size(968 & -2, 757 & -2);
437     VideoWriter writer(video_file, fourcc, 25, frame_size, true);
438 
439     if (!writer.isOpened())
440     {
441         // call it repeatedly for easier debugging
442         VideoWriter writer2(video_file, fourcc, 25, frame_size, true);
443         ts->printf(ts->LOG, "Creating a video in %s...\n", video_file.c_str());
444         ts->printf(ts->LOG, "Cannot create VideoWriter object with codec %s.\n", fourcc_str.c_str());
445         ts->set_failed_test_info(ts->FAIL_MISMATCH);
446         return;
447     }
448 
449     const size_t IMAGE_COUNT = 30;
450     vector<Mat> images;
451 
452     for( size_t i = 0; i < IMAGE_COUNT; ++i )
453     {
454         string file_path = format("%s../python/images/QCIF_%02d.bmp", dir.c_str(), i);
455         Mat img = imread(file_path, IMREAD_COLOR);
456 
457         if (img.empty())
458         {
459             ts->printf(ts->LOG, "Creating a video in %s...\n", video_file.c_str());
460             ts->printf(ts->LOG, "Error: cannot read frame from %s.\n", file_path.c_str());
461             ts->printf(ts->LOG, "Continue creating the video file...\n");
462             ts->set_failed_test_info(ts->FAIL_INVALID_TEST_DATA);
463             break;
464         }
465 
466         for (int k = 0; k < img.rows; ++k)
467             for (int l = 0; l < img.cols; ++l)
468                 if (img.at<Vec3b>(k, l) == Vec3b::all(0))
469                     img.at<Vec3b>(k, l) = Vec3b(0, 255, 0);
470                 else img.at<Vec3b>(k, l) = Vec3b(0, 0, 255);
471 
472         resize(img, img, frame_size, 0.0, 0.0, INTER_CUBIC);
473 
474         images.push_back(img);
475         writer << img;
476     }
477 
478     writer.release();
479     VideoCapture cap(video_file);
480 
481     size_t FRAME_COUNT = (size_t)cap.get(CAP_PROP_FRAME_COUNT);
482 
483     size_t allowed_extra_frames = 0;
484 
485     // Hack! Newer FFmpeg versions in this combination produce a file
486     // whose reported duration is one frame longer than needed, and so
487     // the calculated frame count is also off by one. Ideally, we'd want
488     // to fix both writing (to produce the correct duration) and reading
489     // (to correctly report frame count for such files), but I don't know
490     // how to do either, so this is a workaround for now.
491     // See also the same hack in CV_PositioningTest::run.
492     if (fourcc == VideoWriter::fourcc('M', 'P', 'E', 'G') && ext == "mkv")
493         allowed_extra_frames = 1;
494 
495     // Hack! Some GStreamer encoding pipelines drop last frame in the video
496     int allowed_frame_frop = 0;
497 #ifdef HAVE_GSTREAMER
498     allowed_frame_frop = 1;
499 #endif
500 
501     if (FRAME_COUNT < IMAGE_COUNT - allowed_frame_frop || FRAME_COUNT > IMAGE_COUNT + allowed_extra_frames)
502     {
503         ts->printf(ts->LOG, "\nFrame count checking for video_%s.%s...\n", fourcc_str.c_str(), ext.c_str());
504         ts->printf(ts->LOG, "Video codec: %s\n", fourcc_str.c_str());
505         if (allowed_extra_frames != 0)
506             ts->printf(ts->LOG, "Required frame count: %d-%d; Returned frame count: %d\n",
507                        IMAGE_COUNT, IMAGE_COUNT + allowed_extra_frames, FRAME_COUNT);
508         else
509             ts->printf(ts->LOG, "Required frame count: %d; Returned frame count: %d\n", IMAGE_COUNT, FRAME_COUNT);
510         ts->printf(ts->LOG, "Error: Incorrect frame count in the video.\n");
511         ts->printf(ts->LOG, "Continue checking...\n");
512         ts->set_failed_test_info(ts->FAIL_BAD_ACCURACY);
513         return;
514     }
515 
516     for (int i = 0; (size_t)i < IMAGE_COUNT-allowed_frame_frop; i++)
517     {
518         Mat frame; cap >> frame;
519         if (frame.empty())
520         {
521             ts->printf(ts->LOG, "\nVideo file directory: %s\n", ".");
522             ts->printf(ts->LOG, "File name: video_%s.%s\n", fourcc_str.c_str(), ext.c_str());
523             ts->printf(ts->LOG, "Video codec: %s\n", fourcc_str.c_str());
524             ts->printf(ts->LOG, "Error: cannot read the next frame with index %d.\n", i+1);
525             ts->set_failed_test_info(ts->FAIL_MISSING_TEST_DATA);
526             break;
527         }
528 
529         Mat img = images[i];
530 
531         const double thresDbell = 40;
532         double psnr = cvtest::PSNR(img, frame);
533 
534         if (psnr > thresDbell)
535         {
536             ts->printf(ts->LOG, "\nReading frame from the file video_%s.%s...\n", fourcc_str.c_str(), ext.c_str());
537             ts->printf(ts->LOG, "Frame index: %d\n", i+1);
538             ts->printf(ts->LOG, "Difference between saved and original images: %g\n", psnr);
539             ts->printf(ts->LOG, "Maximum allowed difference: %g\n", thresDbell);
540             ts->printf(ts->LOG, "Error: too big difference between saved and original images.\n");
541             break;
542         }
543     }
544 }
545 
run(int)546 void CV_ImageTest::run(int)
547 {
548     ImageTest(ts->get_data_path());
549 }
550 
run(int)551 void CV_SpecificImageTest::run(int)
552 {
553     SpecificImageTest(ts->get_data_path());
554 }
555 
run(int)556 void CV_VideoTest::run(int)
557 {
558     for (int i = 0; ; ++i)
559     {
560         const cvtest::VideoFormat& fmt = cvtest::g_specific_fmt_list[i];
561         if( fmt.empty() )
562             break;
563         VideoTest(ts->get_data_path(), fmt);
564     }
565 }
566 
run(int)567 void CV_SpecificVideoTest::run(int)
568 {
569     for (int i = 0; ; ++i)
570     {
571         const cvtest::VideoFormat& fmt = cvtest::g_specific_fmt_list[i];
572         if( fmt.empty() )
573             break;
574         SpecificVideoTest(ts->get_data_path(), fmt);
575     }
576 }
577 
578 #ifdef HAVE_JPEG
TEST(Videoio_Image,regression)579 TEST(Videoio_Image, regression) { CV_ImageTest test; test.safe_run(); }
580 #endif
581 
582 #if BUILD_WITH_VIDEO_INPUT_SUPPORT && BUILD_WITH_VIDEO_OUTPUT_SUPPORT && !defined(__APPLE__)
TEST(Videoio_Video,regression)583 TEST(Videoio_Video, regression) { CV_VideoTest test; test.safe_run(); }
TEST(Videoio_Video,write_read)584 TEST(Videoio_Video, write_read) { CV_SpecificVideoTest test; test.safe_run(); }
585 #endif
586 
TEST(Videoio_Image,write_read)587 TEST(Videoio_Image, write_read) { CV_SpecificImageTest test; test.safe_run(); }
588