Coverage Report

Created: 2025-07-23 08:18

/src/graphicsmagick/magick/average.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
% Copyright (C) 2003 - 2018 GraphicsMagick Group
3
% Copyright (C) 2003 ImageMagick Studio
4
% Copyright 1991-1999 E. I. du Pont de Nemours and Company
5
%
6
% This program is covered by multiple licenses, which are described in
7
% Copyright.txt. You should have received a copy of Copyright.txt with this
8
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
9
%
10
% GraphicsMagick Image Averaging Methods.
11
%
12
*/
13

14
/*
15
  Include declarations.
16
*/
17
#include "magick/studio.h"
18
#include "magick/monitor.h"
19
#include "magick/omp_data_view.h"
20
#include "magick/pixel_cache.h"
21

22
/*
23
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
24
%                                                                             %
25
%                                                                             %
26
%     A v e r a g e I m a g e s                                               %
27
%                                                                             %
28
%                                                                             %
29
%                                                                             %
30
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
31
%
32
%  The Average() method takes a set of images and averages them together.
33
%  Each image in the set must have the same width and height.  Average()
34
%  returns a single image with each corresponding pixel component of
35
%  each image averaged.   On failure, a NULL image is returned and
36
%  exception describes the reason for the failure.
37
%
38
%  The format of the AverageImage method is:
39
%
40
%      Image *AverageImages(Image *image,ExceptionInfo *exception)
41
%
42
%  A description of each parameter follows:
43
%
44
%    o image: The image sequence.
45
%
46
%    o exception: Return any errors or warnings in this structure.
47
%
48
%
49
*/
50
MagickExport Image *AverageImages(const Image *image,ExceptionInfo *exception)
51
0
{
52
0
  ThreadViewDataSet
53
0
    *pixels_sums;
54
55
0
  Image
56
0
    *average_image;
57
58
0
  const Image
59
0
    *last_image;
60
61
0
  long
62
0
    y;
63
64
0
  unsigned long
65
0
    row_count=0;
66
67
0
  double
68
0
    number_scenes;
69
70
0
  unsigned long
71
0
    number_pixels;
72
73
0
  MagickPassFail
74
0
    status=MagickPass;
75
76
  /*
77
    Ensure the image are the same size.
78
  */
79
0
  assert(image != (Image *) NULL);
80
0
  assert(image->signature == MagickSignature);
81
0
  assert(exception != (ExceptionInfo *) NULL);
82
0
  assert(exception->signature == MagickSignature);
83
0
  if (image->next == (Image *) NULL)
84
0
    ThrowImageException3(ImageError,ImageSequenceIsRequired,
85
0
                         UnableToAverageImage);
86
0
  {
87
0
    const Image
88
0
      *next;
89
90
0
    for (next=image; next != (Image *) NULL; next=next->next)
91
0
      {
92
0
        if ((next->columns != image->columns) || (next->rows != image->rows))
93
0
          ThrowImageException3(OptionError,UnableToAverageImageSequence,
94
0
                               ImageWidthsOrHeightsDiffer);
95
0
      }
96
0
  }
97
  /*
98
    Allocate sum accumulation buffer.
99
  */
100
0
  number_pixels=image->columns;
101
0
  pixels_sums=AllocateThreadViewDataArray(image,exception,number_pixels,
102
0
                                          sizeof(DoublePixelPacket));
103
0
  if (pixels_sums == (ThreadViewDataSet *) NULL)
104
0
    ThrowImageException3(ResourceLimitError,MemoryAllocationFailed,
105
0
                         UnableToAverageImageSequence);
106
  /*
107
    Initialize average next attributes.
108
  */
109
0
  average_image=CloneImage(image,image->columns,image->rows,True,exception);
110
0
  if (average_image == (Image *) NULL)
111
0
    {
112
0
      DestroyThreadViewDataSet(pixels_sums);
113
0
      return((Image *) NULL);
114
0
    }
115
0
  average_image->storage_class=DirectClass;
116
117
0
  number_scenes=(double) GetImageListLength(image);
118
0
  last_image=GetLastImageInList(image);
119
#if defined(HAVE_OPENMP)
120
#  pragma omp parallel for schedule(static) shared(row_count, status)
121
#endif
122
0
  for (y=0; y < (long) image->rows; y++)
123
0
    {
124
0
      register DoublePixelPacket
125
0
        *pixels_sum;
126
127
0
      const Image
128
0
        *next;
129
130
0
      register const PixelPacket
131
0
        *p;
132
133
0
      register long
134
0
        x;
135
136
0
      MagickBool
137
0
        thread_status;
138
139
0
      thread_status=status;
140
0
      if (thread_status == MagickFail)
141
0
        continue;
142
143
0
      pixels_sum=AccessThreadViewData(pixels_sums);
144
145
      /*
146
        Compute sum over each pixel color component.
147
      */
148
0
      for (next=image; next != (Image *) NULL; next=next->next)
149
0
        {
150
0
          ViewInfo
151
0
            *next_view;
152
153
0
          next_view=OpenCacheView((Image *) next);
154
0
          if (next_view == (ViewInfo *) NULL)
155
0
            thread_status=MagickFail;
156
0
          if (next_view != (ViewInfo *) NULL)
157
0
            {
158
0
              p=AcquireCacheViewPixels(next_view,0,y,next->columns,1,exception);
159
0
              if (p == (const PixelPacket *) NULL)
160
0
                thread_status=MagickFail;
161
0
              if (p != (const PixelPacket *) NULL)
162
0
                {
163
0
                  if (next == image)
164
0
                    {
165
0
                      for (x=0; x < (long) next->columns; x++)
166
0
                        {
167
0
                          pixels_sum[x].red=p[x].red;
168
0
                          pixels_sum[x].green=p[x].green;
169
0
                          pixels_sum[x].blue=p[x].blue;
170
0
                          pixels_sum[x].opacity=p[x].opacity;
171
0
                        }
172
0
                    }
173
0
                  else
174
0
                    {
175
0
                      for (x=0; x < (long) next->columns; x++)
176
0
                        {
177
0
                          pixels_sum[x].red+=p[x].red;
178
0
                          pixels_sum[x].green+=p[x].green;
179
0
                          pixels_sum[x].blue+=p[x].blue;
180
0
                          pixels_sum[x].opacity+=p[x].opacity;
181
0
                        }
182
0
                    }
183
0
                }
184
0
              CloseCacheView(next_view);
185
0
            }
186
0
        }
187
      /*
188
        Average next pixels.
189
      */
190
0
      if (thread_status != MagickFail)
191
0
        {
192
0
          register PixelPacket
193
0
            *q;
194
195
0
          q=SetImagePixelsEx(average_image,0,y,average_image->columns,1,exception);
196
0
          if (q == (PixelPacket *) NULL)
197
0
            thread_status=MagickFail;
198
0
          if (q != (PixelPacket *) NULL)
199
0
            {
200
0
              for (x=0; x < (long) average_image->columns; x++)
201
0
                {
202
0
                  q[x].red=(Quantum) (pixels_sum[x].red/number_scenes+0.5);
203
0
                  q[x].green=(Quantum) (pixels_sum[x].green/number_scenes+0.5);
204
0
                  q[x].blue=(Quantum) (pixels_sum[x].blue/number_scenes+0.5);
205
0
                  q[x].opacity=(Quantum) (pixels_sum[x].opacity/number_scenes+0.5);
206
0
                }
207
0
              if (!SyncImagePixelsEx(average_image,exception))
208
0
                thread_status=MagickFail;
209
0
            }
210
0
        }
211
212
#if defined(HAVE_OPENMP)
213
#  pragma omp atomic
214
#endif
215
0
      row_count++;
216
0
      if (QuantumTick(row_count,average_image->rows))
217
0
        if (!MagickMonitorFormatted(row_count,average_image->rows,exception,
218
0
                                    "[%s,...,%s] Average image sequence...",
219
0
                                    image->filename,last_image->filename))
220
0
          thread_status=MagickFail;
221
222
0
      if (thread_status == MagickFail)
223
0
        {
224
0
          status=MagickFail;
225
#if defined(HAVE_OPENMP)
226
#  pragma omp flush (status)
227
#endif
228
0
        }
229
0
    }
230
231
0
  DestroyThreadViewDataSet(pixels_sums);
232
233
0
  if (status == MagickFail)
234
0
    {
235
0
      DestroyImage(average_image);
236
0
      average_image=(Image *) NULL;
237
0
    }
238
239
0
  return(average_image);
240
0
}