15 #include "pandabase.h"
18 #include "openCVTexture.h"
19 #include "clockObject.h"
20 #include "config_gobj.h"
21 #include "config_vision.h"
22 #include "bamReader.h"
23 #include "bamCacheRecord.h"
33 OpenCVTexture(
const string &name) :
45 OpenCVTexture(
const OpenCVTexture ©) :
70 if (this_frame != _last_frame_update) {
71 int frame = get_frame();
72 if (_current_frame != frame) {
74 do_update_frame(cdata, frame);
75 _current_frame = frame;
79 int max_z = max(cdata->_z_size, (
int)_pages.size());
80 for (
int z = 0; z < max_z; ++z) {
81 VideoPage &page = _pages[z];
82 if (!page._color.is_from_file() || !page._alpha.is_from_file()) {
83 do_update_frame(cdata, frame, z);
87 _last_frame_update = this_frame;
107 PT(OpenCVTexture) copy = new OpenCVTexture(get_name());
109 copy->do_assign(cdata_copy_tex, this, cdata_tex);
120 do_assign(
Texture::CData *cdata_tex, const OpenCVTexture *copy,
121 const
Texture::CData *cdata_copy_tex) {
122 VideoTexture::do_assign(cdata_tex, copy, cdata_copy_tex);
123 _pages = copy->_pages;
140 from_camera(
int camera_index,
int z,
int alpha_file_channel,
143 if (!do_reconsider_z_size(cdata, z, options)) {
146 nassertr(z >= 0 && z < cdata->_z_size,
false);
148 cdata->_alpha_file_channel = alpha_file_channel;
150 VideoPage &page = do_modify_page(cdata, z);
151 if (alpha_file_channel == 0) {
154 if (!page._color.from_camera(camera_index)) {
158 if (!do_reconsider_video_properties(cdata, page._color, 3, z, options)) {
165 if (!page._alpha.from_camera(camera_index)) {
169 if (!do_reconsider_video_properties(cdata, page._alpha, 1, z, options)) {
173 do_set_format(cdata, F_alpha);
176 cdata->_loaded_from_image =
true;
177 clear_current_frame();
178 do_update_frame(cdata, 0);
190 OpenCVTexture::VideoPage &OpenCVTexture::
191 do_modify_page(
const Texture::CData *cdata,
int z) {
192 nassertr(z < cdata->_z_size, _pages[0]);
193 while (z >= (
int)_pages.size()) {
194 _pages.push_back(VideoPage());
207 do_reconsider_video_properties(Texture::CData *cdata,
208 const OpenCVTexture::VideoStream &stream,
209 int num_components,
int z,
211 double frame_rate = 0.0f;
214 if (stream.is_from_file()) {
215 frame_rate = cvGetCaptureProperty(stream._capture, CV_CAP_PROP_FPS);
216 num_frames = (int)cvGetCaptureProperty(stream._capture, CV_CAP_PROP_FRAME_COUNT);
217 if (vision_cat.is_debug()) {
219 <<
"Loaded " << stream._filename <<
", " << num_frames <<
" frames at "
220 << frame_rate <<
" fps\n";
225 if (vision_cat.is_debug()) {
227 <<
"Loaded camera stream\n";
231 int width = (int)cvGetCaptureProperty(stream._capture, CV_CAP_PROP_FRAME_WIDTH);
232 int height = (int)cvGetCaptureProperty(stream._capture, CV_CAP_PROP_FRAME_HEIGHT);
236 do_adjust_this_size(cdata, x_size, y_size, get_name(),
true);
238 if (vision_cat.is_debug()) {
240 <<
"Video stream is " << width <<
" by " << height
241 <<
" pixels; fitting in texture " << x_size <<
" by "
242 << y_size <<
" texels.\n";
245 if (!do_reconsider_image_properties(cdata, x_size, y_size, num_components,
246 T_unsigned_byte, z, options)) {
250 if (cdata->_loaded_from_image &&
251 (get_video_width() != width || get_video_height() != height ||
252 get_num_frames() != num_frames || get_frame_rate() != frame_rate)) {
254 <<
"Video properties have changed for texture " << get_name()
255 <<
" level " << z <<
".\n";
259 set_frame_rate(frame_rate);
260 set_num_frames(num_frames);
261 set_video_size(width, height);
278 return new OpenCVTexture;
288 do_update_frame(Texture::CData *cdata,
int frame) {
289 int max_z = max(cdata->_z_size, (
int)_pages.size());
290 for (
int z = 0; z < max_z; ++z) {
291 do_update_frame(cdata, frame, z);
302 do_update_frame(Texture::CData *cdata,
int frame,
int z) {
303 if (vision_cat.is_spam()) {
305 <<
"Updating OpenCVTexture page " << z <<
"\n";
308 VideoPage &page = _pages[z];
309 if (page._color.is_valid() || page._alpha.is_valid()) {
310 do_modify_ram_image(cdata);
311 ++(cdata->_image_modified);
313 int dest_x_pitch = cdata->_num_components * cdata->_component_width;
314 int dest_y_pitch = cdata->_x_size * dest_x_pitch;
316 if (page._color.is_valid()) {
317 nassertv(get_num_components() >= 3 && get_component_width() == 1);
319 const unsigned char *r, *g, *b;
320 int x_pitch, y_pitch;
321 if (page._color.get_frame_data(frame, r, g, b, x_pitch, y_pitch)) {
322 nassertv(get_video_width() <= cdata->_x_size && get_video_height() <= cdata->_y_size);
323 nassertv(!cdata->_ram_images.empty())
324 unsigned char *dest = cdata->_ram_images[0]._image.p() + do_get_expected_ram_page_size(cdata) * z;
326 if (cdata->_num_components == 3 && x_pitch == 3) {
328 int copy_bytes = get_video_width() * dest_x_pitch;
329 nassertv(copy_bytes <= dest_y_pitch && copy_bytes <= abs(y_pitch));
331 for (
int y = 0; y < get_video_height(); ++y) {
332 memcpy(dest, r, copy_bytes);
333 dest += dest_y_pitch;
341 for (
int y = 0; y < get_video_height(); ++y) {
344 for (
int x = 0; x < get_video_width(); ++x) {
346 dest[dx + 1] = g[sx];
347 dest[dx + 2] = b[sx];
351 dest += dest_y_pitch;
359 if (page._alpha.is_valid()) {
360 nassertv(get_component_width() == 1);
362 const unsigned char *source[3];
363 int x_pitch, y_pitch;
364 if (page._alpha.get_frame_data(frame, source[0], source[1], source[2],
366 nassertv(get_video_width() <= cdata->_x_size && get_video_height() <= cdata->_y_size);
367 nassertv(!cdata->_ram_images.empty())
368 unsigned char *dest = cdata->_ram_images[0]._image.p() + do_get_expected_ram_page_size(cdata) * z;
373 const unsigned char *sch = source[0];
374 if (cdata->_alpha_file_channel >= 1 && cdata->_alpha_file_channel <= 3) {
375 sch = source[cdata->_alpha_file_channel - 1];
378 for (
int y = 0; y < get_video_height(); ++y) {
381 int dx = (cdata->_num_components - 1) * cdata->_component_width;
383 for (
int x = 0; x < get_video_width(); ++x) {
388 dest += dest_y_pitch;
403 do_read_one(Texture::CData *cdata,
405 int z,
int n,
int primary_file_num_channels,
int alpha_file_channel,
412 nassertr(n == 0,
false);
413 nassertr(z >= 0 && z < cdata->_z_size,
false);
415 VideoPage &page = do_modify_page(cdata, z);
416 if (!page._color.read(fullpath)) {
418 <<
"OpenCV couldn't read " << fullpath <<
" as video.\n";
421 if (!alpha_fullpath.empty()) {
422 if (!page._alpha.read(alpha_fullpath)) {
424 <<
"OpenCV couldn't read " << alpha_fullpath <<
" as video.\n";
435 if (cdata->_filename.empty()) {
436 cdata->_filename = fullpath;
437 cdata->_alpha_filename = alpha_fullpath;
440 cdata->_fullpath = fullpath;
441 cdata->_alpha_fullpath = alpha_fullpath;
444 cdata->_primary_file_num_channels = 3;
445 cdata->_alpha_file_channel = 0;
447 if (alpha_fullpath.empty()) {
449 if (!do_reconsider_video_properties(cdata, page._color, 3, z, options)) {
456 cdata->_alpha_file_channel = alpha_file_channel;
458 if (!do_reconsider_video_properties(cdata, page._color, 4, z, options)) {
464 if (!do_reconsider_video_properties(cdata, page._alpha, 4, z, options)) {
471 set_loaded_from_image();
472 clear_current_frame();
473 do_update_frame(cdata, 0);
484 do_load_one(Texture::CData *cdata,
485 const PNMImage &pnmimage,
const string &name,
487 if (z <= (
int)_pages.size()) {
488 VideoPage &page = do_modify_page(cdata, z);
493 return Texture::do_load_one(cdata, pnmimage, name, z, n, options);
502 register_with_read_factory() {
522 OpenCVTexture::VideoStream::
535 OpenCVTexture::VideoStream::
536 VideoStream(
const OpenCVTexture::VideoStream ©) :
542 if (copy.is_valid()) {
543 if (copy.is_from_file()) {
544 read(copy._filename);
546 from_camera(copy._camera_index);
556 OpenCVTexture::VideoStream::
584 bool OpenCVTexture::VideoStream::
585 get_frame_data(
int frame,
586 const unsigned char *&r,
587 const unsigned char *&g,
588 const unsigned char *&b,
589 int &x_pitch,
int &y_pitch) {
590 nassertr(is_valid(),
false);
592 if (is_from_file() && _next_frame != frame) {
593 cvSetCaptureProperty(_capture, CV_CAP_PROP_POS_FRAMES, frame);
596 _next_frame = frame + 1;
597 IplImage *image = cvQueryFrame(_capture);
602 r = (
const unsigned char *)image->imageData;
606 y_pitch = image->widthStep;
608 if (image->dataOrder == 1) {
612 g = r + image->height * y_pitch;
613 b = g + image->height * y_pitch;
616 if (image->origin == 0) {
621 r += (image->height - 1) * y_pitch;
622 g += (image->height - 1) * y_pitch;
623 b += (image->height - 1) * y_pitch;
636 bool OpenCVTexture::VideoStream::
641 _capture = cvCaptureFromFile(os_specific.c_str());
642 if (_capture == NULL) {
645 _filename = filename;
655 bool OpenCVTexture::VideoStream::
656 from_camera(
int camera_index) {
659 _capture = cvCaptureFromCAM(camera_index);
660 if (_capture == NULL) {
663 _camera_index = camera_index;
673 void OpenCVTexture::VideoStream::
675 if (_capture != NULL) {
676 cvReleaseCapture(&_capture);
684 #endif // HAVE_OPENCV
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
Specifies parameters that may be passed to the loader.
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
The base class for a family of animated Textures that take their input from a video source...
This template class calls PipelineCycler::read_unlocked(), and then provides a transparent read-only ...
The name of a file, such as a texture file or an Egg file.
An instance of this class is written to the front of a Bam or Txo file to make the file a cached inst...
int get_frame_count(Thread *current_thread=Thread::get_current_thread()) const
Returns the number of times tick() has been called since the ClockObject was created, or since it was last reset.
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
string to_os_specific() const
Converts the filename from our generic Unix-like convention (forward slashes starting with the root a...
void register_factory(TypeHandle handle, CreateFunc *func)
Registers a new kind of thing the Factory will be able to create.
string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
void add_dependent_file(const Filename &pathname)
Adds the indicated file to the list of files that will be loaded to generate the data in this record...
TypeHandle is the identifier used to differentiate C++ class types.