Copy sections of a video

Hi

I heard of OF some time ago but recently attended a workshop in Limerick delivered by Arturo and Pierre and was impressed with the possibilities. Great introduction, thanks! So I have decided to dip my toe in today. I’ve limited experience with C++ and its about 2 years since I did anything with it so apologies if my questions are not well framed…

What I am looking at right now is the possiblity of grabbing sections (e.g top left hand quarter) from several video files, such as QTs, stored on a local HD and copying the sections to play back as a collage of video elements in a single window.

I have used PHP quite a bit and I am thinking along the lines of combining functions such as imageCopy() and imageCreateFromJpeg(), where you can isolate a region in a source image and copy that section to a new image file and display it in a window. Is it possible to do something like this with ‘video’ in OF? There doesn’t appear to be anything obvious in the Video documentation but maybe there’s an addon lib I am not aware of.

Cheers
gM

there’s several possibilities:

  • you can get the pixels data by hand and load it in a texture to draw just that part. youll need a loop to go through the pixel data and get just the part you want. this can be really slow depending on the number of videos you want to draw.

  • also you can use some fbos:

http://addons.openframeworks.cc/project-…-fbotexture

allocate them to be the size of the part of each video you want to draw, draw the videos into them displaced to match the part you want and then draw the fbos to the screen.

  • another more posibility is to use an ofxCvColorImage to load the pixel data from the videos and use the setROI method before drawing them to draw only the part you want.

i suppose the last one is the easiest and probably not so slow, the second one should be the fastest but perhaps a little bit more complex. also if it’s too slow you can try disabling the use of textures in the videos as your not going to use them, by calling:

video.setUseTexture(false);

Hey Thanks for the quick response…

I had a look at the easiest option :oops: after doing some revision in C++ and put together the following by stripping out one examples in Open CV Addon examples:

I called the ofxCVColorImage.setROI() method and it appears to have cropped the video image alright; it does however continue to select the ROI from the top left corner of the video clip ignoring the parameters that I pass in to the setROI function. I marked out the line below in the code.


//testApp.cpp/////////////////////

#include “testApp.h”

//--------------------------------------------------------------
void testApp::setup(){

ofBackground(100,150,150);
vidPlayer.loadMovie(“fingers.mov”);
vidPlayer.play();
//vidPlayer.setSpeed(5.0);

colorImg01.allocate(320,240);
colorImg01.setROI(200,120,100,100); //I imagined this would have set the ROI 200px in from the left of the frame and 120px from the top with a width of 100px and height 100px. It appears to set the width and height but still starts the ROI at the top left (e.g. pixel(0,0)) in the video frame. Tried uncommenting the ROI Virtual functions in the “ofxCvColorImage.h” but no change or build errors.

}

//--------------------------------------------------------------
void testApp::update(){

colorImg01.setFromPixels(vidPlayer.getPixels(),320,240);
vidPlayer.idleMovie();

}

//--------------------------------------------------------------
void testApp::draw(){
colorImg01.draw(20,20,100,100);//positions and scales the image on the screen
}

haven’t really tested it but this should work:

  
---------------------------------  
//testApp.cpp/////////////////////  
---------------------------------  
  
#include "testApp.h"  
  
  
//--------------------------------------------------------------  
void testApp::setup(){  
  
ofBackground(100,150,150);  
vidPlayer.loadMovie("fingers.mov");  
vidPlayer.play();  
//vidPlayer.setSpeed(5.0);  
  
colorImg01.allocate(320,240);  
  
}  
  
//--------------------------------------------------------------  
void testApp::update(){  
  
vidPlayer.idleMovie();  
colorImg01.resetROI();  
colorImg01.setFromPixels(vidPlayer.getPixels(),320,240);  
colorImg01.setROI(200,120,100,100);   
  
}  
  
//--------------------------------------------------------------  
void testApp::draw(){  
colorImg01.draw(20,20,100,100);//positions and scales the image on the screen  
}   

Thanks that works a treat.!
Seems like the resetROI() method is critical.

Just on another issue is there anyone who is actively developing documentation on the functions in OF? I know from working in a number of different enviroments now that that having some code examples inside the function documentation can seriously reduce learning time and also makes it easier for you.
is it possible for members to post code examples into the documentation section of the OF site?
I am guessing I might be looking for a lot of help with OF in the future; I might feel less guilty about that if I could contribute something back to the documentation :slight_smile:

What I recently did, when I encountered the same challenge, was to add an alternative draw function to the ofTexture and ofVideoPlayer classes that uses alternative texture coordinates to draw the video. It does not take additional videomemory or copying pixels and is therefor very fast and efficient.

I will copy-paste some code here that may send you on your way:

  
  
// in ofTexture.h:  
...  
protected:  
	bool bAnchorIsPct;  
  
	float left;  
	float top;  
	float right;  
	float bottom;  
...  
  
// in ofTexture.cpp:  
void ofTexture::setSubsection(float x, float y, float w, float h){  
    if(texData.tex_t == 0) return;  
    if(texData.tex_u == 0) return;  
  
    left = max(0.0f, min(1.0f, x / texData.width));  
    top = max(0.0f, min(1.0f, y / texData.height));  
    right = max(0.0f, min(1.0f, left + (w / texData.width)));  
    bottom = max(0.0f, min(1.0f, top + (h / texData.height)));  
  
    // disable texture hack for now  
    bTexHackEnabled = false;  
}  
  
void ofTexture::resetSubsection(){  
    left = 0.0f;  
    top = 0.0f;  
    right = 1.0f;  
    bottom = 1.0f;  
  
    // TODO: only enable texture hack if it was previously enabled  
    bTexHackEnabled = true;  
}  
  
// in ofTexture::draw(float x, float y, float w, float h):  
...  
GLfloat tx0 = max(offsetw, left*texData.tex_t);  
GLfloat ty0 = max(offseth, top*texData.tex_u);  
GLfloat tx1 = min(right*texData.tex_t, texData.tex_t-offsetw);  
GLfloat ty1 = min(bottom*texData.tex_u, texData.tex_u-offseth);  
...  
  

You can then draw the top left quarter of any 320x240 video like this:

  
  
ofVideoPlayer video;  
  
...  
  
video.getTextureReference().setSubsection(0, 0, 160, 120);  
video.draw(100, 100, 160, 120);  
video.getTextureReference().resetSubsection();  
  
...  
  

You can also set the subsection once instead of resetting it every time.

IMPORTANT: the video needs to have a valid texture reference, otherwise your app will bug. You can prevent this by adding a ‘setSubsection()’ function to the video, which I did, and first check if the reference exists before calling its ‘setSubsection’ function. Same goes for ‘resetSubsection’. (edit: see my post below for an example)

You can draw each subsection multiple times without much performance degredation. You can also draw the video multiple times, each using its own subsection, without seeing your FPS drop significantly. And you can even draw a 160x120 subsection at a different width/height to stretch your video, all at a nice framerate. This is because all of the hard work is done on your graphics adapter and usually is blazingly fast. Even with higher resolutions, including full HD, if your videocard can handle it.

And yes, those function names suck, hehe. I am open to better naming suggestions. (edit: maybe setROI would be a nice one, hehe).

Hi Droozle

Thanks for the code… I’ve certainly blown some C++ cobwebs off the brain today. Its good…

I ran into some errors when I tried to build the code. I included it here in full with the sections you posted in place (colored orange).

If you have a chance, and notice anything I might have entered incorrectly, I would greatly appreciate it…
Cheers

///////////////////////////////////////////////////////////
///////////////////// OfTexture.h /////////////////////////

#ifndef _IMAGE_TEXTURE_H_
#define _IMAGE_TEXTURE_H_

#include “ofConstants.h”
#include “ofGraphics.h”

typedef struct{

bool bAllocated;
int glType;
int glTypeInternal;
int textureTarget;
float tex_t;
float tex_u;
float tex_w;
float tex_h;
float width;
float height;
bool bFlipTexture;

//we do this because openGL expects an array
//but we don’t want people to work with textureName[1]
//so we make textureID point to the same location
union{
struct{
unsigned int textureID; //use me
};
unsigned int textureName[1]; //don’t use me
};

}ofTextureData;

//enable / disable the slight offset we add to ofTexture’s texture coords to compensate for bad edge artifiacts
//enabled by default
void ofEnableTextureEdgeHack();
void ofDisableTectureEdgeHack();

class ofTexture : public ofBaseDraws{

public :

ofTexture();
virtual ~ofTexture();

// -----------------------------------------------------------------------
// we allow pass by copy and assignment operator
// it does a straight copy but you are getting the textureID of mom’s texture
// so this means that your texture and mom’s texture are the same thing
// so in other words be careful! calling clear on your texture will trash mom’s
// texture and vice versa.
ofTexture(const ofTexture& mom);
ofTexture& operator=(const ofTexture& mom);
// -----------------------------------------------------------------------

void allocate(int w, int h, int internalGlDataType); //uses the currently set OF texture type - default ARB texture
void allocate(int w, int h, int internalGlDataType, bool bUseARBExtention); //lets you overide the default OF texture type
void clear();
void loadData(unsigned char * data, int w, int h, int glDataType);
void loadScreenData(int x, int y, int w, int h);

//the anchor is the point the image is drawn around.
//this can be useful if you want to rotate an image around a particular point.
void setAnchorPercent(float xPct, float yPct); //set the anchor as a percentage of the image width/height ( 0.0-1.0 range )
void setAnchorPoint(int x, int y); //set the anchor point in pixels
void resetAnchor(); //resets the anchor to (0, 0)

void draw(float x, float y, float w, float h);
void draw(float x, float y);

//for the advanced user who wants to draw textures in their own way
void bind();
void unbind();

bool bAllocated();

ofTextureData getTextureData();

float getHeight();
float getWidth();

ofTextureData texData;

protected:
ofPoint anchor;
bool bAnchorIsPct;

float left;//new droozel
float top;//new droozel
float right;//new droozel
float bottom;//new droozel

};

#endif
///////////////////// end OfTexture.h /////////////////////
///////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////
//////////////////////ofTexture.cpp/////////////////////////

#include “ofTexture.h”
#include “ofUtils.h” // for nextPow2()
#include “ofAppRunner.h” // for getWidth()

static bool bTexHackEnabled = true;

//---------------------------------
void ofEnableTextureEdgeHack(){
bTexHackEnabled = true;
}

//---------------------------------
void ofDisableTectureEdgeHack(){
bTexHackEnabled = false;
}

//----------------------------------------------------------
ofTexture::ofTexture(){
texData.bAllocated = false;
texData.textureName[0] = 0;
texData.textureID = 0;
texData.bFlipTexture = false;
texData.textureTarget = GL_TEXTURE_2D;
texData.glTypeInternal = 0;
texData.glType = 0;
texData.width = 0;
texData.height = 0;
texData.tex_w = 0;
texData.tex_h = 0;
texData.tex_t = 0;
texData.tex_u = 0;

resetAnchor();
}

//----------------------------------------------------------
bool ofTexture::bAllocated(){
return texData.bAllocated;
}

//----------------------------------------------------------
ofTexture::ofTexture(const ofTexture& mom){
texData = mom.texData;
}

//----------------------------------------------------------
ofTexture& ofTexture::operator=(const ofTexture& mom){
texData = mom.texData;
return *this;
}

//----------------------------------------------------------
ofTextureData ofTexture::getTextureData(){
if(!texData.bAllocated){
ofLog(OF_LOG_ERROR, “getTextureData() - texture has not been allocated”);
}
return texData;
}

//----------------------------------------------------------
ofTexture::~ofTexture(){
clear();
}

//----------------------------------------------------------
void ofTexture::clear(){
// try to free up the texture memory so we don’t reallocate
// http://www.opengl.org/documentation/spe-…-tures.html
if (texData.textureName[0] != 0){
glDeleteTextures(1, (GLuint *)texData.textureName);
}

texData.bAllocated = false;
}

//----------------------------------------------------------
void ofTexture::allocate(int w, int h, int internalGlDataType){
allocate(w, h, internalGlDataType, ofGetUsingArbTex());
}

//----------------------------------------------------------
void ofTexture::allocate(int w, int h, int internalGlDataType, bool bUseARBExtention){

//our graphics card might not support arb so we have to see if it is supported.
#ifndef TARGET_OF_IPHONE
if (bUseARBExtention && GLEE_ARB_texture_rectangle){
texData.tex_w = w;
texData.tex_h = h;
texData.tex_t = w;
texData.tex_u = h;
texData.textureTarget = GL_TEXTURE_RECTANGLE_ARB;
} else
#endif
{
//otherwise we need to calculate the next power of 2 for the requested dimensions
//ie (320x240) becomes (512x256)
texData.tex_w = ofNextPow2(w);
texData.tex_h = ofNextPow2(h);
texData.tex_t = 1.0f;
texData.tex_u = 1.0f;
texData.textureTarget = GL_TEXTURE_2D;
}

texData.glTypeInternal = internalGlDataType;

// attempt to free the previous bound texture, if we can:
clear();

glGenTextures(1, (GLuint *)texData.textureName); // could be more then one, but for now, just one

glEnable(texData.textureTarget);

glBindTexture(texData.textureTarget, (GLuint)texData.textureName[0]);
#ifndef TARGET_OF_IPHONE
// can’t do this on OpenGL ES: on full-blown OpenGL,
// internalGlDataType and glDataType (GL_LUMINANCE below)
// can be different; on ES they must be exactly the same.
glTexImage2D(texData.textureTarget, 0, texData.glTypeInternal, (GLint)texData.tex_w, (GLint)texData.tex_h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0); // init to black…
#else
glTexImage2D(texData.textureTarget, 0, texData.glTypeInternal, texData.tex_w, texData.tex_h, 0, texData.glTypeInternal, GL_UNSIGNED_BYTE, 0);
#endif

glTexParameterf(texData.textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(texData.textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameterf(texData.textureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(texData.textureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

glDisable(texData.textureTarget);

texData.width = w;
texData.height = h;
#ifdef TARGET_OF_IPHONE
texData.bFlipTexture = true; // textures need to be flipped for the iphone
#else
texData.bFlipTexture = false;
#endif
texData.bAllocated = true;
}

//----------------------------------------------------------
void ofTexture::loadData(unsigned char * data, int w, int h, int glDataType){

// can we allow for uploads bigger then texture and
// just take as much as the texture can?
//
// ie:
// int uploadW = MIN(w, tex_w);
// int uploadH = MIN(h, tex_h);
// but with a “step” size of w?
// check “glTexSubImage2D”

if ( w > texData.tex_w || h > texData.tex_h) {
ofLog(OF_LOG_ERROR,“image data too big for allocated texture. not uploading…”);
return;
}

//update our size with the new dimensions - this should be the same size or smaller than the allocated texture size
texData.width = w;
texData.height = h;
texData.glType = glDataType;

//compute new tex co-ords based on the ratio of data’s w, h to texture w,h;
#ifndef TARGET_OF_IPHONE
if (texData.textureTarget == GL_TEXTURE_RECTANGLE_ARB){
texData.tex_t = w;
texData.tex_u = h;
} else
#endif
{
texData.tex_t = (float)(w) / (float)texData.tex_w;
texData.tex_u = (float)(h) / (float)texData.tex_h;
}

// ok this is an ultra annoying bug :
// opengl texels and linear filtering -
// when we have a sub-image, and we scale it
// we can clamp the border pixels to the border,
// but the borders of the sub image get mixed with
// neighboring pixels…
// grr…
//
// the best solution would be to pad out the image
// being uploaded with 2 pixels on all sides, and
// recompute tex_t coordinates…
// another option is a gl_arb non pow 2 textures…
// the current hack is to alter the tex_t, tex_u calcs, but
// that makes the image slightly off…
// this is currently being done in draw…
//
// we need a good solution for this…
//
// http://www.opengl.org/discussion-boards-…-770#000001
// http://www.opengl.org/discussion-boards-…-770#000001

//------------------------ likely, we are uploading continuous data
GLint prevAlignment;
glGetIntegerv(GL_UNPACK_ALIGNMENT, &prevAlignment);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

// update the texture image:
glEnable(texData.textureTarget);
glBindTexture(texData.textureTarget, (GLuint)texData.textureName[0]);
glTexSubImage2D(texData.textureTarget,0,0,0,w,h,texData.glType,GL_UNSIGNED_BYTE,data);
glDisable(texData.textureTarget);

//------------------------ back to normal.
glPixelStorei(GL_UNPACK_ALIGNMENT, prevAlignment);

#ifdef TARGET_OF_IPHONE
texData.bFlipTexture = true; // textures need to be flipped for the iphone
#else
texData.bFlipTexture = false;
#endif

}

//----------------------------------------------------------
void ofTexture::loadScreenData(int x, int y, int w, int h){

int screenHeight = ofGetHeight();
y = screenHeight - y;
y -= h; // top, bottom issues
texData.bFlipTexture = true;

if ( w > texData.tex_w || h > texData.tex_h) {
ofLog(OF_LOG_ERROR,“image data too big for allocated texture. not uploading…”);
return;
}

//update our size with the new dimensions - this should be the same size or smaller than the allocated texture size
texData.width = w;
texData.height = h;
texData.glType = GL_RGB;

//compute new tex co-ords based on the ratio of data’s w, h to texture w,h;
#ifndef TARGET_OF_IPHONE // DAMIAN
if (texData.textureTarget == GL_TEXTURE_RECTANGLE_ARB){
texData.tex_t = (float)(w);
texData.tex_u = (float)(h);
} else
#endif
{
texData.tex_t = (float)(w) / (float)texData.tex_w;
texData.tex_u = (float)(h) / (float)texData.tex_h;
}

glEnable(texData.textureTarget);
glBindTexture(texData.textureTarget, (GLuint)texData.textureName[0]);
glCopyTexSubImage2D(texData.textureTarget, 0,0,0,x,y,w,h);
glDisable(texData.textureTarget);
}

//we could cap these values - but it might be more useful
//to be able to set anchor points outside the image

//----------------------------------------------------------
void ofTexture::setAnchorPercent(float xPct, float yPct){
anchor.x = xPct;
anchor.y = yPct;

bAnchorIsPct = true;
}

//----------------------------------------------------------
void ofTexture::setAnchorPoint(int x, int y){
anchor.x = x;
anchor.y = y;

bAnchorIsPct = false;
}

//----------------------------------------------------------
void ofTexture::resetAnchor(){
anchor = 0;
bAnchorIsPct = false;
}

//----------------------------------------------------------
void ofTexture::bind(){
//we could check if it has been allocated - but we don’t do that in draw()
glEnable(texData.textureTarget);
glBindTexture( texData.textureTarget, (GLuint)texData.textureName[0]);
}

//----------------------------------------------------------
void ofTexture::unbind(){
glDisable(texData.textureTarget);
}

//----------------------------------------------------------
void ofTexture::draw(float x, float y, float w, float h){

glEnable(texData.textureTarget);

// bind the texture
glBindTexture( texData.textureTarget, (GLuint)texData.textureName[0] );

GLfloat px0 = 0; // up to you to get the aspect ratio right
GLfloat py0 = 0;
GLfloat px1 = w;
GLfloat py1 = h;

if (texData.bFlipTexture == true){
GLint temp = (GLint)py0;
py0 = py1;
py1 = temp;
}

// for rect mode center, let’s do this:
if (ofGetRectMode() == OF_RECTMODE_CENTER){
px0 = -w/2;
py0 = -h/2;
px1 = +w/2;
py1 = +h/2;
}

//we translate our drawing points by our anchor point.
//we still respect ofRectMode so if you have rect mode set to
//OF_RECTMODE_CENTER your anchor will be relative to that.
GLfloat anchorX;
GLfloat anchorY;

if(bAnchorIsPct){
anchorX = anchor.x * w;
anchorY = anchor.y * h;
}else{
anchorX = anchor.x;
anchorY = anchor.y;
}

px0 -= anchorX;
py0 -= anchorY;
px1 -= anchorX;
py1 -= anchorY;

// -------------------------------------------------
// complete hack to remove border artifacts.
// slightly, slightly alters an image, scaling…
// to remove the border.
// we need a better solution for this, but
// to constantly add a 2 pixel border on all uploaded images
// is insane…

GLfloat offsetw = 0;
GLfloat offseth = 0;

if (texData.textureTarget == GL_TEXTURE_2D && bTexHackEnabled) {
offsetw = 1.0f / (texData.tex_w);
offseth = 1.0f / (texData.tex_h);
}
// -------------------------------------------------
//replaced by Droozle’s vars below///////////////////
//GLfloat tx0 = 0+offsetw;
//GLfloat ty0 = 0+offseth;
//GLfloat tx1 = texData.tex_t - offsetw;
//GLfloat ty1 = texData.tex_u - offseth;

//new////////Droozle///////////////////////////////
GLfloat tx0 = max(offsetw, left*texData.tex_t);
GLfloat ty0 = max(offseth, top*texData.tex_u);
GLfloat tx1 = min(right*texData.tex_t, texData.tex_t-offsetw);
GLfloat ty1 = min(bottom*texData.tex_u, texData.tex_u-offseth);
////////////////////////////////////////////////////////////////

glPushMatrix();

glTranslatef(x,y,0.0f);

GLfloat tex_coords[] = {
tx0,ty0,
tx1,ty0,
tx1,ty1,
tx0,ty1
};
GLfloat verts[] = {
px0,py0,
px1,py0,
px1,py1,
px0,py1
};

glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glTexCoordPointer(2, GL_FLOAT, 0, tex_coords );
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, verts );
glDrawArrays( GL_TRIANGLE_FAN, 0, 4 );
glDisableClientState( GL_TEXTURE_COORD_ARRAY );

glPopMatrix();
glDisable(texData.textureTarget);
}

//----------------------------------------------------------
void ofTexture::draw(float x, float y){
draw(x,y,texData.width, texData.height);
}

//----------------------------------------------------------
float ofTexture::getHeight(){
return texData.height;
}

//----------------------------------------------------------
float ofTexture::getWidth(){
return texData.width;
}
//new/////Droozle/////////////////////////
void ofTexture::setSubsection(float x, float y, float w, float h){
if(texData.tex_t == 0) return;
if(texData.tex_u == 0) return;

left = max(0.0f, min(1.0f, x / texData.width));
top = max(0.0f, min(1.0f, y / texData.height));
right = max(0.0f, min(1.0f, left + (w / texData.width)));
bottom = max(0.0f, min(1.0f, top + (h / texData.height)));

// disable texture hack for now
bTexHackEnabled = false;
}

void ofTexture::resetSubsection(){
left = 0.0f;
top = 0.0f;
right = 1.0f;
bottom = 1.0f;

// TODO: only enable texture hack if it was previously enabled
bTexHackEnabled = true;
}

/////////////////end ofTexture.cpp/////////////////////////
///////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////
///////////////////// testApp.cpp//////////////////////////

#include “testApp.h”

//--------------------------------------------------------------
void testApp::setup(){

ofBackground(150,150,150);
video.loadMovie(“vid0.mov”);//440px x 330px QTvideo
video.getTextureReference().setSubsection(0, 0, 160, 120);
}

//--------------------------------------------------------------
void testApp::update(){

}

//--------------------------------------------------------------
void testApp::draw(){

video.draw(100, 100, 160, 120);
video.getTextureReference().resetSubsection();
}

//////////////////end testApp.cpp//////////////////////////
///////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////
////////////////////// testApp.h///////////////////////////

#ifndef _TEST_APP
#define _TEST_APP

#include “ofMain.h”
#include “ofxOpenCv.h”

class testApp : public ofBaseApp{

public:

void setup();
void update();
void draw();

void keyPressed (int key);
void mouseMoved(int x, int y );
void mouseDragged(int x, int y, int button);
void mousePressed(int x, int y, int button);
void mouseReleased(int x, int y, int button);
void windowResized(int w, int h);

ofVideoPlayer video;

int vidWidth;
int vidHeight;

};

#endif

////////////////////// testApp.h///////////////////////////
///////////////////////////////////////////////////////////

Most of it seems OK to me, except the following:

Either do this:

  
  
//--------------------------------------------------------------  
void testApp::setup(){  
  ofBackground(150,150,150);  
  video.loadMovie("vid0.mov"); //440px x 330px QTvideo  
  
  // video is only drawn once and we only need the upper left  
  // quarter of it, so we only need to set the subsection once  
  video.getTextureReference().setSubsection(0, 0, 220, 165);  
}  
  
//--------------------------------------------------------------  
void testApp::update(){  
  video.idleMovie();  
}  
  
//--------------------------------------------------------------  
void testApp::draw(){  
  // example: draw at (100,100) at size 440x330  
  // (video will be same size as original, but only shows  
  //  the upper left quarter as if you magnified it)  
  video.draw(100, 100, 440, 330);  
}  
  

OR this:

  
  
//--------------------------------------------------------------  
void testApp::setup(){  
  ofBackground(150,150,150);  
  video.loadMovie("vid0.mov"); //440px x 330px QTvideo  
}  
  
//--------------------------------------------------------------  
void testApp::update(){  
  video.idleMovie();  
}  
  
//--------------------------------------------------------------  
void testApp::draw(){  
  // video is drawn multiple times, each using its own  
  // subsection, so we set it every time we draw it:  
  video.getTextureReference().setSubsection(0, 0, 220, 165);  
  video.draw(100, 100, 220, 165);  
  
  // as an example: specify a different subsection (lower right quarter)  
  video.getTextureReference().setSubsection(220, 165, 220, 165);  
  video.draw(400, 100, 220, 165);  
  
  // as an example: reset the subsection to draw the full video  
  video.getTextureReference().resetSubsection();  
  // ... but draw it at half the size  
  video.draw(100, 300, 220, 165);  
}  
  

Did you get any real errors? If so, can you post them? If you did not get any errors but simply saw the full video, the above examples should clarify things.

NOTE: the first example might not work if a call to idleMovie is needed in order to initialize the video’s texture reference. So if your program crashes, try the second example. I made a note in my previous post about a workaround for this: add a ‘setSubsection’ function to your ofVideoPlayer class that looks like this:

  
  
//----------------------------------------------------------  
void ofVideoPlayer::setSubsection(float x, float y, float w, float h){  
    if(bUseTexture){  
        tex.setSubsection(x, y, w, h);  
    }  
}  
  
//----------------------------------------------------------  
void ofVideoPlayer::resetSubsection(){  
    if(bUseTexture){  
        tex.resetSubsection();  
    }  
}  
  

And use ‘video.setSubsection(0, 0, 220, 165);’ instead of ‘video.getTextureReference().setSubsection(0, 0, 220, 165);’.

Paul

Hey Thanks

That’s working fine now…
I have to say the framerate is really impressive!! at least 3x faster!!
I pulled a test with eight video files running simultaneously and got a frame rate of 45fps

The errors I experienced previously mainly turned out to be human ( void lack_ofSleep() was being called on an infinite loop )

In fact I forgot to include the function declarations for setSubsection() and resetSubsection() in the ‘ofTexture.h’ class.
Following that, as you predicted, the ‘video.getTextureReference().setSubsection(0, 0, 220, 165);’ caused a crash for me but the ‘video.setSubsection(0, 0, 220, 165);’ has sorted this out.

Thanks a lot, really, that’s a great help!
Much appreciated!

Good to hear it worked for you :slight_smile: Now I feel like I finally contributed something useful to the OF community, hehe.

Consider not running multiple video streams at the same time, but instead create 1 big video containing all of them in a single stream, then using subsectioning to draw them on different parts of your screen. Provided the video resolution and bitrate can be handled by your system, it will be faster than playing separate streams. Your hard disk will be grateful. It does mean you only have a single “timeline” though.

I am working on an exhibit at the moment that uses this “technique” extensively. When finished, I will most definitely post something about it in the examples section of the forums.

Maybe this is a good time to ask the OF team to incorporate this functionality in the next release? Using subsections of textures not only helps when faced with problems like the one in this thread, but also enables you to use “sprite textures” containing more than one frame of a small animation (old tech, but still useful).

And maybe it would also be a good idea to balance the functionality of the classes. Why is there a “setAnchorPoint()” function, but no “getAnchorPoint()” for example?

Cheers,

Paul

Hey
Be sure to post a link re your exhibit; it would be good to see how all this functionality finds its way into practical applications.

Had a look at your video masking technique also…very good. For some reason I could not get a live webcam image playing back to the screen with the videograbber example?! but for some other reason it works fine in your video masking… :slight_smile:
No need to explain I think my head is loaded for at least a week now. I might set up a separate thread on that one.

Thanks Again
Paul G

Hi,
I’m aware this thread’s been quiet for a while, but as there are quite a few references to the subsection function I suppose there’s a few interested.

I’ve been trying to use this in OF 007, but getting a lot of errors as the structure of Graphics in 007 doesn’t seem to allow the above mentioned fix to oftexture - I’m not sure but it seems to be made for 006.

If anyone has gotten this to work in OF 007, and can you give some pointers?

Or is there in the meantime a better way to take a subsection of a video/cam?

Thanks!

you can do something like:

  
  
ofMesh roi;  
roi.addVertex(x,y);  
roi.addTexCoord(x,y);  
roi.addVertex(x+roiW,y);  
roi.addTexCoord(x+roiW,y);  
roi.addVertex(x+roiW,y+roiH);  
roi.addTexCoord(x+roiW,y+roiH);  
roi.addVertex(x,y+roiH);  
roi.addTexCoord(x,y+roiH);  
roi.setMode(OF_PRIMITIVE_TRIANGLE_FAN);  
  
  
video.getTextureReference().bind();  
roi.draw();  
video.getTextureReference().unbind();  
  

where x,y is where you want to draw the video and roiW, roiH the width and height of the subsection