Sign in to follow this  

API for making movies from a series of images

This topic is 4711 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I am looking for an API, or some code i can use to make a movie, can be quick time, AVI, MPEG, from a series of images that are all the same size. It would be great if it could make it from tiff images, but if not the images can be converted before the movie is made. Any ideas. Thanks

Share this post


Link to post
Share on other sites
Adobe Premiere, VirtualDub (www.virtualdub.org) are the known ones, but Roxio, ArcSoft, MAGIX, InterVideo, Ulead Systems, and CyberLink Corp are a few other companies that market video / image editing tools.

-cb

Share this post


Link to post
Share on other sites
Here is some simple code I threw together real quick one day when I needed to stitch some images into a movie. It does not save a sound track but that should be fairly trivial to add if you read up on win32 multimedia compression api.

The simple api header (yep the pragma ain't standard, but I wrote it for myself and its quicker to use it than write the typical guard):

// video_compress.h

#pragma once

struct video_frame_properties
{
DWORD width;
DWORD height;
DWORD use_first_image_size;
DWORD sizing_type;
COLORREF bkg_color;
BOOL preserve_aspect_ratio;
DWORD frame_rate;
};

BOOL Compress(video_frame_properties* prop, const TCHAR *name, TCHAR **images, int num_images);




Here is the meat of the code:

// video_compress.cpp
#include "stdafx.h"
#include <Vfw.h>
#include "video_compress.h"
#include <gdiplus.h>
using namespace Gdiplus;

Bitmap* OpenBitmap(const TCHAR* name)
{
#ifdef UNICODE
Bitmap *bm=Bitmap::FromFile(name,FALSE);
#else
WCHAR wimg[MAX_PATH];
MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,name,-1,wimg,MAX_PATH);
Bitmap *bm=Bitmap::FromFile(wimg,FALSE);
#endif
return bm;
}

void LoadFrame(BYTE* bits, video_frame_properties* prop, const TCHAR* name)
{
Bitmap* bm=OpenBitmap(name);
if(!bm)
return;

Bitmap* bm_final=new Bitmap((INT)(prop->width),(INT)(prop->height),PixelFormat24bppRGB);

Graphics* g=Graphics::FromImage(bm_final);
Color c(GetRValue(prop->bkg_color),GetGValue(prop->bkg_color),GetBValue(prop->bkg_color));
g->Clear(c);

INT src_width;
INT src_height;
INT src_x=0;
INT src_y=0;

INT dst_width;
INT dst_height;
INT dst_x=0;
INT dst_y=0;

INT width=(INT)(prop->width);
INT height=(INT)(prop->height);

if(prop->sizing_type)
{
if((INT)(bm->GetWidth())>=width)
{
dst_width=src_width=width;
}
else
{
dst_width=src_width=bm->GetWidth();
dst_x=(width-src_width)/2;

}
if((INT)(bm->GetHeight())>=height)
{
dst_height=src_height=height;
}
else
{
dst_height=src_height=bm->GetHeight();
dst_y=(height-src_height)/2;
}
}
else
{
src_width=bm->GetWidth();
src_height=bm->GetHeight();

if(prop->preserve_aspect_ratio)
{
float aspect_canvas=((float)(width))/((float)(height));
float aspect_image=((float)(bm->GetWidth()))/((float)(bm->GetHeight()));
if(aspect_image<aspect_canvas)
{
dst_height=height;
dst_width=(INT)(aspect_image*(float)(dst_height));
dst_x=width/2-dst_width/2;
}
else
{
dst_width=width;
dst_height=(INT)((float)(dst_width)/aspect_image);
dst_y=height/2-dst_height/2;
}
if(dst_width>width)
dst_width=width;
if(dst_height>height)
dst_height=height;

}
else
{
dst_width=width;
dst_height=height;
}
}

Rect dst_rect(dst_x,dst_y,dst_width,dst_height);
g->DrawImage(bm,dst_rect,src_x,src_y,src_width,src_height,UnitPixel,NULL,NULL,NULL );

delete g;
delete bm;

Rect r(0,0,width,height);
BitmapData bd;
if(bm_final->LockBits(&r,ImageLockModeRead, PixelFormat24bppRGB, &bd)==Ok)
{
BYTE *src=(BYTE*)(bd.Scan0);
BYTE *dst=bits;
int num_bytes=3*width;
int dst_stride;
int src_stride;
if(bd.Stride>0)
{
dst=bits+num_bytes*(height-1);
dst_stride=-num_bytes;
src_stride=bd.Stride;
}
else
{
dst_stride=num_bytes;
src_stride=-bd.Stride;
}
for(UINT i=0;i<prop->height;i++)
{
memcpy(dst,src,num_bytes);
dst+=dst_stride;
src+=src_stride;
}
bm_final->UnlockBits(&bd);
}

delete bm_final;
}

BOOL Compress(video_frame_properties* prop, const TCHAR *name, TCHAR **images, int num_images)
{
PAVIFILE pf=NULL;
PAVISTREAM ps=NULL;
BITMAPINFOHEADER bi;
AVISTREAMINFO asi;

if(prop->use_first_image_size)
{
Bitmap* bm=OpenBitmap(images[0]);
prop->width=bm->GetWidth();
prop->height=bm->GetHeight();

delete bm;
}

bi.biSize=sizeof(BITMAPINFOHEADER);
bi.biWidth=(LONG)(prop->width);
bi.biHeight=(LONG)(prop->height);
bi.biPlanes=1;
bi.biBitCount=24;
bi.biCompression=BI_RGB;
bi.biSizeImage=0;
bi.biXPelsPerMeter=bi.biYPelsPerMeter=200;
bi.biClrImportant=bi.biClrUsed=0;


COMPVARS cv;
memset(&cv,0,sizeof(cv));
cv.cbSize=sizeof(cv);


if(!ICCompressorChoose(NULL,ICMF_CHOOSE_DATARATE | ICMF_CHOOSE_KEYFRAME, &bi, NULL, &cv, _T("Choose a compression method")))
{
DWORD err=GetLastError();
return FALSE;
}

// Create new AVI file using AVIFileOpen.
HRESULT hr = AVIFileOpen(&pf, name, OF_WRITE | OF_CREATE, NULL);
if (hr != 0)
{
ICCompressorFree(&cv);
return FALSE;
}

memset(&asi,0,sizeof(asi));
asi.fccType=streamtypeVIDEO;
asi.fccHandler=cv.fccHandler;
asi.dwScale=1;
asi.dwRate=prop->frame_rate;
asi.dwLength=0;
asi.dwQuality=cv.lQ;
asi.rcFrame.top=asi.rcFrame.left=0;
asi.rcFrame.bottom=prop->height;
asi.rcFrame.right=prop->width;
strcpy(asi.szName,"A test");

hr = AVIFileCreateStream(pf, &ps, &asi);
if (hr != 0)
{ //Stream created OK? If not, close file.
ICCompressorFree(&cv);
AVIFileRelease(pf);
return FALSE;
}

// Set format of new stream using AVIStreamSetFormat.
if(cv.lpbiOut)
hr = AVIStreamSetFormat(ps, 0, cv.lpbiOut, sizeof(bi));
else
hr = AVIStreamSetFormat(ps, 0, &bi, sizeof(bi));
if (hr != 0)
{
ICCompressorFree(&cv);
AVIStreamRelease(ps);
AVIFileRelease(pf);
return FALSE;
}

DWORD num_pixels=prop->height*prop->width;
BYTE *bits=new BYTE[3*num_pixels];
for(DWORD j=0;j<num_pixels;j++)
{
DWORD p=(j<<1) + j;
bits[p++]=255;
bits[p++]=0;
bits[p]=0;
}

BITMAPINFO bih;
memcpy(&bih,&bi,sizeof(bi));

if(cv.fccHandler==mmioFOURCC('D','I','B',' '))
{
LONG samples;
LONG bytes;
for(int i=0;i<num_images;i++)
{
LoadFrame(bits, prop, images[i]);
AVIStreamWrite(ps,i,1,bits,3*num_pixels,AVIIF_KEYFRAME,&samples,&bytes);
}
}
else
{
if(ICSeqCompressFrameStart(&cv, &bih))
{
BOOL iskey;
LONG size;
LONG samples;
LONG bytes;
for(int i=0;i<num_images;i++)
{
LoadFrame(bits, prop, images[i]);
LPVOID dataout=ICSeqCompressFrame(&cv,0,bits,&iskey,&size);
if(dataout)
{
DWORD flags=(iskey ? AVIIF_KEYFRAME : 0);
AVIStreamWrite(ps,i,1,dataout,size,flags,&samples,&bytes);
}
}
ICSeqCompressFrameEnd(&cv);
}
else
{
DWORD err=GetLastError();
}
}
ICCompressorFree(&cv);
AVIStreamRelease(ps);
AVIFileRelease(pf);
delete[] bits;
return TRUE;
}




It works most of the time. It does not seem to work with some codecs. I have the best luck with Indeo, Cinepack, and Microsoft Video.

Share this post


Link to post
Share on other sites
ok, i figured out the libs, and it is working now, but it doesn't seem to output any movie. Here is the code for the test i set up, it brings up the compression chooser, and i pick, and it seems to work, but there is no output.

#include "video_compress.h"

int main()
{
TCHAR **files;
files = new TCHAR*[3];

files[0] = "C:\\Documents and Settings\\x\\Desktop\\movie\\1.bmp";
files[1] = "C:\\Documents and Settings\\x\\Desktop\\movie\\2.bmp";
files[2] = "C:\\Documents and Settings\\x\\Desktop\\movie\\3.bmp";

video_frame_properties vfp;
vfp.width = 100;
vfp.height = 100;
vfp.use_first_image_size = false;
vfp.bkg_color = 0;
vfp.preserve_aspect_ratio = false;
vfp.frame_rate = 30;

Compress(&vfp, "C:\\Documents and Settings\\x\\Desktop\\movie\\test.avi", files, 1);


return 0;
}

Share this post


Link to post
Share on other sites
You'll notice that the function I wrote does very minimal error reporting. What is probably happening is the function is returning FALSE (meaning there was an error) and simply not telling you why it failed. You could either step through the function and see where it is failing and why, or at a minimum, add more robust error reporting to the function.

Share this post


Link to post
Share on other sites

This topic is 4711 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this