예제 #1
0
파일: yuv.c 프로젝트: vgck/opendr2
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   R e a d Y U V I m a g e                                                   %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method ReadYUVImage reads an image with digital YUV (CCIR 601 4:1:1) bytes
%  and returns it.  It allocates the memory necessary for the new Image
%  structure and returns a pointer to the new image.
%
%  The format of the ReadYUVImage method is:
%
%      Image *ReadYUVImage(const ImageInfo *image_info)
%
%  A description of each parameter follows:
%
%    o image:  Method ReadYUVImage returns a pointer to the image after
%      reading.  A null image is returned if there is a memory shortage or
%      if the image cannot be read.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%
*/
Export Image *ReadYUVImage(const ImageInfo *image_info)
{
  Image
    *chroma_image,
    *image,
    *zoom_image;

  int
    count,
    y;

  register int
    i,
    x;

  register PixelPacket
    *q,
    *r;

  register unsigned char
    *p;

  unsigned char
    *scanline;

  unsigned int
    status;

  /*
    Allocate image structure.
  */
  image=AllocateImage(image_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  if ((image->columns == 0) || (image->rows == 0))
    ReaderExit(OptionWarning,"Must specify image size",image);
  image->depth=8;
  if (image_info->interlace != PartitionInterlace)
    {
      /*
        Open image file.
      */
      status=OpenBlob(image_info,image,ReadBinaryType);
      if (status == False)
        ReaderExit(FileOpenWarning,"Unable to open file",image);
      for (i=0; i < image->offset; i++)
        (void) ReadByte(image);
    }
  /*
    Allocate memory for a scanline.
  */
  scanline=(unsigned char *)
    AllocateMemory(image->columns*sizeof(unsigned char));
  if (scanline == (unsigned char *) NULL)
    ReaderExit(ResourceLimitWarning,"Memory allocation failed",image);
  do
  {
    /*
      Convert raster image to pixel packets.
    */
    if (image_info->interlace == PartitionInterlace)
      {
        AppendImageFormat("Y",image->filename);
        status=OpenBlob(image_info,image,ReadBinaryType);
        if (status == False)
          ReaderExit(FileOpenWarning,"Unable to open file",image);
      }
    for (y=0; y < (int) image->rows; y++)
    {
      if ((y > 0) || (image->previous == (Image *) NULL))
        (void) ReadBlob(image,image->columns,scanline);
      p=scanline;
      q=SetPixelCache(image,0,y,image->columns,1);
      if (q == (PixelPacket *) NULL)
        break;
      for (x=0; x < (int) image->columns; x++)
      {
        q->red=UpScale(*p++);
        q->green=0;
        q->blue=0;
        q++;
      }
      if (!SyncPixelCache(image))
        break;
      if (image->previous == (Image *) NULL)
        ProgressMonitor(LoadImageText,y,image->rows);
    }
    if (image_info->interlace == PartitionInterlace)
      {
        CloseBlob(image);
        AppendImageFormat("U",image->filename);
        status=OpenBlob(image_info,image,ReadBinaryType);
        if (status == False)
          ReaderExit(FileOpenWarning,"Unable to open file",image);
      }
    chroma_image=CloneImage(image,image->columns/2,image->rows/2,True);
    if (chroma_image == (Image *) NULL)
      ReaderExit(ResourceLimitWarning,"Memory allocation failed",image);
    for (y=0; y < (int) chroma_image->rows; y++)
    {
      (void) ReadBlob(image,chroma_image->columns,scanline);
      p=scanline;
      q=SetPixelCache(chroma_image,0,y,chroma_image->columns,1);
      if (q == (PixelPacket *) NULL)
        break;
      for (x=0; x < (int) chroma_image->columns; x++)
      {
        q->red=0;
        q->green=UpScale(*p++);
        q->blue=0;
        q++;
      }
      if (!SyncPixelCache(chroma_image))
        break;
    }
    if (image_info->interlace == PartitionInterlace)
      {
        CloseBlob(image);
        AppendImageFormat("V",image->filename);
        status=OpenBlob(image_info,image,ReadBinaryType);
        if (status == False)
          ReaderExit(FileOpenWarning,"Unable to open file",image);
      }
    for (y=0; y < (int) chroma_image->rows; y++)
    {
      (void) ReadBlob(image,chroma_image->columns,scanline);
      p=scanline;
      q=GetPixelCache(chroma_image,0,y,chroma_image->columns,1);
      if (q == (PixelPacket *) NULL)
        break;
      for (x=0; x < (int) chroma_image->columns; x++)
      {
        q->blue=UpScale(*p++);
        q++;
      }
      if (!SyncPixelCache(chroma_image))
        break;
    }
    /*
      Scale image.
    */
    chroma_image->orphan=True;
    zoom_image=SampleImage(chroma_image,image->columns,image->rows);
    DestroyImage(chroma_image);
    if (zoom_image == (Image *) NULL)
      ReaderExit(ResourceLimitWarning,"Memory allocation failed",image);
    for (y=0; y < (int) image->rows; y++)
    {
      q=GetPixelCache(image,0,y,image->columns,1);
      r=GetPixelCache(zoom_image,0,y,zoom_image->columns,1);
      if ((q == (PixelPacket *) NULL) || (r == (PixelPacket *) NULL))
        break;
      for (x=0; x < (int) image->columns; x++)
      {
        q->green=r->green;
        q->blue=r->blue;
        r++;
        q++;
      }
      if (!SyncPixelCache(image))
        break;
    }
    DestroyImage(zoom_image);
    TransformRGBImage(image,YCbCrColorspace);
    if (image_info->interlace == PartitionInterlace)
      (void) strcpy(image->filename,image_info->filename);
    /*
      Proceed to next image.
    */
    if (image_info->subrange != 0)
      if (image->scene >= (image_info->subimage+image_info->subrange-1))
        break;
    count=ReadBlob(image,image->columns,(char *) scanline);
    if (count > 0)
      {
        /*
          Allocate next image structure.
        */
        AllocateNextImage(image_info,image);
        if (image->next == (Image *) NULL)
          {
            DestroyImages(image);
            return((Image *) NULL);
          }
        image=image->next;
        ProgressMonitor(LoadImagesText,TellBlob(image),image->filesize);
      }
  } while (count > 0);
  FreeMemory(scanline);
  while (image->previous != (Image *) NULL)
    image=image->previous;
  CloseBlob(image);
  return(image);
}
int tc_filter(frame_list_t *ptr_, char *options)
{
	vframe_list_t *ptr = (vframe_list_t *)ptr_;
	int instance = ptr->filter_id;
	Image *pattern, *resized, *orig = 0;
	ImageInfo *image_info;

	PixelPacket *pixel_packet;
	pixelsMask *pixel_last;
	ExceptionInfo exception_info;

	if(ptr->tag & TC_FILTER_GET_CONFIG) {
		char buf[128];
		optstr_filter_desc(options, MOD_NAME, MOD_CAP, MOD_VERSION,
				   MOD_AUTHOR, "VRYMO", "1");

		tc_snprintf(buf, 128, "/dev/null");
		optstr_param(options, "pattern", "Pattern image file path", "%s", buf);
		tc_snprintf(buf, 128, "results.dat");
		optstr_param(options, "results", "Results file path" , "%s", buf);
		tc_snprintf(buf, 128, "%f", compare[instance]->delta);
		optstr_param(options, "delta", "Delta error", "%f",buf,"0.0", "100.0");
		return 0;
	}

	//----------------------------------
	//
	// filter init
	//
	//----------------------------------


	if(ptr->tag & TC_FILTER_INIT)
	{

		unsigned int t,r,index;
		pixelsMask *temp;

		compare[instance] = tc_malloc(sizeof(compareData));
		if(compare[instance] == NULL)
			return (-1);

		compare[instance]->vob = tc_get_vob();
		if(compare[instance]->vob ==NULL)
            return(-1);

		compare[instance]->delta=DELTA_COLOR;
		compare[instance]->step=1;
		compare[instance]->width=0;
		compare[instance]->height=0;
		compare[instance]->frames = 0;
		compare[instance]->pixel_mask = NULL;
		pixel_last = NULL;

		compare[instance]->width = compare[instance]->vob->ex_v_width;
		compare[instance]->height = compare[instance]->vob->ex_v_height;

		if (options != NULL) {
			char pattern_name[PATH_MAX];
			char results_name[PATH_MAX];
			memset(pattern_name,0,PATH_MAX);
			memset(results_name,0,PATH_MAX);

			if(verbose) tc_log_info(MOD_NAME, "options=%s", options);

			optstr_get(options, "pattern", "%[^:]", pattern_name);
			optstr_get(options, "results", "%[^:]", results_name);
			optstr_get(options, "delta", "%f", &compare[instance]->delta);

			if (verbose > 1) {
				tc_log_info(MOD_NAME, "Compare Image Settings:");
				tc_log_info(MOD_NAME, "      pattern = %s\n", pattern_name);
				tc_log_info(MOD_NAME, "      results = %s\n", results_name);
				tc_log_info(MOD_NAME, "        delta = %f\n", compare[instance]->delta);
			}

			if (strlen(results_name) == 0) {
				// Ponemos el nombre del fichero al original con extension dat
				strlcpy(results_name, "/tmp/compare.dat", sizeof(results_name));

			}
			if (!(compare[instance]->results = fopen(results_name, "w")))
			{
				tc_log_perror(MOD_NAME, "could not open file for writing");
			}

			InitializeMagick("");
			if (verbose > 1)
                tc_log_info(MOD_NAME, "Magick Initialized successfully");

			GetExceptionInfo(&exception_info);
			image_info = CloneImageInfo ((ImageInfo *) NULL);
			strlcpy(image_info->filename, pattern_name, MaxTextExtent);
			if (verbose > 1)
			     tc_log_info(MOD_NAME, "Trying to open image");
			orig = ReadImage(image_info,
					 &exception_info);

			if (orig == (Image *) NULL) {
				MagickWarning(exception_info.severity,
					      exception_info.reason,
					      exception_info.description);
				strlcpy(pattern_name, "/dev/null", sizeof(pattern_name));
			}else{
			       if (verbose > 1)
			       		tc_log_info(MOD_NAME, "Image loaded successfully");
			     }
		}

		else{
			tc_log_perror(MOD_NAME, "Not image provided");
		}

		if (options != NULL)
			if (optstr_lookup (options, "help")) {
				help_optstr();
			}


		fprintf(compare[instance]->results,"#fps:%f\n",compare[instance]->vob->fps);

		if (orig != NULL){
                        // Flip and resize
			if (compare[instance]->vob->im_v_codec == CODEC_YUV)
				TransformRGBImage(orig,YCbCrColorspace);
			if (verbose > 1) tc_log_info(MOD_NAME, "Resizing the Image");
			resized = ResizeImage(orig,
					      compare[instance]->width,
					      compare[instance]->height,
					      GaussianFilter,
					      1,
					      &exception_info);
			if (verbose > 1)
				tc_log_info(MOD_NAME, "Flipping the Image");
			pattern = FlipImage(resized, &exception_info);
			if (pattern == (Image *) NULL) {
				MagickError (exception_info.severity,
					     exception_info.reason,
					     exception_info.description);
			}

			// Filling the matrix with the pixels values not
			// alpha

			if (verbose > 1) tc_log_info(MOD_NAME, "GetImagePixels");
			pixel_packet = GetImagePixels(pattern,0,0,
						      pattern->columns,
						      pattern->rows);

			if (verbose > 1) tc_log_info(MOD_NAME, "Filling the Image matrix");
			for (t = 0; t < pattern->rows; t++)
				for (r = 0; r < pattern->columns; r++){
					index = t*pattern->columns + r;
					if (pixel_packet[index].opacity == 0){
						temp=tc_malloc(sizeof(struct pixelsMask));
						temp->row=t;
						temp->col=r;
						temp->r = (uint8_t)ScaleQuantumToChar(pixel_packet[index].red);
						temp->g = (uint8_t)ScaleQuantumToChar(pixel_packet[index].green);
						temp->b = (uint8_t)ScaleQuantumToChar(pixel_packet[index].blue);
						temp->next=NULL;

						if (pixel_last == NULL){
							pixel_last = temp;
							compare[instance]->pixel_mask = temp;
						}else{
							pixel_last->next = temp;
							pixel_last = temp;
						}
					}
				}

			if (verbose)
                tc_log_info(MOD_NAME, "%s %s",
					    MOD_VERSION, MOD_CAP);
		}
		return(0);
	}


	//----------------------------------
	//
	// filter close
	//
	//----------------------------------


	if(ptr->tag & TC_FILTER_CLOSE) {

		if (compare[instance] != NULL) {
			fclose(compare[instance]->results);
			free(compare[instance]);
		}
		DestroyMagick();
		compare[instance]=NULL;

		return(0);

	} /* filter close */

	//----------------------------------
	//
	// filter frame routine
	//
	//----------------------------------


	// tag variable indicates, if we are called before
	// transcodes internal video/audio frame processing routines
	// or after and determines video/audio context

	if((ptr->tag & TC_POST_M_PROCESS) && (ptr->tag & TC_VIDEO))  {
		// For now I only support RGB color space
		pixelsMask *item = NULL;
		double sr,sg,sb;
		double avg_dr,avg_dg,avg_db;

		if (compare[instance]->vob->im_v_codec == CODEC_RGB){

			int r,g,b,c;
			double width_long;

			if (compare[instance]->pixel_mask != NULL)
			{
				item = compare[instance]->pixel_mask;
				c = 0;

				sr = 0.0;
				sg = 0.0;
				sb = 0.0;

				width_long = compare[instance]->width*3;
				while(item){
					r = item->row*width_long + item->col*3;
					g = item->row*width_long
						+ item->col*3 + 1;
					b = item->row*width_long
						+ item->col*3 + 2;

				// diff between points
				// Interchange RGB values if necesary
					sr = sr + (double)abs((unsigned char)ptr->video_buf[r] - item->r);
					sg = sg + (double)abs((unsigned char)ptr->video_buf[g] - item->g);
					sb = sb + (double)abs((unsigned char)ptr->video_buf[b] - item->b);
					item = item->next;
					c++;
				}

				avg_dr = sr/(double)c;
				avg_dg = sg/(double)c;
				avg_db = sb/(double)c;

				if ((avg_dr < compare[instance]->delta) && (avg_dg < compare[instance]->delta) && (avg_db < compare[instance]->delta))
					fprintf(compare[instance]->results,"1");
				else
					fprintf(compare[instance]->results,"n");
				fflush(compare[instance]->results);
			}
			compare[instance]->frames++;
			return(0);
		}else{

                        // The colospace is YUV

                        // FIXME: Doesn't works, I need to code all this part
			// again

			int Y,Cr,Cb,c;

			if (compare[instance]->pixel_mask != NULL)
			{
				item = compare[instance]->pixel_mask;
				c = 0;

				sr = 0.0;
				sg = 0.0;
				sb = 0.0;

				while(item){
					Y  = item->row*compare[instance]->width + item->col;
					Cb = compare[instance]->height*compare[instance]->width
						+ (int)((item->row*compare[instance]->width + item->col)/4);
					Cr = compare[instance]->height*compare[instance]->width
						+ (int)((compare[instance]->height*compare[instance]->width)/4)
						+ (int)((item->row*compare[instance]->width + item->col)/4);

				        // diff between points
				        // Interchange RGB values if necesary

					sr = sr + (double)abs((unsigned char)ptr->video_buf[Y] - item->r);
					sg = sg + (double)abs((unsigned char)ptr->video_buf[Cb] - item->g);
					sb = sb + (double)abs((unsigned char)ptr->video_buf[Cr] - item->b);
					item = item->next;
					c++;
				}

				avg_dr = sr/(double)c;
				avg_dg = sg/(double)c;
				avg_db = sb/(double)c;

				if ((avg_dr < compare[instance]->delta) && (avg_dg < compare[instance]->delta) && (avg_db < compare[instance]->delta))
					fprintf(compare[instance]->results,"1");
				else
					fprintf(compare[instance]->results,"n");
			}
			compare[instance]->frames++;
			return(0);

		}
	}

	return(0);
}
예제 #3
0
파일: yuv.c 프로젝트: vgck/opendr2
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   W r i t e Y U V I m a g e                                                 %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method WriteYUVImage writes an image to a file in the digital YUV
%  (CCIR 601 4:1:1) format.
%
%  The format of the WriteYUVImage method is:
%
%      unsigned int WriteYUVImage(const ImageInfo *image_info,Image *image)
%
%  A description of each parameter follows.
%
%    o status: Method WriteYUVImage return True if the image is written.
%      False is returned is there is a memory shortage or if the image file
%      fails to write.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%    o image:  A pointer to a Image structure.
%
%
*/
Export unsigned int WriteYUVImage(const ImageInfo *image_info,Image *image)
{
  Image
    *chroma_image,
    *yuv_image;

  int
    y;

  register int
    x;

  register PixelPacket
    *p;

  unsigned int
    height,
    scene,
    status,
    width;

  if (image_info->interlace != PartitionInterlace)
    {
      /*
        Open output image file.
      */
      status=OpenBlob(image_info,image,WriteBinaryType);
      if (status == False)
        WriterExit(FileOpenWarning,"Unable to open file",image);
    }
  if (image_info->interlace == PartitionInterlace)
    {
      AppendImageFormat("Y",image->filename);
      status=OpenBlob(image_info,image,WriteBinaryType);
      if (status == False)
        WriterExit(FileOpenWarning,"Unable to open file",image);
    }
  scene=0;
  do
  {
    /*
      Sample image to an even width and height.
    */
    TransformRGBImage(image,RGBColorspace);
    width=image->columns+(image->columns & 0x01);
    height=image->rows+(image->rows & 0x01);
    image->orphan=True;
    yuv_image=SampleImage(image,width,height);
    if (yuv_image == (Image *) NULL)
      WriterExit(ResourceLimitWarning,"Unable to zoom image",image);
    RGBTransformImage(yuv_image,YCbCrColorspace);
    /*
      Initialize Y channel.
    */
    for (y=0; y < (int) yuv_image->rows; y++)
    {
      p=GetPixelCache(yuv_image,0,y,yuv_image->columns,1);
      if (p == (PixelPacket *) NULL)
        break;
      for (x=0; x < (int) yuv_image->columns; x++)
      {
        (void) WriteByte(image,DownScale(p->red));
        p++;
      }
      if (image->previous == (Image *) NULL)
        if (QuantumTick(y,image->rows))
          ProgressMonitor(SaveImageText,y,image->rows);
    }
    DestroyImage(yuv_image);
    /*
      Downsample image.
    */
    image->orphan=True;
    chroma_image=SampleImage(image,width/2,height/2);
    if (chroma_image == (Image *) NULL)
      WriterExit(ResourceLimitWarning,"Unable to zoom image",image);
    RGBTransformImage(chroma_image,YCbCrColorspace);
    /*
      Initialize U channel.
    */
    if (image_info->interlace == PartitionInterlace)
      {
        CloseBlob(image);
        AppendImageFormat("U",image->filename);
        status=OpenBlob(image_info,image,WriteBinaryType);
        if (status == False)
          WriterExit(FileOpenWarning,"Unable to open file",image);
      }
    for (y=0; y < (int) chroma_image->rows; y++)
    {
      p=GetPixelCache(chroma_image,0,y,chroma_image->columns,1);
      if (p == (PixelPacket *) NULL)
        break;
      for (x=0; x < (int) chroma_image->columns; x++)
      {
        (void) WriteByte(image,DownScale(p->green));
        p++;
      }
    }
    /*
      Initialize V channel.
    */
    if (image_info->interlace == PartitionInterlace)
      {
        CloseBlob(image);
        AppendImageFormat("V",image->filename);
        status=OpenBlob(image_info,image,WriteBinaryType);
        if (status == False)
          WriterExit(FileOpenWarning,"Unable to open file",image);
      }
    for (y=0; y < (int) chroma_image->rows; y++)
    {
      p=GetPixelCache(chroma_image,0,y,chroma_image->columns,1);
      if (p == (PixelPacket *) NULL)
        break;
      for (x=0; x < (int) chroma_image->columns; x++)
      {
	(void) WriteByte(image,DownScale(p->blue));
        p++;
      }
    }
    DestroyImage(chroma_image);
    if (image_info->interlace == PartitionInterlace)
      (void) strcpy(image->filename,image_info->filename);
    if (image->next == (Image *) NULL)
      break;
    image=GetNextImage(image);
    ProgressMonitor(SaveImagesText,scene++,GetNumberScenes(image));
  } while (image_info->adjoin);
  if (image_info->adjoin)
    while (image->previous != (Image *) NULL)
      image=image->previous;
  CloseBlob(image);
  return(True);
}
예제 #4
0
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   W r i t e H T M L I m a g e                                               %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method WriteHTMLImage writes an image in the HTML encoded image format.
%
%  The format of the WriteHTMLImage method is:
%
%      unsigned int WriteHTMLImage(const ImageInfo *image_info,Image *image)
%
%  A description of each parameter follows.
%
%    o status: Method WriteHTMLImage return True if the image is written.
%      False is returned is there is a memory shortage or if the image file
%      fails to write.
%
%    o image_info: Specifies a pointer to an ImageInfo structure.
%
%    o image:  A pointer to a Image structure.
%
%
*/
Export unsigned int WriteHTMLImage(const ImageInfo *image_info,Image *image)
{
  char
    buffer[MaxTextExtent],
    filename[MaxTextExtent],
    mapname[MaxTextExtent],
    url[MaxTextExtent];

  Image
    *next;

  ImageInfo
    *local_info;

  int
    x,
    y;

  register char
    *p;

  register PixelPacket
    *q;

  unsigned int
    height,
    status,
    width;

  /*
    Open image.
  */
  status=OpenBlob(image_info,image,WriteBinaryType);
  if (status == False)
    WriterExit(FileOpenWarning,"Unable to open file",image);
  CloseBlob(image);
  TransformRGBImage(image,RGBColorspace);
  *url='\0';
  if ((Latin1Compare(image_info->magick,"FTP") == 0) ||
      (Latin1Compare(image_info->magick,"HTTP") == 0))
    {
      /*
        Extract URL base from filename.
      */
      p=strrchr(image->filename,'/');
      if (p)
        {
          p++;
          (void) strcpy(url,image_info->magick);
          (void) strcat(url,":");
          url[Extent(url)+p-image->filename]='\0';
          (void) strncat(url,image->filename,p-image->filename);
          (void) strcpy(image->filename,p);
        }
    }
  /*
    Refer to image map file.
  */
  (void) strcpy(filename,image->filename);
  AppendImageFormat("map",filename);
  (void) strcpy(mapname,BaseFilename(filename));
  (void) strcpy(image->filename,image_info->filename);
  (void) strcpy(filename,image->filename);
  local_info=CloneImageInfo(image_info);
  if (local_info == (ImageInfo *) NULL)
    WriterExit(FileOpenWarning,"Unable to allocate memory",image);
  local_info->adjoin=True;
  status=True;
  if (Latin1Compare(image_info->magick,"SHTML") != 0)
    {
      /*
        Open output image file.
      */
      status=OpenBlob(image_info,image,WriteBinaryType);
      if (status == False)
        WriterExit(FileOpenWarning,"Unable to open file",image);
      /*
        Write the HTML image file.
      */
      (void) strcpy(buffer,"<html version=\"2.0\">\n");
      (void) WriteBlob(image,strlen(buffer),buffer);
      (void) strcpy(buffer,"<head>\n");
      (void) WriteBlob(image,strlen(buffer),buffer);
      (void) sprintf(buffer,"<title>%.1024s</title>\n",
        image->label ? image->label : BaseFilename(image->filename));
      (void) WriteBlob(image,strlen(buffer),buffer);
      (void) strcpy(buffer,"</head>\n");
      (void) WriteBlob(image,strlen(buffer),buffer);
      (void) strcpy(buffer,"<body>\n");
      (void) WriteBlob(image,strlen(buffer),buffer);
      (void) strcpy(buffer,"<center>\n");
      (void) WriteBlob(image,strlen(buffer),buffer);
      (void) sprintf(buffer,"<h1>%.1024s</h1>\n",image->filename);
      (void) WriteBlob(image,strlen(buffer),buffer);
      (void) strcpy(buffer,"<br><br>\n");
      (void) WriteBlob(image,strlen(buffer),buffer);
      (void) strcpy(filename,image->filename);
      AppendImageFormat("gif",filename);
      (void) sprintf(buffer,
        "<img ismap usemap=#%.1024s src=\"%.1024s\" border=0>\n",
        mapname,filename);
      (void) WriteBlob(image,strlen(buffer),buffer);
      /*
        Determine the size and location of each image tile.
      */
      width=image->columns;
      height=image->rows;
      x=0;
      y=0;
      if (image->montage != (char *) NULL)
        (void) ParseGeometry(image->montage,&x,&y,&width,&height);
      /*
        Write an image map.
      */
      (void) sprintf(buffer,"<map name=%.1024s>\n",mapname);
      (void) WriteBlob(image,strlen(buffer),buffer);
      (void) sprintf(buffer,"  <area href=""%.1024s""",url);
      (void) WriteBlob(image,strlen(buffer),buffer);
      if (image->directory == (char *) NULL)
        {
          (void) sprintf(buffer,"%.1024s shape=rect coords=0,0,%u,%u>\n",
            image->filename,width-1,height-1);
          (void) WriteBlob(image,strlen(buffer),buffer);
        }
      else
        for (p=image->directory; *p != '\0'; p++)
          if (*p != '\n')
            (void) WriteByte(image,*p);
          else
            {
              (void) sprintf(buffer," shape=rect coords=%d,%d,%d,%d>\n",
                x,y,x+(int) width-1,y+(int) height-1);
              (void) WriteBlob(image,strlen(buffer),buffer);
              if (*(p+1) != '\0')
                {
                  (void) sprintf(buffer,"  <area href=""%.1024s""",url);
                  (void) WriteBlob(image,strlen(buffer),buffer);
                }
              x+=width;
              if (x >= (int) image->columns)
                {
                  x=0;
                  y+=height;
                }
            }
      (void) strcpy(buffer,"</map>\n");
      (void) WriteBlob(image,strlen(buffer),buffer);
      if (image->montage != (char *) NULL)
        {
          char
            color[MaxTextExtent] = "#000";

          /*
            Make montage background transparent.
          */
          q=GetPixelCache(image,0,0,1,1);
          if (q != (PixelPacket *) NULL)
            FormatString(color,HexColorFormat,q->red,q->green,q->blue);
          TransparentImage(image,color);
        }
      (void) strcpy(filename,image->filename);
      (void) strcpy(buffer,"</center>\n");
      (void) WriteBlob(image,strlen(buffer),buffer);
      (void) strcpy(buffer,"</body>\n");
      (void) WriteBlob(image,strlen(buffer),buffer);
      (void) strcpy(buffer,"</html>\n");
      status=WriteBlob(image,strlen(buffer),buffer);
      CloseBlob(image);
      /*
        Write the image as transparent GIF.
      */
      (void) strcpy(image->filename,filename);
      AppendImageFormat("gif",image->filename);
      next=image->next;
      image->next=(Image *) NULL;
      status|=WriteGIFImage(local_info,image);
      image->next=next;
      /*
        Determine image map filename.
      */
      (void) strcpy(image->filename,filename);
      for (p=filename+Extent(filename)-1; p > (filename+1); p--)
        if (*p == '.')
          {
            (void) strncpy(image->filename,filename,p-filename);
            image->filename[p-filename]='\0';
            break;
          }
      (void) strcat(image->filename,"_map.shtml");
    }
  /*
    Open image map.
  */
  status=OpenBlob(local_info,image,WriteBinaryType);
  if (status == False)
    WriterExit(FileOpenWarning,"Unable to open file",image);
  DestroyImageInfo(local_info);
  /*
    Determine the size and location of each image tile.
  */
  width=image->columns;
  height=image->rows;
  x=0;
  y=0;
  if (image->montage != (char *) NULL)
    (void) ParseGeometry(image->montage,&x,&y,&width,&height);
  /*
    Write an image map.
  */
  (void) sprintf(buffer,"<map name=%.1024s>\n",mapname);
  (void) WriteBlob(image,strlen(buffer),buffer);
  (void) sprintf(buffer,"  <area href=""%.1024s""",url);
  (void) WriteBlob(image,strlen(buffer),buffer);
  if (image->directory == (char *) NULL)
    {
      (void) sprintf(buffer,"%.1024s shape=rect coords=0,0,%u,%u>\n",
        image->filename,width-1,height-1);
      (void) WriteBlob(image,strlen(buffer),buffer);
    }
  else
    for (p=image->directory; *p != '\0'; p++)
      if (*p != '\n')
        (void) WriteByte(image,*p);
      else
        {
          (void) sprintf(buffer," shape=rect coords=%d,%d,%d,%d>\n",x,y,
            x+(int) width-1,y+(int) height-1);
          (void) WriteBlob(image,strlen(buffer),buffer);
          if (*(p+1) != '\0')
            {
              (void) sprintf(buffer,"  <area href=""%.1024s""",url);
              (void) WriteBlob(image,strlen(buffer),buffer);
            }
          x+=width;
          if (x >= (int) image->columns)
            {
              x=0;
              y+=height;
            }
        }
  (void) strcpy(buffer,"</map>\n");
  (void) WriteBlob(image,strlen(buffer),buffer);
  CloseBlob(image);
  (void) strcpy(image->filename,filename);
  return(status);
}