Extracting thumbnail and metaData from a video[0]/ large img

Questions and postings pertaining to the usage of ImageMagick regardless of the interface. This includes the command-line utilities, as well as the C and C++ APIs. Usage questions are like "How do I use ImageMagick to create drop shadows?".
Post Reply
kunjbhai

Extracting thumbnail and metaData from a video[0]/ large img

Post by kunjbhai »

Hi,
I am trying to extract thumbnail and selective profile info from images using Version: ImageMagick 6.5.1-3 2009-04-13 Q8 (built from source) on windows.
Now the problem comes with the large images(100mB) or video files(3mB mpeg with 1000 frames) as even after supplying a large memory area to use so that imageMagick does not have to do any disk-swapping it takes a really long time( in minutes) to create the in-memory pixel cache.

I don't really require the cache as i simply want a thumbnail and the metadata of the image. But the moment I call readImage or blobToImage methods the pixel cache generation starts automatically.

Is there some way by which I can avoid this pixel cache and get the thumbnail and the metadata.

The only way I could find was using stream but then I was unable to extract thumbnail or metadata using that.

On the command line if I say identify -ping then that also extracts limited information pretty fast irrespective of the size of the multimedia file. I tried looking into identify code to see if I could get some lead but that also did not help.

Another thing for files like psd,gif,mpeg etc. which contain multiple layers or scenes I simply want to get thumbnail and metadata for the first scene or layer.
To achieve that I am using the following sequence so as to avoid loading all the frames or layers in the memory:
Magick::Image image;
image.subImage(3);
image.subRange(1);
image.read("file.gif");

The above code is from one of the posts I found on the forum and too be honest I don't really understand the difference between subImage and subRange. Is this really the best way to do it or is there some even more efficient method?

I did not use the STL method as there the only method which could help was readImages but then that would first read in all the images and then only i will be able to access the one required by me using the list<image>
User avatar
magick
Site Admin
Posts: 11064
Joined: 2003-05-31T11:32:55-07:00

Re: Extracting thumbnail and metaData from a video[0]/ large img

Post by magick »

Another way to extract just one image from a multi-frame set is:
  • image.read("file.gif[0]");
In general this should be efficient. However in the case of some video formats, ImageMagick uses an external delegate and decodes the entire sequence to disk and then only reads the number of frames you specify (e.g. [0] reads one frame). We have a patch to make this process more efficient but do not have an ETA when it will be implemented.

Large JPEG images are be converted to thumbnails efficiently with the -size hint (e.g. convert -size 320x320 bigassimage.jpg thumb.jpg).

Otherwise ImageMagick may not be the right solution for extracting thumbnails from large images if performance is essential.
kunjbhai

Re: Extracting thumbnail and metaData from a video[0]/ large img

Post by kunjbhai »

but image.read("file.psd[0]"), still reads in the complete psd and not just the first layer.

Also can you suggest me what should I use for generating thumbnails with minimal time overhead.
User avatar
magick
Site Admin
Posts: 11064
Joined: 2003-05-31T11:32:55-07:00

Re: Extracting thumbnail and metaData from a video[0]/ large img

Post by magick »

but image.read("file.psd[0]"), still reads in the complete psd and not just the first layer.
Works for us. We're using ImageMagick-6.5.1-6.
Also can you suggest me what should I use for generating thumbnails with minimal time overhead.
We have no recommendations.
kunjbhai

Re: Extracting thumbnail and metaData from a video[0]/ large img

Post by kunjbhai »

For me the psd is getting loaded completely and not just the first layer.
This is how I analyzed it using files:http://cid-7572e6fdaea4368b.skydrive.li ... Flower.psd and http://cid-7572e6fdaea4368b.skydrive.li ... ic/vid.mpg.
I ran the following shell script:
date
convert Flower.psd test.jpeg

date
convert Flower.psd[0] test.jpeg

date
convert vid.mpg test.jpeg

date
convert vid.mpg[0] test.jpeg
date

The times were as follows:
Fri Apr 24 19:58:41 IST 2009
Fri Apr 24 19:58:51 IST 2009 //time to generate 5 thumbnails for each layer from psd is 10sec Mem usage: 14448 KB
Fri Apr 24 19:59:02 IST 2009 //time to generate 1 thumbnail for just the first layer from psd is ~10sec Mem usage: 14448 KB
Fri Apr 24 20:00:55 IST 2009 //time to generate 1016 thumbnails for each frame from mpeg is 53sec Mem usage: 336,396 KB
Fri Apr 24 20:01:03 IST 2009 //time to generate 1 thumbnail for just the first frame from mpeg is 8sec Mem usage: 7,364 KB

As you can see the time for generating all thumbnails and just a single one from psd are same and so is the memory usage whereas for a mpeg it decreases drastically.

Also after looking into the code of psd.c (delegate used for psd files), I could not see any place where it takes into consideration the layer asked as it simply iterates all the layers and loads first there profiles and then there pixel data:
//These are some code snippets to show this to you. Plz note I have removed the exception checks to make it concise
psd.c
Method: static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception)

Code: Select all

 /*
    If we are only "pinging" the image, then we're done - so return.
  */
  if (image_info->ping != MagickFalse)
    {
      (void) CloseBlob(image);
      return(GetFirstImageInList(image));
    }
  /*
    Layer and mask block.
  */

Code: Select all

          MagickOffsetType
            layer_offset;

          layer_offset=offset+length;
          number_layers=(short) ReadBlobMSBShort(image);
 
          layer_info=(LayerInfo *) AcquireQuantumMemory((size_t) number_layers, sizeof(*layer_info));

          (void) ResetMagickMemory(layer_info,0,(size_t) number_layers* sizeof(*layer_info));
          for (i=0; i < number_layers; i++)
          {
            if (image->debug != MagickFalse)
              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                "  reading layer #%ld",i+1);
            layer_info[i].page.y=(long) ReadBlobMSBLong(image);
            layer_info[i].page.x=(long) ReadBlobMSBLong(image);
            layer_info[i].page.height=ReadBlobMSBLong(image)-layer_info[i].page.y;
            layer_info[i].page.width=ReadBlobMSBLong(image)-layer_info[i].page.x;

Code: Select all

       /*
          Read pixel data for each layer.
        */
        for (i=0; i < number_layers; i++)
        {
            for (j=0; j < (long) layer_info[i].channels; j++)
            {
              if (image->debug != MagickFalse)
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),"    reading data for channel %ld", j);
#if     1
              if (layer_info[i].channel_info[j].size <= (2*layer_info[i].image->rows))
                {
                  long
                    k;

Code: Select all

           /*
              Allocate layered image.
            */
            layer_info[i].image=CloneImage(image,layer_info[i].page.width,   layer_info[i].page.height,MagickFalse,&image->exception);
            (void) SetImageBackgroundColor(layer_info[i].image);
            layer_info[i].image->compose= PSDBlendModeToCompositeOperator(layer_info[i].blendkey);
            if (layer_info[i].visible == MagickFalse)
              layer_info[i].image->compose=NoCompositeOp;
            if (psd_info.mode == CMYKMode)
              (void) SetImageColorspace(layer_info[i].image,CMYKColorspace);
            for (j=0; j < (long) layer_info[i].channels; j++)
              if (layer_info[i].channel_info[j].type == -1)
                layer_info[i].image->matte=MagickTrue;
image.read("file.psd[0]")
I am making my image from a file starting from an offset till some number of bytes then I am reading that completely into memory and passing the blob to create the image. How should I use the method sugested in your last post to extract first layer/scene/frame from this blob.

Is there a way by which I could tell the offset in a file and the number of bytes to read for creating image so that ImageMagick can decide how much bytes to read into memory and how much to put on disk swap files etc. (Note: I cannot use stream as I am unable to create thumbnail with it)
User avatar
magick
Site Admin
Posts: 11064
Joined: 2003-05-31T11:32:55-07:00

Re: Extracting thumbnail and metaData from a video[0]/ large img

Post by magick »

Grab ImageMagick 6.5.1-7 Beta sometime tomorrow. It supports the subimage specification syntax (e.g. image.psd[0], image.mpg[1-3]).

You can use the stream program to produce thumbnails by using a pipe to subsample the streamed pixels. We will most likely add an option to the stream program in the future to do this automatically. In the mean-time to determine if subsampling is acceptable, resize some of your images with the -sample option and then -resize. If the -sample option produces acceptable results then our proposal is the right solution.
kunjbhai

Re: Extracting thumbnail and metaData from a video[0]/ large img

Post by kunjbhai »

We will investigate ensuring that both PSD and MPEG return only one frame efficiently with image.psd[0] and vid.mpg[0] within the next few days.
Well I only found problem with the psd case as there the time and memory cost were same irrespective of the fact whether a layer or whole psd was asked to be worked upon. In mpeg I was able to see the cost change with the number of frames asked to transform(as should be expected)
You can use the stream program to produce thumbnails by using a pipe to subsample the streamed pixels. We will most likely add an option to the stream program in the future to do this automatically. In the mean-time to determine if subsampling is acceptable, resize some of your images with the -sample option and then -resize. If the -sample option produces acceptable results then our proposal is the right solution.
Please correct me if I have understood you wrong. Basically you are advising to use pipe with stream for:
  • Fetching the thumbnail for just the first layer/frame from a file contaning some n bytes of multimedia data starting from an offset (other than zero) without streaming in the complete n bytes.
    Fetching the thumbnail for just the first layer/frame from a complete multimedia file without streaming in the complete pixel data for the whole file.


I haven't yet seen any examples of using pipes or subsampling through the API, so I will search for these and hopefully that would solve my problem.

Thanks for your quick and informative replies.
kunjbhai

Re: Extracting thumbnail and metaData from a video[0]/ large img

Post by kunjbhai »

I could not get the stream thing going. Can you please give an example for that.
Also I tried following plz have a look into it:
(I also posted following on another thread viewtopic.php?f=1&t=13439 where you had suggested the subImage() method but since no reply came I thought may be that is no more active so I am posting here again. Do tell me if you want me to delete that.

Magick::Image image;
image.subImage(3);
image.subRange(1);

I was trying to use the above methods to get the first scene from a mpg (http://cid-7572e6fdaea4368b.skydrive.li ... ic/vid.mpg) while using blob but it does not work like ("file.mpg[0]")
I used the following code:

Code:
int main(int argc,char * argv)
{
InitializeMagick(argv);
std::string path("C:\\Tests\\vid.mpg");

/** Passing [0] in the file name itself **/
Image *image1=new Image();
image1->read(path+"[0]");
image1->write("C:\\Tests\\SceneInName.jpeg");
delete(image1);
cout<<"Copying from image done.";

/** Asking to retrieve only first frame by using subRange **/
ifstream fl(path.c_str(), ios::in | IOS_IN_BINARY );
fl.seekg(0, ios::end );
size_t blobLen = fl.tellg();
unsigned char* blobData = new unsigned char[blobLen];
char* c=reinterpret_cast<char *>(blobData);
fl.seekg(0,ios::beg);
fl.read(c, blobLen);
fl.close();

// Construct Magick++ Blob
Blob blob(static_cast<const unsigned char*>(blobData), blobLen);
delete [] blobData;

image1=new Magick::Image();
image1->subImage(3);
image1->subRange(1);

image1->magick("MPG");

image1->read( blob);
image1->write("C:\\Tests\\SceneInSubRange.jpeg");
delete(image1);
cout<<"Copying from Bytes done.";
}


Though I only get the first scene by both methods but that happens even if I don't use [0] OR subImage() and subRange() methods at all.

But in first case (path+"[0]") the time taken is way less than during subImage(),subRange() methods which suggests that they are not doing the same thing.

How can we get the same effect as (path+"[0]") for the case where we want to read image from inside a file, from an offset other than zero, (i.e. in case of blobs)?
Post Reply