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