1 /*=========================================================================
2  *
3  *  Copyright Insight Software Consortium
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at
8  *
9  *         http://www.apache.org/licenses/LICENSE-2.0.txt
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  *=========================================================================*/
18 #include "itkVXLVideoIO.h"
19 
20 
21 namespace itk
22 {
23 
24 ///////////////////////////////////////////////////////////////////////////////
25 // Constructor, Destructor, and Print
26 //
27 
28 
29 //
30 // Constructor
31 //
VXLVideoIO()32 VXLVideoIO::VXLVideoIO()
33 {
34   this->ResetMembers();
35 }
36 
37 
38 //
39 // Destructor
40 //
~VXLVideoIO()41 VXLVideoIO::~VXLVideoIO()
42 {
43   this->FinishReadingOrWriting();
44 }
45 
46 
47 //
48 // PrintSelf
49 //
PrintSelf(std::ostream & os,Indent indent) const50 void VXLVideoIO::PrintSelf(std::ostream & os, Indent indent) const
51 {
52   Superclass::PrintSelf(os,indent);
53 
54   os << indent << "Reader Open: "<< this->m_ReaderOpen << std::endl;
55   os << indent << "Writer Open: "<< this->m_WriterOpen << std::endl;
56   os << indent << "Image dimensions: [" << this->m_Dimensions[0] <<","
57      << this->m_Dimensions[1]<<"]" << std::endl;
58   os << indent << "Frame Total: " << this->m_FrameTotal << std::endl;
59 }
60 
61 //
62 // FinishReadingOrWriting
63 //
FinishReadingOrWriting()64 void VXLVideoIO::FinishReadingOrWriting()
65 {
66   delete this->m_Writer;
67   this->m_Writer = nullptr;
68   delete this->m_Reader;
69   this->m_Reader = nullptr;
70 
71   this->ResetMembers();
72 }
73 
74 
75 ///////////////////////////////////////////////////////////////////////////////
76 // Member Accessors
77 //
78 
79 
80 //
81 // GetPositionInMSec
82 //
GetPositionInMSec() const83 VXLVideoIO::TemporalOffsetType VXLVideoIO::GetPositionInMSec() const
84 {
85   return this->m_PositionInMSec;
86 }
87 
88 //
89 // GetRatio
90 //
GetRatio() const91 VXLVideoIO::TemporalRatioType VXLVideoIO::GetRatio() const
92 {
93   return this->m_Ratio;
94 }
95 
96 //
97 // GetFrameTotal
98 //
GetFrameTotal() const99 VXLVideoIO::FrameOffsetType  VXLVideoIO::GetFrameTotal() const
100 {
101   return this->m_FrameTotal;
102 }
103 
104 //
105 // GetFramesPerSecond
106 //
GetFramesPerSecond() const107 VXLVideoIO::TemporalOffsetType VXLVideoIO::GetFramesPerSecond() const
108 {
109   return this->m_FramesPerSecond;
110 }
111 
112 //
113 // GetCurrentFrame
114 //
GetCurrentFrame() const115 VXLVideoIO::FrameOffsetType VXLVideoIO::GetCurrentFrame() const
116 {
117   return this->m_CurrentFrame;
118 }
119 
120 //
121 // GetIFrameInterval
122 //
GetIFrameInterval() const123 VXLVideoIO::FrameOffsetType VXLVideoIO::GetIFrameInterval() const
124 {
125   return this->m_IFrameInterval;
126 }
127 
128 //
129 // GetLastIFrame
130 //
GetLastIFrame() const131 VXLVideoIO::FrameOffsetType VXLVideoIO::GetLastIFrame() const
132 {
133   return this->m_LastIFrame;
134 }
135 
136 //
137 // SetCameraIndex
138 //
SetCameraIndex(int idx)139 void VXLVideoIO::SetCameraIndex(int idx)
140 {
141   this->m_CameraIndex = idx;
142 }
143 
144 //
145 // GetCameraIndex
146 //
GetCameraIndex()147 int VXLVideoIO::GetCameraIndex()
148 {
149   return this->m_CameraIndex;
150 }
151 
152 ///////////////////////////////////////////////////////////////////////////////
153 // Read related methods
154 //
155 
156 
157 //
158 // SetReadFromFile
159 //
SetReadFromFile()160 void VXLVideoIO::SetReadFromFile()
161 {
162   if (!this->m_ReaderOpen && !this->m_WriterOpen)
163     {
164     this->m_ReadType = ReadFromFile;
165     }
166   else
167     {
168     itkExceptionMacro("Cannot change read type while reader is open");
169     }
170 }
171 
172 //
173 // SetReadFromCamera
174 //
SetReadFromCamera()175 void VXLVideoIO::SetReadFromCamera()
176 {
177   if (!this->m_ReaderOpen && !this->m_WriterOpen)
178     {
179     this->m_ReadType = ReadFromCamera;
180     }
181   else
182     {
183     itkExceptionMacro("Cannot change read type while reader is open");
184     }
185 }
186 
187 //
188 // CanReadFile
189 //
CanReadFile(const char * filename)190 bool VXLVideoIO::CanReadFile(const char* filename)
191 {
192   // Make sure filename is specified
193   std::string fname = filename;
194   if (fname == "")
195     {
196     itkDebugMacro(<< "NoFilename specified");
197     return false;
198     }
199 
200   // Check File Extension (container type)
201   //
202   // Note: For now we only allow avi format, but this isn't right. We need to
203   //       support all formats that vxl does (which I believe is some subset
204   //       of all the formats that ffmpeg supports)
205   bool extensionFound = false;
206   std::string::size_type extPos = fname.rfind(".avi");
207   if ( extPos != std::string::npos && extPos == fname.length() - 4 )
208     {
209     extensionFound = true;
210     }
211   extPos = fname.rfind(".AVI");
212   if ( extPos != std::string::npos && extPos == fname.length() - 4 )
213     {
214     extensionFound = true;
215     }
216   if (!extensionFound)
217     {
218     itkDebugMacro(<< "Unrecognized file extension");
219     return false;
220     }
221 
222 
223   // Try opening to read
224   vidl_ffmpeg_istream localStream(filename);
225   if (!localStream.is_open())
226     {
227     return false;
228     }
229 
230   // Return true if successful
231   return true;
232 }
233 
234 //
235 // CanReadCamera
236 //
CanReadCamera(CameraIDType cameraID) const237 bool VXLVideoIO::CanReadCamera( CameraIDType  cameraID ) const
238 {
239   itkWarningMacro( << "For now, camera reading is not supported with VXL:"<<cameraID);
240   return false;
241 }
242 
243 
244 //
245 // ReadImageInformation
246 //
ReadImageInformation()247 void VXLVideoIO::ReadImageInformation()
248 {
249 
250   // Get information from camera
251   if (this->m_ReadType == ReadFromCamera)
252     {
253     itkExceptionMacro( << "For now, camera reading is not supported with VXL");
254     }
255 
256   // Get information from file
257   else if (this->m_ReadType == ReadFromFile)
258     {
259     // Set up local stream
260     vidl_ffmpeg_istream localStream(this->GetFileName());
261 
262     // Populate information
263     this->m_FrameTotal = localStream.num_frames();
264     this->m_Dimensions.clear();
265     this->m_Dimensions.push_back( localStream.width() );
266     this->m_Dimensions.push_back( localStream.height() );
267     this->m_PixelFormat = localStream.format();
268     this->m_FramesPerSecond = localStream.frame_rate();
269     this->m_NumberOfComponents = this->GetNChannelsFromPixelFormat(this->m_PixelFormat);
270 
271     // Assing the component type
272     unsigned int bytesPerPixel = this->GetSizeFromPixelFormat(this->m_PixelFormat);
273     if (bytesPerPixel == 0)
274       {
275       itkExceptionMacro("Faile to load local steam. FFMPEG libraries seems to be missing in VXL installation.");
276       }
277     if (bytesPerPixel == 1)
278       {
279       this->m_ComponentType = UCHAR;
280       }
281     else if (bytesPerPixel == 2)
282       {
283       this->m_ComponentType = UINT;
284       }
285     else
286       {
287       itkExceptionMacro("Unknown Pixel Component Type");
288       }
289 
290     // Try to figure out if there are I-Frame issues we need to worry about
291     // and compensate accrodingly
292     if (this->m_FrameTotal > 0)
293       {
294       localStream.advance();  // Advance to first frame (0)
295       localStream.advance();  // Try to advance to frame 1 and see what we get
296       this->m_IFrameInterval = localStream.frame_number();
297       if (this->m_IFrameInterval == 0)
298         {
299         itkExceptionMacro(<< "I-Frame spacing for this video is zeror! Please check input data.");
300         }
301       this->m_LastIFrame =
302         static_cast<FrameOffsetType>((float)this->m_FrameTotal / (float)this->m_IFrameInterval)
303         * this->m_IFrameInterval -1;
304 
305       // If the I-Frame spacing is not 1, warn the user
306       if (this->m_IFrameInterval != 1)
307         {
308         itkWarningMacro(<< "VXL can only seek to I-Frames. I-Frame spacing for this video is "
309           << this->m_IFrameInterval << ". Last I-Frame is " << this->m_LastIFrame);
310         }
311       }
312     }
313 
314   // Should never get here
315   else
316     {
317     itkExceptionMacro(<< "Invalid Read Type... How did we get here?");
318     }
319 }
320 
321 //
322 // Read
323 //
Read(void * buffer)324 void VXLVideoIO::Read(void *buffer)
325 {
326 
327   // Make sure we've already called ReadImageInformation (dimensions are non-zero)
328   if (this->m_Dimensions.size() != 2 ||
329       this->m_Dimensions[0] == 0 || this->m_Dimensions[1] == 0)
330     {
331     itkExceptionMacro(<< "Cannot read frame with zero dimension. May need to call ReadImageInformation");
332     }
333 
334   // If video is not already open, open it and keep it open
335   if (!this->m_ReaderOpen)
336     {
337     this->OpenReader();
338     }
339 
340   // Advance to the next frame if possible
341   if(!this->m_Reader->advance())
342     {
343     itkDebugMacro(<< "Could not advance to the next frame");
344     }
345 
346   // Read the current frame
347   this->m_VIDLFrame = this->m_Reader->current_frame();
348 
349   // Check to see if the pixel format needs converting at all
350   if (!this->PixelFormatSupported(this->m_PixelFormat))
351     {
352 
353     // Convert to RGBA (4 channels), RGB (3 channels), or mono (1 channel)
354     unsigned int pixelSize = GetSizeFromPixelFormat(this->m_PixelFormat);
355     if (this->m_NumberOfComponents == 4)
356       {
357       std::stringstream ss;
358       ss << "RGBA " << pixelSize*8*4;
359       this->m_VIDLFrame = vidl_convert_frame(this->m_VIDLFrame,
360                             vidl_pixel_format_from_string(ss.str()));
361       }
362     else if (this->m_NumberOfComponents == 3)
363       {
364       std::stringstream ss;
365       ss << "RGB " << pixelSize*8*3;
366       this->m_VIDLFrame = vidl_convert_frame(this->m_VIDLFrame,
367                             vidl_pixel_format_from_string(ss.str()));
368       }
369     else if (this->m_NumberOfComponents == 1)
370       {
371       std::stringstream ss;
372       ss << "MONO " << pixelSize*8;
373       this->m_VIDLFrame = vidl_convert_frame(this->m_VIDLFrame,
374                             vidl_pixel_format_from_string(ss.str().c_str()));
375       }
376     else
377       {
378       itkExceptionMacro(<< "Unsupported Pixel Format " << vidl_pixel_format_to_string(this->m_PixelFormat));
379       }
380 
381     }
382 
383   // Copy the data to the buffer
384   size_t bufferSize = this->m_VIDLFrame->size();
385   memcpy(buffer, this->m_VIDLFrame->data(), bufferSize);
386 }
387 
388 
389 //
390 // SetNextFrameToRead
391 //
SetNextFrameToRead(FrameOffsetType frameNumber)392 bool VXLVideoIO::SetNextFrameToRead( FrameOffsetType frameNumber)
393 {
394   // If the reader isn't open, open it
395   if (!this->m_ReaderOpen)
396     {
397     this->OpenReader();
398     }
399 
400   // Make sure we're not setting past the end
401   if (frameNumber > this->m_LastIFrame)
402     {
403     itkDebugMacro(<< "Warning: Trying to seek past end of video (past last I-Frame)");
404     return false;
405     }
406 
407   if (this->m_Reader->is_open())
408     {
409     this->m_Reader->seek_frame(frameNumber);
410     this->UpdateReaderProperties();
411     this->Modified();
412 
413     return true;
414     }
415   return false;
416 }
417 
418 ///////////////////////////////////////////////////////////////////////////////
419 // Write related methods
420 //
421 
422 
423 //
424 // CanWriteFile
425 //
CanWriteFile(const char * filename)426 bool VXLVideoIO::CanWriteFile(const char* filename)
427 {
428 
429   // Make sure reader is closed
430   if (this->m_ReaderOpen)
431     {
432     itkWarningMacro(<< "Can't write anything if reader is open");
433     return false;
434     }
435 
436   // Make sure filename is specified
437   std::string fname = filename;
438   if (fname == "")
439     {
440     itkWarningMacro(<< "No Filename specified");
441     return false;
442     }
443 
444   // Check File Extension (container type)
445   //
446   // Note: For now we only allow avi format, but this isn't right. We need to
447   //       support all formats that vxl does (which I believe is some subset
448   //       of all the formats that ffmpeg supports)
449   bool extensionFound = false;
450   std::string::size_type extPos = fname.rfind(".avi");
451   if ( extPos != std::string::npos && extPos == fname.length() - 4 )
452     {
453     extensionFound = true;
454     }
455   extPos = fname.rfind(".AVI");
456   if ( extPos != std::string::npos && extPos == fname.length() - 4 )
457     {
458     extensionFound = true;
459     }
460   if (!extensionFound)
461     {
462     itkWarningMacro(<< "Unrecognized file extension " << fname);
463     return false;
464     }
465 
466   return true;
467 }
468 
469 //
470 // WriteImageInformation
471 //
WriteImageInformation()472 void VXLVideoIO::WriteImageInformation()
473 {
474   // Don't do anything
475 }
476 
477 //
478 // SetWriterParameters
479 //
SetWriterParameters(TemporalRatioType fps,const std::vector<SizeValueType> & dim,const char * fourCC,unsigned int nChannels,IOComponentType componentType)480 void VXLVideoIO::SetWriterParameters(TemporalRatioType fps, const std::vector<SizeValueType>& dim,
481                                      const char* fourCC, unsigned int nChannels,
482                                      IOComponentType componentType)
483 {
484   if (this->m_ReaderOpen || this->m_WriterOpen)
485     {
486     itkExceptionMacro("Can not set the writer's parameters when either reader or writer is already open");
487     }
488 
489   if (componentType != UCHAR && componentType != UINT)
490     {
491     itkExceptionMacro("VXL IO only supports writing video with pixels of UCHAR and UINT");
492     }
493   else
494     {
495     this->m_ComponentType = componentType;
496     }
497 
498   if (dim.size() != 2)
499     {
500     itkExceptionMacro("VXL IO only supports 2D video");
501     }
502   this->m_Dimensions.clear();
503   this->m_Dimensions.push_back(dim[0]);
504   this->m_Dimensions.push_back(dim[1]);
505 
506   this->m_FramesPerSecond = fps;
507   this->m_Encoder = this->FourCCtoEncoderType(fourCC);
508   this->m_NumberOfComponents = nChannels;
509 
510   // Figure out the right pixel type to write out
511   if (this->m_NumberOfComponents == 4)
512     {
513     this->m_PixelType = RGBA;
514     std::stringstream ss;
515     ss << "RGBA " << this->m_NumberOfComponents * this->GetComponentSize() * 8;
516     this->m_PixelFormat = vidl_pixel_format_from_string(ss.str());
517     }
518   else if (this->m_NumberOfComponents == 3)
519     {
520     this->m_PixelType = RGB;
521     std::stringstream ss;
522     ss << "RGB " << this->m_NumberOfComponents * this->GetComponentSize() * 8;
523     this->m_PixelFormat = vidl_pixel_format_from_string(ss.str());
524     }
525   else if (this->m_NumberOfComponents == 1)
526     {
527     this->m_PixelType = SCALAR;
528     std::stringstream ss;
529     ss << "MONO " << this->m_NumberOfComponents * this->GetComponentSize() * 8;
530     this->m_PixelFormat = vidl_pixel_format_from_string(ss.str());
531     }
532   else
533     {
534     itkExceptionMacro(<< "Invalid number of channels " << this->m_NumberOfComponents);
535     }
536 }
537 
538 //
539 // Write
540 //
Write(const void * buffer)541 void VXLVideoIO::Write(const void *buffer)
542 {
543   // Make sure parameters are specified
544   if (this->m_FramesPerSecond == 0 || this->m_Dimensions.size() != 2 || this->m_Encoder == 0)
545     {
546     itkExceptionMacro("Can not write with empty parameters. You probably need to call SetWriterParameters");
547     }
548 
549   // If the writer isn't open yet, open it
550   if (!this->m_WriterOpen)
551     {
552     this->OpenWriter();
553     }
554 
555   // Create the output frame
556   this->m_VIDLFrame = new vidl_shared_frame(const_cast<void*>(buffer),
557     this->m_Dimensions[0], this->m_Dimensions[1], this->m_PixelFormat);
558 
559   // Write the frame out
560   this->m_Writer->write_frame(this->m_VIDLFrame);
561 }
562 
563 
564 ///////////////////////////////////////////////////////////////////////////////
565 // Protected methods
566 //
567 
568 //
569 // GetNChannelsFromPixelFormat
570 //
GetNChannelsFromPixelFormat(vidl_pixel_format fmt)571 unsigned int VXLVideoIO::GetNChannelsFromPixelFormat(vidl_pixel_format fmt)
572 {
573 
574   vidl_pixel_traits traits = vidl_pixel_format_traits(fmt);
575   return traits.num_channels;
576 }
577 
578 //
579 // GetSizeFromPixelFormat
580 //
GetSizeFromPixelFormat(vidl_pixel_format fmt)581 unsigned int VXLVideoIO::GetSizeFromPixelFormat(vidl_pixel_format fmt)
582 {
583 
584   vidl_pixel_traits traits = vidl_pixel_format_traits(fmt);
585   return traits.bits_per_pixel/8;
586 }
587 
588 //
589 // PixelFormatSupported
590 //
PixelFormatSupported(vidl_pixel_format fmt)591 bool VXLVideoIO::PixelFormatSupported(vidl_pixel_format fmt)
592 {
593   // Get a string representation of the format
594   std::string s = vidl_pixel_format_to_string(fmt);
595   vul_reg_exp reRGB(".*_RGB_.*");
596   if (reRGB.find(s))
597     {
598     return true;
599     }
600   vul_reg_exp reRGBA(".*_RGBA_.*");
601   if (reRGBA.find(s))
602     {
603     return true;
604     }
605   vul_reg_exp reMONO(".*_MONO_.*");
606   if (reMONO.find(s))
607     {
608     return true;
609     }
610 
611   return false;
612 }
613 
614 //
615 // FourCCtoEncoderType
616 //
FourCCtoEncoderType(const char * fourCC)617 vidl_ffmpeg_ostream_params::encoder_type VXLVideoIO::FourCCtoEncoderType(const char* fourCC)
618 {
619   if (!strcmp(fourCC, "DIVX"))
620     {
621     return vidl_ffmpeg_ostream_params::MPEG4;
622     }
623   else if (!strcmp(fourCC, "MP42"))
624     {
625     return vidl_ffmpeg_ostream_params::MSMPEG4V2;
626     }
627   else if (!strcmp(fourCC, "MP2V"))
628     {
629     return vidl_ffmpeg_ostream_params::MPEG2VIDEO;
630     }
631   else if (!strcmp(fourCC, "DVCS"))
632     {
633     return vidl_ffmpeg_ostream_params::DVVIDEO;
634     }
635   else if (!strcmp(fourCC, "Ljpg"))
636     {
637     return vidl_ffmpeg_ostream_params::LJPEG;
638     }
639   else if (!strcmp(fourCC, "raw "))
640     {
641     return vidl_ffmpeg_ostream_params::RAWVIDEO;
642     }
643   else
644     {
645     itkWarningMacro(<< "Unknown FourCC: " << fourCC);
646     return vidl_ffmpeg_ostream_params::DEFAULT;
647     }
648 }
649 
650 //
651 // UpdateReaderProperties
652 //
UpdateReaderProperties()653 void VXLVideoIO::UpdateReaderProperties()
654 {
655   this->m_CurrentFrame = this->m_Reader->frame_number();
656 
657   this->m_Ratio = (double)this->m_CurrentFrame / (double)this->m_FrameTotal;
658   this->m_PositionInMSec = this->m_Reader->duration() * this->m_Ratio;
659 
660 }
661 
662 //
663 // OpenReader
664 //
OpenReader()665 void VXLVideoIO::OpenReader()
666 {
667   if (this->m_ReaderOpen)
668     {
669     itkExceptionMacro("Can not open reader while video is already open for reading");
670     }
671 
672   if (this->m_WriterOpen)
673     {
674     itkExceptionMacro("Can not open reader while video is already open for writing");
675     }
676 
677   // If neither reader nor writer is currently open, open the reader
678   if (this->m_ReadType == ReadFromFile)
679     {
680     this->m_Reader = new vidl_ffmpeg_istream();
681     this->m_Reader->open(this->GetFileName());
682     if (this->m_Reader->is_open())
683       {
684       this->m_ReaderOpen = true;
685       }
686     else
687       {
688       itkExceptionMacro("Video failed to open");
689       }
690     }
691 
692   // Read from camera
693   else if (this->m_ReadType == ReadFromCamera)
694     {
695     itkWarningMacro(<< "VXL camera not currently implemented");
696     }
697 }
698 
699 //
700 // OpenWriter
701 //
OpenWriter()702 void VXLVideoIO::OpenWriter()
703 {
704   if (this->m_WriterOpen)
705     {
706     itkExceptionMacro("Can not open writer while video is already open for writing");
707     }
708 
709   if (this->m_ReaderOpen)
710     {
711     itkExceptionMacro("Can not open writer while video is already open for reading");
712     }
713 
714   vidl_ffmpeg_ostream_params parameters;
715   parameters.frame_rate_ = this->m_FramesPerSecond;
716   parameters.ni_ = this->m_Dimensions[0];
717   parameters.nj_ = this->m_Dimensions[1];
718   parameters.encoder_ = this->m_Encoder;
719 
720   this->m_Writer = new vidl_ffmpeg_ostream(this->GetFileName(), parameters);
721 
722   this->m_WriterOpen = true;
723 
724 }
725 
726 //
727 // ResetMembers
728 //
ResetMembers()729 void VXLVideoIO::ResetMembers()
730 {
731   this->m_PixelFormat = VIDL_PIXEL_FORMAT_UNKNOWN;
732   this->m_VIDLFrame = 0;
733   this->m_VIDLFrame = 0;
734   this->m_Encoder = vidl_ffmpeg_ostream_params::DEFAULT;
735   this->m_Reader = nullptr;
736   this->m_Writer = nullptr;
737   this->m_WriterOpen = false;
738   this->m_ReaderOpen = false;
739   this->m_FramesPerSecond = 0;
740   this->m_Dimensions.clear();
741   this->m_FrameTotal = 0;
742   this->m_CurrentFrame = 0;
743   this->m_IFrameInterval = 0;
744   this->m_LastIFrame = 0;
745 
746   // Default to reading from a file
747   this->m_ReadType = ReadFromFile;
748   this->m_CameraIndex = 0;
749 
750   // Members from ImageIOBase
751   this->m_PixelType = SCALAR;
752   this->m_ComponentType = UCHAR;
753   this->SetNumberOfDimensions(2);
754   this->m_Spacing[0] = 1.0;
755   this->m_Spacing[1] = 1.0;
756   this->m_Origin[0] = 0.0;
757   this->m_Origin[1] = 0.0;
758 }
759 
760 
761 } // end namespace itk
762