1//
2//  PRCustTraceEdges.m
3//  PRICE
4//
5//  Created by Riccardo Mottola on Fri Mar 19 2004.
6//  Copyright (c) 2004-2014 Carduus. All rights reserved.
7//
8// This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
9// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
10
11#import "PRCustTraceEdges.h"
12#import "PRTraceEdges.h"
13#import "PRMedian.h"
14#import "PRGrayscaleFilter.h"
15#include <math.h>
16#include <limits.h>
17
18
19@implementation PRCustTraceEdges
20
21- (PRImage *)filterImage:(PRImage *)image with:(NSArray *)parameters progressPanel:(PRCProgress *)progressPanel
22{
23    int filterType;
24    float thresLevel;
25    BOOL useZeroCross;
26    BOOL enable1;
27    enum medianForms form1;
28    int size1;
29    BOOL separable1;
30    BOOL enable2;
31    enum medianForms form2;
32    int size2;
33    BOOL separable2;
34    BOOL enable3;
35    enum medianForms form3;
36    int size3;
37    BOOL separable3;
38
39
40    /* interpret the parameters */
41    filterType = [[parameters objectAtIndex:0] intValue];
42    thresLevel = [[parameters objectAtIndex:1] floatValue];
43    useZeroCross = [[parameters objectAtIndex:2] boolValue];
44    enable1 = [[parameters objectAtIndex:3] boolValue];
45    form1 = [[parameters objectAtIndex:4] intValue];
46    size1 = [[parameters objectAtIndex:5] intValue];
47    separable1 = [[parameters objectAtIndex:6] boolValue];
48    enable2 = [[parameters objectAtIndex:7] boolValue];
49    form2 = [[parameters objectAtIndex:8] intValue];
50    size2 = [[parameters objectAtIndex:9] intValue];
51    separable2 = [[parameters objectAtIndex:10] boolValue];
52    enable3 = [[parameters objectAtIndex:11] boolValue];
53    form3 = [[parameters objectAtIndex:12] intValue];
54    size3 = [[parameters objectAtIndex:13] intValue];
55    separable3 = [[parameters objectAtIndex:14] boolValue];
56
57    return [self edgeImage
58        :image :filterType :thresLevel :useZeroCross
59        :enable1 :form1 :size1 :separable1
60        :enable2 :form2 :size2 :separable2
61        :enable3 :form3 :size3 :separable3
62        :progressPanel];
63}
64
65- (NSString *)actionName
66{
67    return @"Edge Tracer";
68}
69
70
71- (PRImage *)edgeImage :(PRImage *)srcImage :(int)filterType :(float)thresholdLevel :(BOOL)useZeroCross :(BOOL)enable1 :(enum medianForms)form1 :(int)size1 :(BOOL)separable1 :(BOOL)enable2 :(enum medianForms)form2 :(int)size2 :(BOOL)separable2 :(BOOL) enable3 :(enum medianForms)form3 :(int)size3 :(BOOL)separable3 :(PRCProgress *)prPanel
72{
73  PRImage           *destImage;
74  NSBitmapImageRep  *destImageRep;
75  unsigned char     *destData;
76  NSInteger         w, h;
77  NSInteger         x, y;
78  NSInteger         destSamplesPerPixel;
79  NSInteger         destBytesPerRow;
80  NSInteger         destBytesPerPixel;
81  PRMedian          *medianFilter;
82  PRTraceEdges      *edgeFilter;
83  int               finalLevels;
84  int               finalLevelSize;
85
86  progressSteps = 0;
87  totalProgressSteps = 1;
88  if (enable1)
89    totalProgressSteps += 2;
90  if (enable2)
91    totalProgressSteps += 2;
92  if (enable3)
93    totalProgressSteps += 2;
94
95  if (!enable1 && !enable2 && !enable3) /* if none of the median processors is enabled */
96    totalProgressSteps++;
97
98  progPanel = prPanel;
99
100  /* check the number of images to process */
101  finalLevels = 0;
102  if (enable1)
103    finalLevels++;
104  if (enable2)
105    finalLevels++;
106  if (enable3)
107    finalLevels++;
108  finalLevelSize = 0;
109  if (finalLevels)
110    finalLevelSize = UCHAR_MAX / finalLevels;
111
112  /* get source image representation and associated information */
113  if (progPanel != nil)
114    {
115      [self setActivity:@"Get image size"];
116      [self advanceProgress];
117    }
118
119  w = [srcImage width];
120  h = [srcImage height];
121
122  /* check bith depth and color/greyscale image */
123  if ([srcImage hasColor])
124    {
125      PRGrayscaleFilter *grayFilter;
126
127      grayFilter = [[PRGrayscaleFilter alloc] init];
128      srcImage = [grayFilter filterImage:srcImage :METHOD_LUMINANCE];
129      [grayFilter release];
130    }
131  destSamplesPerPixel = 1;
132
133  /* allocate destination image and its representation */
134  destImage = [[PRImage alloc] initWithSize:NSMakeSize(w, h)];
135  destImageRep = [[NSBitmapImageRep alloc]
136                    initWithBitmapDataPlanes:NULL
137                                  pixelsWide:w
138                                  pixelsHigh:h
139                               bitsPerSample:8
140                             samplesPerPixel:destSamplesPerPixel
141                                    hasAlpha:NO
142                                    isPlanar:NO
143                              colorSpaceName:NSCalibratedWhiteColorSpace
144                                 bytesPerRow:0
145                                bitsPerPixel:0];
146
147  destData = [destImageRep bitmapData];
148  destBytesPerRow = [destImageRep bytesPerRow];
149  destBytesPerPixel = [destImageRep bitsPerPixel] / 8;
150
151  /* let's make the paper white */
152  memset(destData, UCHAR_MAX, h * destBytesPerRow);
153
154  /* allocate filters */
155  medianFilter = [[PRMedian alloc] init];
156  edgeFilter   = [[PRTraceEdges alloc] init];
157
158  if (finalLevels > 0)
159    {
160      if (enable1)
161        {
162          PRImage   *firstImage;
163          NSBitmapImageRep  *firstImageRep;
164          NSInteger fiBytesPerRow;
165          NSInteger fiBytesPerPixel;
166          unsigned char     *fiData;
167
168            if (progPanel != nil)
169              {
170                [self setActivity:@"Processing image 1: median"];
171                [self advanceProgress];
172              }
173          firstImage  = [medianFilter medianImage :srcImage :form1 :size1 :separable1 :NULL];
174          if (progPanel != nil)
175            {
176              [self setActivity:@"Processing image 1: trace edges"];
177              [self advanceProgress];
178            }
179          firstImage  = [edgeFilter edgeImage :firstImage  :filterType :YES :thresholdLevel :useZeroCross];
180          firstImageRep = [firstImage bitmapRep];
181          fiBytesPerRow = [firstImageRep bytesPerRow];
182          fiBytesPerPixel = [firstImageRep bitsPerPixel] / 8;
183          fiData = [firstImageRep bitmapData];
184          for (y = 0; y < h; y++)
185            {
186              for (x = 0; x < w; x++)
187                {
188                  if (*(fiData + fiBytesPerRow*y + fiBytesPerPixel*x) == 0)
189                    *(destData + destBytesPerRow*y + destBytesPerPixel*x) -= finalLevelSize;
190                }
191            }
192        }
193
194      if (enable2)
195        {
196          PRImage   *secondImage;
197          NSBitmapImageRep  *secondImageRep;
198          NSInteger siBytesPerRow;
199          NSInteger siBytesPerPixel;
200          unsigned char     *siData;
201
202            if (progPanel != nil)
203              {
204                [self setActivity:@"Processing image 2: median"];
205                [self advanceProgress];
206              }
207          secondImage = [medianFilter medianImage :srcImage :form2 :size2 :separable2 :NULL];
208          if (progPanel != nil)
209            {
210              [self setActivity:@"Processing image 2: trace edges"];
211              [self advanceProgress];
212            }
213          secondImage = [edgeFilter edgeImage :secondImage :filterType :YES :thresholdLevel :useZeroCross];
214          secondImageRep = [secondImage bitmapRep];
215          siBytesPerRow = [secondImageRep bytesPerRow];
216          siBytesPerPixel = [secondImageRep bitsPerPixel] / 8;
217          siData = [secondImageRep bitmapData];
218          for (y = 0; y < h; y++)
219              {
220                for (x = 0; x < w; x++)
221                  {
222                    if (*(siData + siBytesPerRow*y + siBytesPerPixel*x) == 0)
223                      *(destData + destBytesPerRow*y + destBytesPerPixel*x) -= finalLevelSize;
224                  }
225              }
226        }
227
228      if (enable3)
229        {
230          PRImage   *thirdImage;
231          NSBitmapImageRep  *thirdImageRep;
232          NSInteger tiBytesPerRow;
233          NSInteger tiBytesPerPixel;
234          unsigned char     *tiData;
235
236          if (progPanel != nil)
237            {
238              [self setActivity:@"Processing image 3: median"];
239              [self advanceProgress];
240            }
241          thirdImage  = [medianFilter medianImage :srcImage :form3 :size3 :separable3 :NULL];
242          if (progPanel != nil)
243            {
244              [self setActivity:@"Processing image 3: trace edges"];
245              [self advanceProgress];
246            }
247          thirdImage  = [edgeFilter edgeImage :thirdImage  :filterType :YES :thresholdLevel :useZeroCross];
248          thirdImageRep = [thirdImage bitmapRep];
249          tiBytesPerRow = [thirdImageRep bytesPerRow];
250          tiBytesPerPixel = [thirdImageRep bitsPerPixel] / 8;
251          tiData = [thirdImageRep bitmapData];
252          for (y = 0; y < h; y++)
253            {
254              for (x = 0; x < w; x++)
255                {
256                  if (*(tiData + tiBytesPerRow*y + tiBytesPerPixel*x) == 0)
257                    *(destData + destBytesPerRow*y + destBytesPerPixel*x) -= finalLevelSize;
258                }
259            }
260        }
261    }
262  else
263    {
264      NSBitmapImageRep  *srcImageRep;
265      unsigned char     *srcData;
266      NSInteger         srcBytesPerRow;
267      NSInteger         srcBytesPerPixel;
268
269      /* no median processing */
270      /* we conventionally process the image */
271      if (progPanel != nil)
272        {
273          [self setActivity:@"Processing image: trace edges"];
274          [self advanceProgress];
275        }
276      srcImage  = [edgeFilter edgeImage :srcImage  :filterType :YES :thresholdLevel :useZeroCross];
277      srcImageRep = [srcImage bitmapRep];
278      srcBytesPerRow = [srcImageRep bytesPerRow];
279      srcBytesPerPixel = [srcImageRep bitsPerPixel] / 8;
280      srcData = [srcImageRep bitmapData];
281      for (y = 0; y < h; y++)
282        {
283          for (x = 0; x < w; x++)
284            {
285              if (*(srcData + srcBytesPerRow*y + srcBytesPerPixel*x) == 0)
286                *(destData + destBytesPerRow*y + destBytesPerPixel*x) = 0;
287            }
288        }
289    }
290
291  /* release filters */
292  [medianFilter release];
293  [edgeFilter release];
294
295  if (progPanel != nil)
296    {
297      [self setActivity:@"Done"];
298      [self showProgress];
299    }
300
301  [destImage setBitmapRep:destImageRep];
302  [destImageRep release];
303  [destImage autorelease];
304  return destImage;
305}
306
307@end
308