source: ImageMagick/trunk/MagickCore/channel.c @ 7096

Revision 7096, 23.5 KB checked in by cristy, 15 months ago (diff)
Line 
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%               CCCC  H   H   AAA   N   N  N   N  EEEEE   L                   %
7%              C      H   H  A   A  NN  N  NN  N  E       L                   %
8%              C      HHHHH  AAAAA  N N N  N N N  RRR     L                   %
9%              C      H   H  A   A  N  NN  N  NN  E       L                   %
10%               CCCC  H   H  A   A  N   N  N   N  EEEEE   LLLLL               %
11%                                                                             %
12%                                                                             %
13%                      MagickCore Image Channel Methods                       %
14%                                                                             %
15%                              Software Design                                %
16%                                John Cristy                                  %
17%                               December 2003                                 %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization      %
21%  dedicated to making software imaging solutions freely available.           %
22%                                                                             %
23%  You may not use this file except in compliance with the License.  You may  %
24%  obtain a copy of the License at                                            %
25%                                                                             %
26%    http://www.imagemagick.org/script/license.php                            %
27%                                                                             %
28%  Unless required by applicable law or agreed to in writing, software        %
29%  distributed under the License is distributed on an "AS IS" BASIS,          %
30%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31%  See the License for the specific language governing permissions and        %
32%  limitations under the License.                                             %
33%                                                                             %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37%
38*/
39
40/*
41  Include declarations.
42*/
43#include "MagickCore/studio.h"
44#include "MagickCore/image.h"
45#include "MagickCore/list.h"
46#include "MagickCore/log.h"
47#include "MagickCore/monitor.h"
48#include "MagickCore/monitor-private.h"
49#include "MagickCore/option.h"
50#include "MagickCore/pixel-accessor.h"
51#include "MagickCore/string-private.h"
52#include "MagickCore/token.h"
53#include "MagickCore/utility.h"
54#include "MagickCore/version.h"
55
56/*
57%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
58%                                                                             %
59%                                                                             %
60%                                                                             %
61%     C h a n n e l F x I m a g e                                             %
62%                                                                             %
63%                                                                             %
64%                                                                             %
65%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
66%
67%  ChannelFxImage() applies a channel expression to the specified image.  The
68%  expression consists of one or more channels, either mnemonic or numeric (e.g.
69%  red, 1), separated by actions as follows:
70%
71%    <=>     exchange two channels (e.g. red<=>blue)
72%    =>      transfer a channel to another (e.g. red=>green)
73%    =       assign a constant (e.g. red=50%)
74%    ,       separate channel operations (e.g. red, green)
75%    |       read channels from next input image (e.g. red | green)
76%    ;       write channels to next output image (e.g. red; green; blue)
77%
78%  A channel without a operation symbol implies extract. For example, to create
79%  3 grayscale images from the red, green, and blue channels of an image, use:
80%
81%    -channel-fx "red; green; blue"
82%
83%  The format of the ChannelFxImage method is:
84%
85%      Image *ChannelFxImage(const Image *image,const char *expression,
86%        ExceptionInfo *exception)
87%
88%  A description of each parameter follows:
89%
90%    o image: the image.
91%
92%    o expression: A channel expression.
93%
94%    o exception: return any errors or warnings in this structure.
95%
96*/
97
98typedef enum
99{
100  ExtractChannelOp,
101  AssignChannelOp,
102  ExchangeChannelOp,
103  TransferChannelOp
104} ChannelFx;
105
106static inline size_t MagickMin(const size_t x,const size_t y)
107{
108  if (x < y)
109    return(x);
110  return(y);
111}
112
113static MagickBooleanType ChannelImage(Image *destination_image,
114  const PixelChannel destination_channel,const ChannelFx channel_op,
115  const Image *source_image,const PixelChannel source_channel,
116  const Quantum pixel,ExceptionInfo *exception)
117{
118  CacheView
119    *source_view,
120    *destination_view;
121
122  MagickBooleanType
123    status;
124
125  size_t
126    height;
127
128  ssize_t
129    y;
130
131  status=MagickTrue;
132  source_view=AcquireCacheView(source_image);
133  destination_view=AcquireCacheView(destination_image);
134  height=MagickMin(source_image->rows,destination_image->rows);
135#if defined(MAGICKCORE_OPENMP_SUPPORT)
136  #pragma omp parallel for schedule(static) shared(status)
137#endif
138  for (y=0; y < (ssize_t) height; y++)
139  {
140    PixelTrait
141      destination_traits,
142      source_traits;
143
144    register const Quantum
145      *restrict p;
146
147    register Quantum
148      *restrict q;
149
150    register ssize_t
151      x;
152
153    size_t
154      width;
155
156    if (status == MagickFalse)
157      continue;
158    p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
159      exception);
160    q=GetCacheViewAuthenticPixels(destination_view,0,y,
161      destination_image->columns,1,exception);
162    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
163      {
164        status=MagickFalse;
165        continue;
166      }
167    destination_traits=GetPixelChannelMapTraits(destination_image,
168      destination_channel);
169    source_traits=GetPixelChannelMapTraits(source_image,source_channel);
170    if ((destination_traits == UndefinedPixelTrait) ||
171        (source_traits == UndefinedPixelTrait))
172      continue;
173    width=MagickMin(source_image->columns,destination_image->columns);
174    for (x=0; x < (ssize_t) width; x++)
175    {
176      if (channel_op == AssignChannelOp)
177        SetPixelChannel(destination_image,destination_channel,pixel,q);
178      else
179        SetPixelChannel(destination_image,destination_channel,
180          GetPixelChannel(source_image,source_channel,p),q);
181      p+=GetPixelChannels(source_image);
182      q+=GetPixelChannels(destination_image);
183    }
184    if (SyncCacheViewAuthenticPixels(destination_view,exception) == MagickFalse)
185      status=MagickFalse;
186  }
187  destination_view=DestroyCacheView(destination_view);
188  source_view=DestroyCacheView(source_view);
189  return(status);
190}
191
192MagickExport Image *ChannelFxImage(const Image *image,const char *expression,
193  ExceptionInfo *exception)
194{
195#define ChannelFxImageTag  "ChannelFx/Image"
196
197  ChannelFx
198    channel_op;
199
200  char
201    token[MaxTextExtent];
202
203  const char
204    *p;
205
206  const Image
207    *source_image;
208
209  double
210    pixel;
211
212  Image
213    *destination_image;
214
215  MagickBooleanType
216    status;
217
218  PixelChannel
219    source_channel,
220    destination_channel;
221
222  ssize_t
223    channels;
224
225  assert(image != (Image *) NULL);
226  assert(image->signature == MagickSignature);
227  if (image->debug != MagickFalse)
228    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
229  assert(exception != (ExceptionInfo *) NULL);
230  assert(exception->signature == MagickSignature);
231  source_image=image;
232  destination_image=CloneImage(source_image,0,0,MagickTrue,exception);
233  if (destination_image == (Image *) NULL)
234    return((Image *) NULL);
235  if (destination_image->colorspace == GRAYColorspace)
236    destination_image->colorspace=RGBColorspace;
237  if (expression == (const char *) NULL)
238    return(destination_image);
239  destination_channel=RedPixelChannel;
240  pixel=0.0;
241  p=(char *) expression;
242  GetMagickToken(p,&p,token);
243  channel_op=ExtractChannelOp;
244  for (channels=0; *p != '\0'; )
245  {
246    ssize_t
247      i;
248
249    /*
250      Interpret channel expression.
251    */
252    if (*token == ',')
253      {
254        destination_channel=(PixelChannel) ((ssize_t) destination_channel+1);
255        GetMagickToken(p,&p,token);
256      }
257    if (*token == '|')
258      {
259        if (GetNextImageInList(source_image) != (Image *) NULL)
260          source_image=GetNextImageInList(source_image);
261        else
262          source_image=GetFirstImageInList(source_image);
263        GetMagickToken(p,&p,token);
264      }
265    if (*token == ';')
266      {
267        Image
268          *canvas;
269
270        status=SetImageStorageClass(destination_image,DirectClass,exception);
271        if (status == MagickFalse)
272          {
273            destination_image=DestroyImageList(destination_image);
274            return(destination_image);
275          }
276        if ((channel_op == ExtractChannelOp) && (channels == 1))
277          {
278            destination_image->colorspace=GRAYColorspace;
279            InitializePixelChannelMap(destination_image);
280          }
281        canvas=CloneImage(source_image,0,0,MagickTrue,exception);
282        if (canvas == (Image *) NULL)
283          {
284            destination_image=DestroyImageList(destination_image);
285            return(destination_image);
286          }
287        if (canvas->colorspace == GRAYColorspace)
288          canvas->colorspace=RGBColorspace;
289        AppendImageToList(&destination_image,canvas);
290        destination_image=GetLastImageInList(destination_image);
291        GetMagickToken(p,&p,token);
292        channels=0;
293        destination_channel=RedPixelChannel;
294      }
295    i=ParsePixelChannelOption(token);
296    if (i < 0)
297      {
298        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
299          "UnrecognizedChannelType","`%s'",token);
300        destination_image=DestroyImageList(destination_image);
301        return(destination_image);
302      }
303    source_channel=(PixelChannel) i;
304    channel_op=ExtractChannelOp;
305    GetMagickToken(p,&p,token);
306    if (*token == '<')
307      {
308        channel_op=ExchangeChannelOp;
309        GetMagickToken(p,&p,token);
310      }
311    if (*token == '=')
312      {
313        if (channel_op != ExchangeChannelOp)
314          channel_op=AssignChannelOp;
315        GetMagickToken(p,&p,token);
316      }
317    if (*token == '>')
318      {
319        if (channel_op != ExchangeChannelOp)
320          channel_op=TransferChannelOp;
321        GetMagickToken(p,&p,token);
322      }
323    switch (channel_op)
324    {
325      case AssignChannelOp:
326      {
327        pixel=StringToDoubleInterval(token,(double) QuantumRange+1.0);
328        GetMagickToken(p,&p,token);
329        break;
330      }
331      case ExchangeChannelOp:
332      case TransferChannelOp:
333      {
334        i=ParsePixelChannelOption(token);
335        if (i < 0)
336          {
337            (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
338              "UnrecognizedChannelType","`%s'",token);
339            destination_image=DestroyImageList(destination_image);
340            return(destination_image);
341          }
342        destination_channel=(PixelChannel) i;
343        GetMagickToken(p,&p,token);
344        break;
345      }
346      case ExtractChannelOp:
347        break;
348     }
349    status=ChannelImage(destination_image,destination_channel,channel_op,
350      source_image,source_channel,ClampToQuantum(pixel),exception);
351    if (status == MagickFalse)
352      {
353        destination_image=DestroyImageList(destination_image);
354        break;
355      }
356    channels++;
357    if (channel_op == ExchangeChannelOp)
358      {
359        status=ChannelImage(destination_image,source_channel,channel_op,
360          source_image,destination_channel,ClampToQuantum(pixel),exception);
361        if (status == MagickFalse)
362          {
363            destination_image=DestroyImageList(destination_image);
364            break;
365          }
366        channels++;
367      }
368    status=SetImageProgress(source_image,ChannelFxImageTag,p-expression,
369      strlen(expression));
370    if (status == MagickFalse)
371      break;
372  }
373  status=SetImageStorageClass(destination_image,DirectClass,exception);
374  if (status == MagickFalse)
375    {
376      destination_image=GetLastImageInList(destination_image);
377      return((Image *) NULL);
378    }
379  if ((channel_op == ExtractChannelOp) && (channels == 1))
380    {
381      destination_image->colorspace=GRAYColorspace;
382      InitializePixelChannelMap(destination_image);
383    }
384  return(destination_image);
385}
386
387/*
388%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
389%                                                                             %
390%                                                                             %
391%                                                                             %
392%     C o m b i n e I m a g e s                                               %
393%                                                                             %
394%                                                                             %
395%                                                                             %
396%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
397%
398%  CombineImages() combines one or more images into a single image.  The
399%  grayscale value of the pixels of each image in the sequence is assigned in
400%  order to the specified channels of the combined image.   The typical
401%  ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc.
402%
403%  The format of the CombineImages method is:
404%
405%      Image *CombineImages(const Image *image,ExceptionInfo *exception)
406%
407%  A description of each parameter follows:
408%
409%    o image: the image.
410%
411%    o exception: return any errors or warnings in this structure.
412%
413*/
414MagickExport Image *CombineImages(const Image *image,ExceptionInfo *exception)
415{
416#define CombineImageTag  "Combine/Image"
417
418  CacheView
419    *combine_view;
420
421  Image
422    *combine_image;
423
424  MagickBooleanType
425    status;
426
427  MagickOffsetType
428    progress;
429
430  ssize_t
431    y;
432
433  /*
434    Ensure the image are the same size.
435  */
436  assert(image != (const Image *) NULL);
437  assert(image->signature == MagickSignature);
438  if (image->debug != MagickFalse)
439    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
440  assert(exception != (ExceptionInfo *) NULL);
441  assert(exception->signature == MagickSignature);
442  combine_image=CloneImage(image,0,0,MagickTrue,exception);
443  if (combine_image == (Image *) NULL)
444    return((Image *) NULL);
445  if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse)
446    {
447      combine_image=DestroyImage(combine_image);
448      return((Image *) NULL);
449    }
450  if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
451    combine_image->matte=MagickTrue;
452  /*
453    Combine images.
454  */
455  status=MagickTrue;
456  progress=0;
457  combine_view=AcquireCacheView(combine_image);
458  for (y=0; y < (ssize_t) combine_image->rows; y++)
459  {
460    CacheView
461      *image_view;
462
463    const Image
464      *next;
465
466    Quantum
467      *pixels;
468
469    register const Quantum
470      *restrict p;
471
472    register Quantum
473      *restrict q;
474
475    register ssize_t
476      i;
477
478    if (status == MagickFalse)
479      continue;
480    pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
481      1,exception);
482    if (pixels == (Quantum *) NULL)
483      {
484        status=MagickFalse;
485        continue;
486      }
487    next=image;
488    for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
489    {
490      PixelChannel
491        channel;
492
493      PixelTrait
494        combine_traits,
495        traits;
496
497      register ssize_t
498        x;
499
500      if (next == (Image *) NULL)
501        continue;
502      channel=GetPixelChannelMapChannel(image,i);
503      traits=GetPixelChannelMapTraits(image,channel);
504      combine_traits=GetPixelChannelMapTraits(combine_image,channel);
505      if ((traits == UndefinedPixelTrait) ||
506          (combine_traits == UndefinedPixelTrait))
507        continue;
508      image_view=AcquireCacheView(next);
509      p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
510      if (p == (const Quantum *) NULL)
511        continue;
512      q=pixels;
513      for (x=0; x < (ssize_t) combine_image->columns; x++)
514      {
515        if (x < (ssize_t) image->columns)
516          {
517            q[i]=GetPixelGray(image,p);
518            p+=GetPixelChannels(image);
519          }
520        q+=GetPixelChannels(combine_image);
521      }
522      image_view=DestroyCacheView(image_view);
523      next=GetNextImageInList(next);
524      if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
525        status=MagickFalse;
526      if (image->progress_monitor != (MagickProgressMonitor) NULL)
527        {
528          MagickBooleanType
529            proceed;
530
531          proceed=SetImageProgress(image,CombineImageTag,progress++,
532            combine_image->rows);
533          if (proceed == MagickFalse)
534            status=MagickFalse;
535        }
536    }
537  }
538  combine_view=DestroyCacheView(combine_view);
539  if (status == MagickFalse)
540    combine_image=DestroyImage(combine_image);
541  return(combine_image);
542}
543
544/*
545%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
546%                                                                             %
547%                                                                             %
548%                                                                             %
549%     S e p a r a t e I m a g e                                               %
550%                                                                             %
551%                                                                             %
552%                                                                             %
553%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
554%
555%  SeparateImage() separates a channel from the image and returns it as a
556%  grayscale image.
557%
558%  The format of the SeparateImage method is:
559%
560%      Image *SeparateImage(const Image *image,const ChannelType channel,
561%        ExceptionInfo *exception)
562%
563%  A description of each parameter follows:
564%
565%    o image: the image.
566%
567%    o channel: the image channel.
568%
569%    o exception: return any errors or warnings in this structure.
570%
571*/
572MagickExport Image *SeparateImage(const Image *image,
573  const ChannelType channel_type,ExceptionInfo *exception)
574{
575#define GetChannelBit(mask,bit)  (((size_t) (mask) >> (size_t) (bit)) & 0x01)
576#define SeparateImageTag  "Separate/Image"
577
578  CacheView
579    *image_view,
580    *separate_view;
581
582  Image
583    *separate_image;
584
585  MagickBooleanType
586    status;
587
588  MagickOffsetType
589    progress;
590
591  ssize_t
592    y;
593
594  /*
595    Initialize spread image attributes.
596  */
597  assert(image != (Image *) NULL);
598  assert(image->signature == MagickSignature);
599  if (image->debug != MagickFalse)
600    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
601  assert(exception != (ExceptionInfo *) NULL);
602  assert(exception->signature == MagickSignature);
603  separate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
604    exception);
605  if (separate_image == (Image *) NULL)
606    return((Image *) NULL);
607  if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
608    {
609      separate_image=DestroyImage(separate_image);
610      return((Image *) NULL);
611    }
612  separate_image->colorspace=GRAYColorspace;
613  /*
614    Separate image.
615  */
616  status=MagickTrue;
617  progress=0;
618  image_view=AcquireCacheView(image);
619  separate_view=AcquireCacheView(separate_image);
620#if defined(MAGICKCORE_OPENMP_SUPPORT)
621  #pragma omp parallel for schedule(static) shared(progress,status)
622#endif
623  for (y=0; y < (ssize_t) image->rows; y++)
624  {
625    register const Quantum
626      *restrict p;
627
628    register Quantum
629      *restrict q;
630
631    register ssize_t
632      x;
633
634    if (status == MagickFalse)
635      continue;
636    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
637    q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
638      exception);
639    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
640      {
641        status=MagickFalse;
642        continue;
643      }
644    for (x=0; x < (ssize_t) image->columns; x++)
645    {
646      register ssize_t
647        i;
648
649      if (GetPixelMask(image,p) != 0)
650        {
651          p+=GetPixelChannels(image);
652          q+=GetPixelChannels(separate_image);
653          continue;
654        }
655      SetPixelChannel(separate_image,GrayPixelChannel,0,q);
656      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
657      {
658        PixelChannel
659          channel;
660
661        PixelTrait
662          traits;
663
664        channel=GetPixelChannelMapChannel(image,i);
665        traits=GetPixelChannelMapTraits(image,channel);
666        if ((traits == UndefinedPixelTrait) ||
667            (GetChannelBit(channel_type,channel) == 0))
668          continue;
669        SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
670      }
671      p+=GetPixelChannels(image);
672      q+=GetPixelChannels(separate_image);
673    }
674    if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
675      status=MagickFalse;
676    if (image->progress_monitor != (MagickProgressMonitor) NULL)
677      {
678        MagickBooleanType
679          proceed;
680
681#if defined(MAGICKCORE_OPENMP_SUPPORT)
682  #pragma omp critical (MagickCore_SeparateImage)
683#endif
684        proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
685        if (proceed == MagickFalse)
686          status=MagickFalse;
687      }
688  }
689  separate_view=DestroyCacheView(separate_view);
690  image_view=DestroyCacheView(image_view);
691  return(separate_image);
692}
693
694/*
695%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
696%                                                                             %
697%                                                                             %
698%                                                                             %
699%     S e p a r a t e I m a g e s                                             %
700%                                                                             %
701%                                                                             %
702%                                                                             %
703%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
704%
705%  SeparateImages() returns a separate grayscale image for each channel
706%  specified.
707%
708%  The format of the SeparateImages method is:
709%
710%      MagickBooleanType SeparateImages(const Image *image,
711%        ExceptionInfo *exception)
712%
713%  A description of each parameter follows:
714%
715%    o image: the image.
716%
717%    o exception: return any errors or warnings in this structure.
718%
719*/
720MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
721{
722  Image
723    *images,
724    *separate_image;
725
726  register ssize_t
727    i;
728
729  assert(image != (Image *) NULL);
730  assert(image->signature == MagickSignature);
731  if (image->debug != MagickFalse)
732    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
733  images=NewImageList();
734  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
735  {
736    PixelChannel
737      channel;
738
739    PixelTrait
740      traits;
741
742    channel=GetPixelChannelMapChannel(image,i);
743    traits=GetPixelChannelMapTraits(image,channel);
744    if ((traits == UndefinedPixelTrait) ||
745        ((traits & UpdatePixelTrait) == 0))
746      continue;
747    separate_image=SeparateImage(image,(ChannelType) (1 << channel),exception);
748    if (separate_image != (Image *) NULL)
749      AppendImageToList(&images,separate_image);
750  }
751  return(images);
752}
Note: See TracBrowser for help on using the repository browser.