00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
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
00029
00030
00031
00032 OpenCVTexture::
00033 OpenCVTexture(const string &name) :
00034 VideoTexture(name)
00035 {
00036 }
00037
00038
00039
00040
00041
00042
00043
00044 OpenCVTexture::
00045 OpenCVTexture(const OpenCVTexture ©) :
00046 VideoTexture(copy),
00047 _pages(copy._pages)
00048 {
00049 nassertv(false);
00050 }
00051
00052
00053
00054
00055
00056
00057 OpenCVTexture::
00058 ~OpenCVTexture() {
00059 }
00060
00061
00062
00063
00064
00065
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 Texture::CDWriter cdata(Texture::_cycler, false);
00074 do_update_frame(cdata, frame);
00075 _current_frame = frame;
00076 } else {
00077
00078 Texture::CDWriter cdata(Texture::_cycler, false);
00079 int max_z = max(cdata->_z_size, (int)_pages.size());
00080 for (int z = 0; z < max_z; ++z) {
00081 VideoPage &page = _pages[z];
00082 if (!page._color.is_from_file() || !page._alpha.is_from_file()) {
00083 do_update_frame(cdata, frame, z);
00084 }
00085 }
00086 }
00087 _last_frame_update = this_frame;
00088 }
00089 }
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104 PT(Texture) OpenCVTexture::
00105 make_copy_impl() {
00106 Texture::CDReader cdata_tex(Texture::_cycler);
00107 PT(OpenCVTexture) copy = new OpenCVTexture(get_name());
00108 Texture::CDWriter cdata_copy_tex(copy->Texture::_cycler, true);
00109 copy->do_assign(cdata_copy_tex, this, cdata_tex);
00110
00111 return copy.p();
00112 }
00113
00114
00115
00116
00117
00118
00119 void OpenCVTexture::
00120 do_assign(Texture::CData *cdata_tex, const OpenCVTexture *copy,
00121 const Texture::CData *cdata_copy_tex) {
00122 VideoTexture::do_assign(cdata_tex, copy, cdata_copy_tex);
00123 _pages = copy->_pages;
00124 }
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139 bool OpenCVTexture::
00140 from_camera(int camera_index, int z, int alpha_file_channel,
00141 const LoaderOptions &options) {
00142 Texture::CDWriter cdata(Texture::_cycler, true);
00143 if (!do_reconsider_z_size(cdata, z, options)) {
00144 return false;
00145 }
00146 nassertr(z >= 0 && z < cdata->_z_size, false);
00147
00148 cdata->_alpha_file_channel = alpha_file_channel;
00149
00150 VideoPage &page = do_modify_page(cdata, z);
00151 if (alpha_file_channel == 0) {
00152
00153 page._alpha.clear();
00154 if (!page._color.from_camera(camera_index)) {
00155 return false;
00156 }
00157
00158 if (!do_reconsider_video_properties(cdata, page._color, 3, z, options)) {
00159 page._color.clear();
00160 return false;
00161 }
00162 } else {
00163
00164 page._color.clear();
00165 if (!page._alpha.from_camera(camera_index)) {
00166 return false;
00167 }
00168
00169 if (!do_reconsider_video_properties(cdata, page._alpha, 1, z, options)) {
00170 page._alpha.clear();
00171 return false;
00172 }
00173 do_set_format(cdata, F_alpha);
00174 }
00175
00176 cdata->_loaded_from_image = true;
00177 clear_current_frame();
00178 do_update_frame(cdata, 0);
00179 return true;
00180 }
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190 OpenCVTexture::VideoPage &OpenCVTexture::
00191 do_modify_page(const Texture::CData *cdata, int z) {
00192 nassertr(z < cdata->_z_size, _pages[0]);
00193 while (z >= (int)_pages.size()) {
00194 _pages.push_back(VideoPage());
00195 }
00196 return _pages[z];
00197 }
00198
00199
00200
00201
00202
00203
00204
00205
00206 bool OpenCVTexture::
00207 do_reconsider_video_properties(Texture::CData *cdata,
00208 const OpenCVTexture::VideoStream &stream,
00209 int num_components, int z,
00210 const LoaderOptions &options) {
00211 double frame_rate = 0.0f;
00212 int num_frames = 0;
00213
00214 if (stream.is_from_file()) {
00215 frame_rate = cvGetCaptureProperty(stream._capture, CV_CAP_PROP_FPS);
00216 num_frames = (int)cvGetCaptureProperty(stream._capture, CV_CAP_PROP_FRAME_COUNT);
00217 if (vision_cat.is_debug()) {
00218 vision_cat.debug()
00219 << "Loaded " << stream._filename << ", " << num_frames << " frames at "
00220 << frame_rate << " fps\n";
00221 }
00222 } else {
00223
00224
00225 if (vision_cat.is_debug()) {
00226 vision_cat.debug()
00227 << "Loaded camera stream\n";
00228 }
00229 }
00230
00231 int width = (int)cvGetCaptureProperty(stream._capture, CV_CAP_PROP_FRAME_WIDTH);
00232 int height = (int)cvGetCaptureProperty(stream._capture, CV_CAP_PROP_FRAME_HEIGHT);
00233
00234 int x_size = width;
00235 int y_size = height;
00236 do_adjust_this_size(cdata, x_size, y_size, get_name(), true);
00237
00238 if (vision_cat.is_debug()) {
00239 vision_cat.debug()
00240 << "Video stream is " << width << " by " << height
00241 << " pixels; fitting in texture " << x_size << " by "
00242 << y_size << " texels.\n";
00243 }
00244
00245 if (!do_reconsider_image_properties(cdata, x_size, y_size, num_components,
00246 T_unsigned_byte, z, options)) {
00247 return false;
00248 }
00249
00250 if (cdata->_loaded_from_image &&
00251 (get_video_width() != width || get_video_height() != height ||
00252 get_num_frames() != num_frames || get_frame_rate() != frame_rate)) {
00253 vision_cat.error()
00254 << "Video properties have changed for texture " << get_name()
00255 << " level " << z << ".\n";
00256 return false;
00257 }
00258
00259 set_frame_rate(frame_rate);
00260 set_num_frames(num_frames);
00261 set_video_size(width, height);
00262
00263
00264
00265 loop(true);
00266
00267 return true;
00268 }
00269
00270
00271
00272
00273
00274
00275
00276 PT(Texture) OpenCVTexture::
00277 make_texture() {
00278 return new OpenCVTexture;
00279 }
00280
00281
00282
00283
00284
00285
00286
00287 void OpenCVTexture::
00288 do_update_frame(Texture::CData *cdata, int frame) {
00289 int max_z = max(cdata->_z_size, (int)_pages.size());
00290 for (int z = 0; z < max_z; ++z) {
00291 do_update_frame(cdata, frame, z);
00292 }
00293 }
00294
00295
00296
00297
00298
00299
00300
00301 void OpenCVTexture::
00302 do_update_frame(Texture::CData *cdata, int frame, int z) {
00303 if (vision_cat.is_spam()) {
00304 vision_cat.spam()
00305 << "Updating OpenCVTexture page " << z << "\n";
00306 }
00307
00308 VideoPage &page = _pages[z];
00309 if (page._color.is_valid() || page._alpha.is_valid()) {
00310 do_modify_ram_image(cdata);
00311 ++(cdata->_image_modified);
00312 }
00313 int dest_x_pitch = cdata->_num_components * cdata->_component_width;
00314 int dest_y_pitch = cdata->_x_size * dest_x_pitch;
00315
00316 if (page._color.is_valid()) {
00317 nassertv(get_num_components() >= 3 && get_component_width() == 1);
00318
00319 const unsigned char *r, *g, *b;
00320 int x_pitch, y_pitch;
00321 if (page._color.get_frame_data(frame, r, g, b, x_pitch, y_pitch)) {
00322 nassertv(get_video_width() <= cdata->_x_size && get_video_height() <= cdata->_y_size);
00323 nassertv(!cdata->_ram_images.empty())
00324 unsigned char *dest = cdata->_ram_images[0]._image.p() + do_get_expected_ram_page_size(cdata) * z;
00325
00326 if (cdata->_num_components == 3 && x_pitch == 3) {
00327
00328 int copy_bytes = get_video_width() * dest_x_pitch;
00329 nassertv(copy_bytes <= dest_y_pitch && copy_bytes <= abs(y_pitch));
00330
00331 for (int y = 0; y < get_video_height(); ++y) {
00332 memcpy(dest, r, copy_bytes);
00333 dest += dest_y_pitch;
00334 r += y_pitch;
00335 }
00336
00337 } else {
00338
00339
00340
00341 for (int y = 0; y < get_video_height(); ++y) {
00342 int dx = 0;
00343 int sx = 0;
00344 for (int x = 0; x < get_video_width(); ++x) {
00345 dest[dx] = r[sx];
00346 dest[dx + 1] = g[sx];
00347 dest[dx + 2] = b[sx];
00348 dx += dest_x_pitch;
00349 sx += x_pitch;
00350 }
00351 dest += dest_y_pitch;
00352 r += y_pitch;
00353 g += y_pitch;
00354 b += y_pitch;
00355 }
00356 }
00357 }
00358 }
00359 if (page._alpha.is_valid()) {
00360 nassertv(get_component_width() == 1);
00361
00362 const unsigned char *source[3];
00363 int x_pitch, y_pitch;
00364 if (page._alpha.get_frame_data(frame, source[0], source[1], source[2],
00365 x_pitch, y_pitch)) {
00366 nassertv(get_video_width() <= cdata->_x_size && get_video_height() <= cdata->_y_size);
00367 nassertv(!cdata->_ram_images.empty())
00368 unsigned char *dest = cdata->_ram_images[0]._image.p() + do_get_expected_ram_page_size(cdata) * z;
00369
00370
00371
00372
00373 const unsigned char *sch = source[0];
00374 if (cdata->_alpha_file_channel >= 1 && cdata->_alpha_file_channel <= 3) {
00375 sch = source[cdata->_alpha_file_channel - 1];
00376 }
00377
00378 for (int y = 0; y < get_video_height(); ++y) {
00379
00380
00381 int dx = (cdata->_num_components - 1) * cdata->_component_width;
00382 int sx = 0;
00383 for (int x = 0; x < get_video_width(); ++x) {
00384 dest[dx] = sch[sx];
00385 dx += dest_x_pitch;
00386 sx += x_pitch;
00387 }
00388 dest += dest_y_pitch;
00389 sch += y_pitch;
00390 }
00391 }
00392 }
00393 }
00394
00395
00396
00397
00398
00399
00400
00401
00402 bool OpenCVTexture::
00403 do_read_one(Texture::CData *cdata,
00404 const Filename &fullpath, const Filename &alpha_fullpath,
00405 int z, int n, int primary_file_num_channels, int alpha_file_channel,
00406 const LoaderOptions &options,
00407 bool header_only, BamCacheRecord *record) {
00408 if (record != (BamCacheRecord *)NULL) {
00409 record->add_dependent_file(fullpath);
00410 }
00411
00412 nassertr(n == 0, false);
00413 nassertr(z >= 0 && z < cdata->_z_size, false);
00414
00415 VideoPage &page = do_modify_page(cdata, z);
00416 if (!page._color.read(fullpath)) {
00417 vision_cat.error()
00418 << "OpenCV couldn't read " << fullpath << " as video.\n";
00419 return false;
00420 }
00421 if (!alpha_fullpath.empty()) {
00422 if (!page._alpha.read(alpha_fullpath)) {
00423 vision_cat.error()
00424 << "OpenCV couldn't read " << alpha_fullpath << " as video.\n";
00425 page._color.clear();
00426 return false;
00427 }
00428 }
00429
00430 if (z == 0) {
00431 if (!has_name()) {
00432 set_name(fullpath.get_basename_wo_extension());
00433 }
00434
00435 if (cdata->_filename.empty()) {
00436 cdata->_filename = fullpath;
00437 cdata->_alpha_filename = alpha_fullpath;
00438 }
00439
00440 cdata->_fullpath = fullpath;
00441 cdata->_alpha_fullpath = alpha_fullpath;
00442 }
00443
00444 cdata->_primary_file_num_channels = 3;
00445 cdata->_alpha_file_channel = 0;
00446
00447 if (alpha_fullpath.empty()) {
00448
00449 if (!do_reconsider_video_properties(cdata, page._color, 3, z, options)) {
00450 page._color.clear();
00451 return false;
00452 }
00453
00454 } else {
00455
00456 cdata->_alpha_file_channel = alpha_file_channel;
00457
00458 if (!do_reconsider_video_properties(cdata, page._color, 4, z, options)) {
00459 page._color.clear();
00460 page._alpha.clear();
00461 return false;
00462 }
00463
00464 if (!do_reconsider_video_properties(cdata, page._alpha, 4, z, options)) {
00465 page._color.clear();
00466 page._alpha.clear();
00467 return false;
00468 }
00469 }
00470
00471 set_loaded_from_image();
00472 clear_current_frame();
00473 do_update_frame(cdata, 0);
00474 return true;
00475 }
00476
00477
00478
00479
00480
00481
00482
00483 bool OpenCVTexture::
00484 do_load_one(Texture::CData *cdata,
00485 const PNMImage &pnmimage, const string &name,
00486 int z, int n, const LoaderOptions &options) {
00487 if (z <= (int)_pages.size()) {
00488 VideoPage &page = do_modify_page(cdata, z);
00489 page._color.clear();
00490 page._alpha.clear();
00491 }
00492
00493 return Texture::do_load_one(cdata, pnmimage, name, z, n, options);
00494 }
00495
00496
00497
00498
00499
00500
00501 void OpenCVTexture::
00502 register_with_read_factory() {
00503
00504
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514 BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
00515 }
00516
00517
00518
00519
00520
00521
00522 OpenCVTexture::VideoStream::
00523 VideoStream() :
00524 _capture(NULL),
00525 _camera_index(-1),
00526 _next_frame(0)
00527 {
00528 }
00529
00530
00531
00532
00533
00534
00535 OpenCVTexture::VideoStream::
00536 VideoStream(const OpenCVTexture::VideoStream ©) :
00537 _capture(NULL),
00538 _camera_index(-1)
00539 {
00540
00541
00542 if (copy.is_valid()) {
00543 if (copy.is_from_file()) {
00544 read(copy._filename);
00545 } else {
00546 from_camera(copy._camera_index);
00547 }
00548 }
00549 }
00550
00551
00552
00553
00554
00555
00556 OpenCVTexture::VideoStream::
00557 ~VideoStream() {
00558 clear();
00559 }
00560
00561
00562
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584 bool OpenCVTexture::VideoStream::
00585 get_frame_data(int frame,
00586 const unsigned char *&r,
00587 const unsigned char *&g,
00588 const unsigned char *&b,
00589 int &x_pitch, int &y_pitch) {
00590 nassertr(is_valid(), false);
00591
00592 if (is_from_file() && _next_frame != frame) {
00593 cvSetCaptureProperty(_capture, CV_CAP_PROP_POS_FRAMES, frame);
00594 }
00595
00596 _next_frame = frame + 1;
00597 IplImage *image = cvQueryFrame(_capture);
00598 if (image == NULL) {
00599 return false;
00600 }
00601
00602 r = (const unsigned char *)image->imageData;
00603 g = r + 1;
00604 b = g + 1;
00605 x_pitch = 3;
00606 y_pitch = image->widthStep;
00607
00608 if (image->dataOrder == 1) {
00609
00610
00611 x_pitch = 1;
00612 g = r + image->height * y_pitch;
00613 b = g + image->height * y_pitch;
00614 }
00615
00616 if (image->origin == 0) {
00617
00618
00619
00620
00621 r += (image->height - 1) * y_pitch;
00622 g += (image->height - 1) * y_pitch;
00623 b += (image->height - 1) * y_pitch;
00624 y_pitch = -y_pitch;
00625 }
00626
00627 return true;
00628 }
00629
00630
00631
00632
00633
00634
00635
00636 bool OpenCVTexture::VideoStream::
00637 read(const Filename &filename) {
00638 clear();
00639
00640 string os_specific = filename.to_os_specific();
00641 _capture = cvCaptureFromFile(os_specific.c_str());
00642 if (_capture == NULL) {
00643 return false;
00644 }
00645 _filename = filename;
00646 return true;
00647 }
00648
00649
00650
00651
00652
00653
00654
00655 bool OpenCVTexture::VideoStream::
00656 from_camera(int camera_index) {
00657 clear();
00658
00659 _capture = cvCaptureFromCAM(camera_index);
00660 if (_capture == NULL) {
00661 return false;
00662 }
00663 _camera_index = camera_index;
00664 return true;
00665 }
00666
00667
00668
00669
00670
00671
00672
00673 void OpenCVTexture::VideoStream::
00674 clear() {
00675 if (_capture != NULL) {
00676 cvReleaseCapture(&_capture);
00677 _capture = NULL;
00678 }
00679 _filename = Filename();
00680 _camera_index = -1;
00681 _next_frame = 0;
00682 }
00683
00684 #endif // HAVE_OPENCV
00685