Panda3D
|
00001 // Filename: openCVTexture.cxx 00002 // Created by: zacpavlov (19Aug05) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "pandabase.h" 00016 00017 #ifdef HAVE_OPENCV 00018 #include "openCVTexture.h" 00019 #include "clockObject.h" 00020 #include "config_gobj.h" 00021 #include "config_vision.h" 00022 #include "bamReader.h" 00023 #include "bamCacheRecord.h" 00024 00025 TypeHandle OpenCVTexture::_type_handle; 00026 00027 //////////////////////////////////////////////////////////////////// 00028 // Function: OpenCVTexture::Constructor 00029 // Access: Published 00030 // Description: Sets up the texture to read frames from a camera 00031 //////////////////////////////////////////////////////////////////// 00032 OpenCVTexture:: 00033 OpenCVTexture(const string &name) : 00034 VideoTexture(name) 00035 { 00036 } 00037 00038 //////////////////////////////////////////////////////////////////// 00039 // Function: OpenCVTexture::Copy Constructor 00040 // Access: Protected 00041 // Description: Use OpenCVTexture::make_copy() to make a duplicate copy of 00042 // an existing OpenCVTexture. 00043 //////////////////////////////////////////////////////////////////// 00044 OpenCVTexture:: 00045 OpenCVTexture(const OpenCVTexture ©) : 00046 VideoTexture(copy), 00047 _pages(copy._pages) 00048 { 00049 nassertv(false); 00050 } 00051 00052 //////////////////////////////////////////////////////////////////// 00053 // Function: OpenCVTexture::Destructor 00054 // Access: Published, Virtual 00055 // Description: 00056 //////////////////////////////////////////////////////////////////// 00057 OpenCVTexture:: 00058 ~OpenCVTexture() { 00059 } 00060 00061 //////////////////////////////////////////////////////////////////// 00062 // Function: OpenCVTexture::consider_update 00063 // Access: Protected, Virtual 00064 // Description: Calls update_frame() if the current frame has 00065 // changed. 00066 //////////////////////////////////////////////////////////////////// 00067 void OpenCVTexture:: 00068 consider_update() { 00069 int this_frame = ClockObject::get_global_clock()->get_frame_count(); 00070 if (this_frame != _last_frame_update) { 00071 int frame = get_frame(); 00072 if (_current_frame != frame) { 00073 update_frame(frame); 00074 _current_frame = frame; 00075 } else { 00076 // Loop through the pages to see if there's any camera stream to update. 00077 int max_z = max(_z_size, (int)_pages.size()); 00078 for (int z = 0; z < max_z; ++z) { 00079 VideoPage &page = _pages[z]; 00080 if (!page._color.is_from_file() || !page._alpha.is_from_file()) { 00081 update_frame(frame, z); 00082 } 00083 } 00084 } 00085 _last_frame_update = this_frame; 00086 } 00087 } 00088 00089 //////////////////////////////////////////////////////////////////// 00090 // Function: OpenCVTexture::do_make_copy 00091 // Access: Protected, Virtual 00092 // Description: Returns a new copy of the same Texture. This copy, 00093 // if applied to geometry, will be copied into texture 00094 // as a separate texture from the original, so it will 00095 // be duplicated in texture memory (and may be 00096 // independently modified if desired). 00097 // 00098 // If the Texture is an OpenCVTexture, the resulting 00099 // duplicate may be animated independently of the 00100 // original. 00101 //////////////////////////////////////////////////////////////////// 00102 PT(Texture) OpenCVTexture:: 00103 do_make_copy() { 00104 PT(OpenCVTexture) tex = new OpenCVTexture(get_name()); 00105 tex->do_assign(*this); 00106 00107 return tex.p(); 00108 } 00109 00110 //////////////////////////////////////////////////////////////////// 00111 // Function: OpenCVTexture::do_assign 00112 // Access: Protected 00113 // Description: Implements make_copy(). 00114 //////////////////////////////////////////////////////////////////// 00115 void OpenCVTexture:: 00116 do_assign(const OpenCVTexture ©) { 00117 VideoTexture::do_assign(copy); 00118 _pages = copy._pages; 00119 } 00120 00121 //////////////////////////////////////////////////////////////////// 00122 // Function: OpenCVTexture::from_camera 00123 // Access: Published 00124 // Description: Sets up the OpenCVTexture (or the indicated page, if z 00125 // is specified) to accept its input from the camera 00126 // with the given index number, or the default camera if 00127 // the index number is -1 or unspecified. 00128 // 00129 // If alpha_file_channel is 0, then the camera image 00130 // becomes a normal RGB texture. If it is 1, 2, or 3, 00131 // then the camera image becomes an alpha texture, using 00132 // the indicated channel of the source. 00133 //////////////////////////////////////////////////////////////////// 00134 bool OpenCVTexture:: 00135 from_camera(int camera_index, int z, int alpha_file_channel, 00136 const LoaderOptions &options) { 00137 if (!do_reconsider_z_size(z)) { 00138 return false; 00139 } 00140 nassertr(z >= 0 && z < get_z_size(), false); 00141 00142 _alpha_file_channel = alpha_file_channel; 00143 00144 VideoPage &page = modify_page(z); 00145 if (alpha_file_channel == 0) { 00146 // A normal RGB texture. 00147 page._alpha.clear(); 00148 if (!page._color.from_camera(camera_index)) { 00149 return false; 00150 } 00151 00152 if (!do_reconsider_video_properties(page._color, 3, z, options)) { 00153 page._color.clear(); 00154 return false; 00155 } 00156 } else { 00157 // An alpha texture. 00158 page._color.clear(); 00159 if (!page._alpha.from_camera(camera_index)) { 00160 return false; 00161 } 00162 00163 if (!do_reconsider_video_properties(page._alpha, 1, z, options)) { 00164 page._alpha.clear(); 00165 return false; 00166 } 00167 do_set_format(F_alpha); 00168 } 00169 00170 set_loaded_from_image(); 00171 clear_current_frame(); 00172 update_frame(0); 00173 return true; 00174 } 00175 00176 //////////////////////////////////////////////////////////////////// 00177 // Function: OpenCVTexture::modify_page 00178 // Access: Private 00179 // Description: Returns a reference to the zth VideoPage (level) of 00180 // the texture. In the case of a 2-d texture, there is 00181 // only one page, level 0; but cube maps and 3-d 00182 // textures have more. 00183 //////////////////////////////////////////////////////////////////// 00184 OpenCVTexture::VideoPage &OpenCVTexture:: 00185 modify_page(int z) { 00186 nassertr(z < _z_size, _pages[0]); 00187 while (z >= (int)_pages.size()) { 00188 _pages.push_back(VideoPage()); 00189 } 00190 return _pages[z]; 00191 } 00192 00193 //////////////////////////////////////////////////////////////////// 00194 // Function: OpenCVTexture::do_reconsider_video_properties 00195 // Access: Private 00196 // Description: Resets the internal Texture properties when a new 00197 // video file is loaded. Returns true if the new image 00198 // is valid, false otherwise. 00199 //////////////////////////////////////////////////////////////////// 00200 bool OpenCVTexture:: 00201 do_reconsider_video_properties(const OpenCVTexture::VideoStream &stream, 00202 int num_components, int z, 00203 const LoaderOptions &options) { 00204 double frame_rate = 0.0f; 00205 int num_frames = 0; 00206 00207 if (stream.is_from_file()) { 00208 frame_rate = cvGetCaptureProperty(stream._capture, CV_CAP_PROP_FPS); 00209 num_frames = (int)cvGetCaptureProperty(stream._capture, CV_CAP_PROP_FRAME_COUNT); 00210 if (vision_cat.is_debug()) { 00211 vision_cat.debug() 00212 << "Loaded " << stream._filename << ", " << num_frames << " frames at " 00213 << frame_rate << " fps\n"; 00214 } 00215 } else { 00216 // In this case, we don't have a specific frame rate or number of 00217 // frames. Let both values remain at 0. 00218 if (vision_cat.is_debug()) { 00219 vision_cat.debug() 00220 << "Loaded camera stream\n"; 00221 } 00222 } 00223 00224 int width = (int)cvGetCaptureProperty(stream._capture, CV_CAP_PROP_FRAME_WIDTH); 00225 int height = (int)cvGetCaptureProperty(stream._capture, CV_CAP_PROP_FRAME_HEIGHT); 00226 00227 int x_size = width; 00228 int y_size = height; 00229 00230 if (Texture::get_textures_power_2() != ATS_none) { 00231 x_size = up_to_power_2(width); 00232 y_size = up_to_power_2(height); 00233 } 00234 00235 if (vision_cat.is_debug()) { 00236 vision_cat.debug() 00237 << "Video stream is " << width << " by " << height 00238 << " pixels; fitting in texture " << x_size << " by " 00239 << y_size << " texels.\n"; 00240 } 00241 00242 if (!do_reconsider_image_properties(x_size, y_size, num_components, 00243 T_unsigned_byte, z, options)) { 00244 return false; 00245 } 00246 00247 if (_loaded_from_image && 00248 (get_video_width() != width || get_video_height() != height || 00249 get_num_frames() != num_frames || get_frame_rate() != frame_rate)) { 00250 vision_cat.error() 00251 << "Video properties have changed for texture " << get_name() 00252 << " level " << z << ".\n"; 00253 return false; 00254 } 00255 00256 set_frame_rate(frame_rate); 00257 set_num_frames(num_frames); 00258 set_video_size(width, height); 00259 00260 // By default, the newly-loaded video stream will immediately start 00261 // looping. 00262 loop(true); 00263 00264 return true; 00265 } 00266 00267 //////////////////////////////////////////////////////////////////// 00268 // Function: OpenCVTexture::make_texture 00269 // Access: Public, Static 00270 // Description: A factory function to make a new OpenCVTexture, used 00271 // to pass to the TexturePool. 00272 //////////////////////////////////////////////////////////////////// 00273 PT(Texture) OpenCVTexture:: 00274 make_texture() { 00275 return new OpenCVTexture; 00276 } 00277 00278 //////////////////////////////////////////////////////////////////// 00279 // Function: OpenCVTexture::update_frame 00280 // Access: Protected, Virtual 00281 // Description: Called once per frame, as needed, to load the new 00282 // image contents. 00283 //////////////////////////////////////////////////////////////////// 00284 void OpenCVTexture:: 00285 update_frame(int frame) { 00286 int max_z = max(_z_size, (int)_pages.size()); 00287 for (int z = 0; z < max_z; ++z) { 00288 update_frame(frame, z); 00289 } 00290 } 00291 00292 //////////////////////////////////////////////////////////////////// 00293 // Function: OpenCVTexture::update_frame 00294 // Access: Protected, Virtual 00295 // Description: This variant of update_frame updates the 00296 // indicated page only. 00297 //////////////////////////////////////////////////////////////////// 00298 void OpenCVTexture:: 00299 update_frame(int frame, int z) { 00300 if (vision_cat.is_spam()) { 00301 vision_cat.spam() 00302 << "Updating OpenCVTexture page " << z << "\n"; 00303 } 00304 00305 VideoPage &page = _pages[z]; 00306 if (page._color.is_valid() || page._alpha.is_valid()) { 00307 do_modify_ram_image(); 00308 ++_image_modified; 00309 } 00310 ssize_t dest_x_pitch = _num_components * _component_width; 00311 ssize_t dest_y_pitch = _x_size * dest_x_pitch; 00312 00313 if (page._color.is_valid()) { 00314 nassertv(get_num_components() >= 3 && get_component_width() == 1); 00315 00316 const unsigned char *r, *g, *b; 00317 ssize_t x_pitch, y_pitch; 00318 if (page._color.get_frame_data(frame, r, g, b, x_pitch, y_pitch)) { 00319 nassertv(get_video_width() <= _x_size && get_video_height() <= _y_size); 00320 unsigned char *dest = _ram_images[0]._image.p() + do_get_expected_ram_page_size() * z; 00321 00322 if (_num_components == 3 && x_pitch == 3) { 00323 // The easy case--copy the whole thing in, row by row. 00324 ssize_t copy_bytes = get_video_width() * dest_x_pitch; 00325 nassertv(copy_bytes <= dest_y_pitch && copy_bytes <= abs(y_pitch)); 00326 00327 for (int y = 0; y < get_video_height(); ++y) { 00328 memcpy(dest, r, copy_bytes); 00329 dest += dest_y_pitch; 00330 r += y_pitch; 00331 } 00332 00333 } else { 00334 // The harder case--interleave in the color channels, pixel by 00335 // pixel, possibly leaving room for alpha. 00336 00337 for (int y = 0; y < get_video_height(); ++y) { 00338 ssize_t dx = 0; 00339 ssize_t sx = 0; 00340 for (int x = 0; x < get_video_width(); ++x) { 00341 dest[dx] = r[sx]; 00342 dest[dx + 1] = g[sx]; 00343 dest[dx + 2] = b[sx]; 00344 dx += dest_x_pitch; 00345 sx += x_pitch; 00346 } 00347 dest += dest_y_pitch; 00348 r += y_pitch; 00349 g += y_pitch; 00350 b += y_pitch; 00351 } 00352 } 00353 } 00354 } 00355 if (page._alpha.is_valid()) { 00356 nassertv(get_component_width() == 1); 00357 00358 const unsigned char *source[3]; 00359 ssize_t x_pitch, y_pitch; 00360 if (page._alpha.get_frame_data(frame, source[0], source[1], source[2], 00361 x_pitch, y_pitch)) { 00362 nassertv(get_video_width() <= _x_size && get_video_height() <= _y_size); 00363 unsigned char *dest = _ram_images[0]._image.p() + do_get_expected_ram_page_size() * z; 00364 00365 // Interleave the alpha in with the color, pixel by pixel. 00366 // Even though the alpha will probably be a grayscale video, 00367 // the OpenCV library presents it as RGB. 00368 const unsigned char *sch = source[0]; 00369 if (_alpha_file_channel >= 1 && _alpha_file_channel <= 3) { 00370 sch = source[_alpha_file_channel - 1]; 00371 } 00372 00373 for (int y = 0; y < get_video_height(); ++y) { 00374 // Start dx at _num_components - 1, which writes to the last 00375 // channel, i.e. the alpha channel. 00376 ssize_t dx = (_num_components - 1) * _component_width; 00377 ssize_t sx = 0; 00378 for (int x = 0; x < get_video_width(); ++x) { 00379 dest[dx] = sch[sx]; 00380 dx += dest_x_pitch; 00381 sx += x_pitch; 00382 } 00383 dest += dest_y_pitch; 00384 sch += y_pitch; 00385 } 00386 } 00387 } 00388 } 00389 00390 //////////////////////////////////////////////////////////////////// 00391 // Function: OpenCVTexture::do_read_one 00392 // Access: Protected, Virtual 00393 // Description: Combines a color and alpha video image from the two 00394 // indicated filenames. Both must be the same kind of 00395 // video with similar properties. 00396 //////////////////////////////////////////////////////////////////// 00397 bool OpenCVTexture:: 00398 do_read_one(const Filename &fullpath, const Filename &alpha_fullpath, 00399 int z, int n, int primary_file_num_channels, int alpha_file_channel, 00400 const LoaderOptions &options, 00401 bool header_only, BamCacheRecord *record) { 00402 if (record != (BamCacheRecord *)NULL) { 00403 record->add_dependent_file(fullpath); 00404 } 00405 00406 nassertr(n == 0, false); 00407 nassertr(z >= 0 && z < _z_size, false); 00408 00409 VideoPage &page = modify_page(z); 00410 if (!page._color.read(fullpath)) { 00411 vision_cat.error() 00412 << "OpenCV couldn't read " << fullpath << " as video.\n"; 00413 return false; 00414 } 00415 if (!alpha_fullpath.empty()) { 00416 if (!page._alpha.read(alpha_fullpath)) { 00417 vision_cat.error() 00418 << "OpenCV couldn't read " << alpha_fullpath << " as video.\n"; 00419 page._color.clear(); 00420 return false; 00421 } 00422 } 00423 00424 if (z == 0) { 00425 if (!has_name()) { 00426 set_name(fullpath.get_basename_wo_extension()); 00427 } 00428 // Don't use has_filename() here, it will cause a deadlock 00429 if (_filename.empty()) { 00430 _filename = fullpath; 00431 _alpha_filename = alpha_fullpath; 00432 } 00433 00434 _fullpath = fullpath; 00435 _alpha_fullpath = alpha_fullpath; 00436 } 00437 00438 _primary_file_num_channels = 3; 00439 _alpha_file_channel = 0; 00440 00441 if (alpha_fullpath.empty()) { 00442 // Only one RGB movie. 00443 if (!do_reconsider_video_properties(page._color, 3, z, options)) { 00444 page._color.clear(); 00445 return false; 00446 } 00447 00448 } else { 00449 // An RGB movie combined with an alpha movie. 00450 _alpha_file_channel = alpha_file_channel; 00451 00452 if (!do_reconsider_video_properties(page._color, 4, z, options)) { 00453 page._color.clear(); 00454 page._alpha.clear(); 00455 return false; 00456 } 00457 00458 if (!do_reconsider_video_properties(page._alpha, 4, z, options)) { 00459 page._color.clear(); 00460 page._alpha.clear(); 00461 return false; 00462 } 00463 } 00464 00465 set_loaded_from_image(); 00466 clear_current_frame(); 00467 update_frame(0); 00468 return true; 00469 } 00470 00471 //////////////////////////////////////////////////////////////////// 00472 // Function: OpenCVTexture::do_load_one 00473 // Access: Protected, Virtual 00474 // Description: Resets the texture (or the particular level of the 00475 // texture) to the indicated static image. 00476 //////////////////////////////////////////////////////////////////// 00477 bool OpenCVTexture:: 00478 do_load_one(const PNMImage &pnmimage, const string &name, 00479 int z, int n, const LoaderOptions &options) { 00480 if (z <= (int)_pages.size()) { 00481 VideoPage &page = modify_page(z); 00482 page._color.clear(); 00483 page._alpha.clear(); 00484 } 00485 00486 return Texture::do_load_one(pnmimage, name, z, n, options); 00487 } 00488 00489 //////////////////////////////////////////////////////////////////// 00490 // Function: OpenCVTexture::register_with_read_factory 00491 // Access: Public, Static 00492 // Description: Factory method to generate a Texture object 00493 //////////////////////////////////////////////////////////////////// 00494 void OpenCVTexture:: 00495 register_with_read_factory() { 00496 // Since Texture is such a funny object that is reloaded from the 00497 // TexturePool each time, instead of actually being read fully from 00498 // the bam file, and since the VideoTexture and OpenCVTexture 00499 // classes don't really add any useful data to the bam record, we 00500 // don't need to define make_from_bam(), fillin(), or 00501 // write_datagram() in this class--we just inherit the same 00502 // functions from Texture. 00503 00504 // We do, however, have to register this class with the BamReader, 00505 // to avoid warnings about creating the wrong kind of object from 00506 // the bam file. 00507 BamReader::get_factory()->register_factory(get_class_type(), make_from_bam); 00508 } 00509 00510 //////////////////////////////////////////////////////////////////// 00511 // Function: OpenCVTexture::VideoStream::Constructor 00512 // Access: Public 00513 // Description: 00514 //////////////////////////////////////////////////////////////////// 00515 OpenCVTexture::VideoStream:: 00516 VideoStream() : 00517 _capture(NULL), 00518 _camera_index(-1), 00519 _next_frame(0) 00520 { 00521 } 00522 00523 //////////////////////////////////////////////////////////////////// 00524 // Function: OpenCVTexture::VideoStream::Copy Constructor 00525 // Access: Public 00526 // Description: 00527 //////////////////////////////////////////////////////////////////// 00528 OpenCVTexture::VideoStream:: 00529 VideoStream(const OpenCVTexture::VideoStream ©) : 00530 _capture(NULL), 00531 _camera_index(-1) 00532 { 00533 // Rather than copying the _capture pointer, we must open a new 00534 // stream that references the same file. 00535 if (copy.is_valid()) { 00536 if (copy.is_from_file()) { 00537 read(copy._filename); 00538 } else { 00539 from_camera(copy._camera_index); 00540 } 00541 } 00542 } 00543 00544 //////////////////////////////////////////////////////////////////// 00545 // Function: OpenCVTexture::VideoStream::Copy Constructor 00546 // Access: Public 00547 // Description: 00548 //////////////////////////////////////////////////////////////////// 00549 OpenCVTexture::VideoStream:: 00550 ~VideoStream() { 00551 clear(); 00552 } 00553 00554 //////////////////////////////////////////////////////////////////// 00555 // Function: OpenCVTexture::VideoStream::get_frame_data 00556 // Access: Public 00557 // Description: Gets the data needed to traverse through the 00558 // decompressed buffer for the indicated frame number. 00559 // It is most efficient to call this in increasing order 00560 // of frame number. Returns true on success, false on 00561 // failure. 00562 // 00563 // In the case of a success indication (true return 00564 // value), the three pointers r, g, b are loaded with 00565 // the addresses of the three components of the 00566 // bottom-left pixel of the image. (They will be 00567 // adjacent in memory in the case of an interleaved 00568 // image, and separated in the case of a 00569 // separate-channel image.) The x_pitch value is filled 00570 // with the amount to add to each pointer to advance to 00571 // the pixel to the right; and the y_pitch value is 00572 // filled with the amount to add to each pointer to 00573 // advance to the pixel above. Note that these values 00574 // may be negative (particularly in the case of a 00575 // top-down image). 00576 //////////////////////////////////////////////////////////////////// 00577 bool OpenCVTexture::VideoStream:: 00578 get_frame_data(int frame, 00579 const unsigned char *&r, 00580 const unsigned char *&g, 00581 const unsigned char *&b, 00582 ssize_t &x_pitch, ssize_t &y_pitch) { 00583 nassertr(is_valid(), false); 00584 00585 if (is_from_file() && _next_frame != frame) { 00586 cvSetCaptureProperty(_capture, CV_CAP_PROP_POS_FRAMES, frame); 00587 } 00588 00589 _next_frame = frame + 1; 00590 IplImage *image = cvQueryFrame(_capture); 00591 if (image == NULL) { 00592 return false; 00593 } 00594 00595 r = (const unsigned char *)image->imageData; 00596 g = r + 1; 00597 b = g + 1; 00598 x_pitch = 3; 00599 y_pitch = image->widthStep; 00600 00601 if (image->dataOrder == 1) { 00602 // Separate channel images. That means a block of r, followed by 00603 // a block of g, followed by a block of b. 00604 x_pitch = 1; 00605 g = r + image->height * y_pitch; 00606 b = g + image->height * y_pitch; 00607 } 00608 00609 if (image->origin == 0) { 00610 // The image data starts with the top row and ends with the bottom 00611 // row--the opposite of Texture::_ram_data's storage convention. 00612 // Therefore, we must increment the initial pointers to the last 00613 // row, and count backwards. 00614 r += (image->height - 1) * y_pitch; 00615 g += (image->height - 1) * y_pitch; 00616 b += (image->height - 1) * y_pitch; 00617 y_pitch = -y_pitch; 00618 } 00619 00620 return true; 00621 } 00622 00623 //////////////////////////////////////////////////////////////////// 00624 // Function: OpenCVTexture::VideoStream::read 00625 // Access: Public 00626 // Description: Sets up the stream to read the indicated file. 00627 // Returns true on success, false on failure. 00628 //////////////////////////////////////////////////////////////////// 00629 bool OpenCVTexture::VideoStream:: 00630 read(const Filename &filename) { 00631 clear(); 00632 00633 string os_specific = filename.to_os_specific(); 00634 _capture = cvCaptureFromFile(os_specific.c_str()); 00635 if (_capture == NULL) { 00636 return false; 00637 } 00638 _filename = filename; 00639 return true; 00640 } 00641 00642 //////////////////////////////////////////////////////////////////// 00643 // Function: OpenCVTexture::VideoStream::from_camera 00644 // Access: Public 00645 // Description: Sets up the stream to display the indicated camera. 00646 // Returns true on success, false on failure. 00647 //////////////////////////////////////////////////////////////////// 00648 bool OpenCVTexture::VideoStream:: 00649 from_camera(int camera_index) { 00650 clear(); 00651 00652 _capture = cvCaptureFromCAM(camera_index); 00653 if (_capture == NULL) { 00654 return false; 00655 } 00656 _camera_index = camera_index; 00657 return true; 00658 } 00659 00660 //////////////////////////////////////////////////////////////////// 00661 // Function: OpenCVTexture::VideoStream::clear 00662 // Access: Public 00663 // Description: Stops the video playback and frees the associated 00664 // resources. 00665 //////////////////////////////////////////////////////////////////// 00666 void OpenCVTexture::VideoStream:: 00667 clear() { 00668 if (_capture != NULL) { 00669 cvReleaseCapture(&_capture); 00670 _capture = NULL; 00671 } 00672 _filename = Filename(); 00673 _camera_index = -1; 00674 _next_frame = 0; 00675 } 00676 00677 #endif // HAVE_OPENCV 00678