1//
2//  PRTraceEdges.m
3//  PRICE
4//
5//  Created by Riccardo Mottola on Wed Jan 14 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
12#import "PRTraceEdges.h"
13#import "PRGrayscaleFilter.h"
14
15#include <math.h>
16#include <limits.h>
17
18
19@implementation PRTraceEdges
20
21- (PRImage *)filterImage:(PRImage *)image with:(NSArray *)parameters progressPanel:(PRCProgress *)progressPanel
22{
23    int filterType;
24    BOOL useThreshold;
25    float thresLevel;
26    BOOL useZeroCross;
27
28    /* interpret the parameters */
29    filterType = [[parameters objectAtIndex:0] intValue];
30    useThreshold = [[parameters objectAtIndex:1] boolValue];
31    thresLevel = [[parameters objectAtIndex:2] floatValue];
32    useZeroCross = [[parameters objectAtIndex:3] boolValue];
33
34    return [self edgeImage:image :filterType :useThreshold :thresLevel :useZeroCross];
35}
36
37- (NSString *)actionName
38{
39    return @"Trace Edges";
40}
41
42
43- (PRImage *)edgeImage :(PRImage *)srcImage :(int)filterType :(BOOL)useThreshold :(float)thresholdLevel :(BOOL)useZeroCross
44{
45  NSBitmapImageRep   *srcImageRep;
46  PRImage            *destImage;
47  NSBitmapImageRep   *destImageRep;
48  NSInteger          w, h;
49  NSInteger          x, y;
50  NSInteger          i, j;
51  unsigned char      *srcData;
52  unsigned char      *destData;
53  NSInteger          destSamplesPerPixel;
54  register NSInteger srcBytesPerPixel;
55  register NSInteger destBytesPerPixel;
56  register NSInteger srcBytesPerRow;
57  register NSInteger destBytesPerRow;
58  float              *M, *N;
59  unsigned char      *p1;
60  int                convMatrix[5][5];
61  float              convSum;
62
63  /* get source image representation and associated information */
64  srcImageRep = [srcImage bitmapRep];
65
66  w = [srcImageRep pixelsWide];
67  h = [srcImageRep pixelsHigh];
68
69  /* check bith depth and color/greyscale image */
70  if ([srcImage hasColor])
71    {
72      PRGrayscaleFilter *grayFilter;
73
74      grayFilter = [[PRGrayscaleFilter alloc] init];
75      /* Luminance conversion leaves us more signal than average */
76      srcImage = [grayFilter filterImage:srcImage :METHOD_LUMINANCE];
77      [grayFilter release];
78      srcImageRep = [srcImage bitmapRep];
79      NSLog (@"done greyscale converting");
80    }
81
82  srcBytesPerRow = [srcImageRep bytesPerRow];
83  srcBytesPerPixel = [srcImageRep bitsPerPixel] / 8;
84  destSamplesPerPixel = 1;
85
86    srcData = [srcImageRep bitmapData];
87
88
89    /* allocate float data */
90    M = (float *)calloc(h*w, sizeof(float));
91    N = (float *)calloc(h*w, sizeof(float));
92
93    /* copy the image to the float matrix */
94    for (y = 0; y < h; y++)
95    {
96        p1 = srcData +  y * srcBytesPerRow;
97        for (x = 0; x < w; x++)
98        {
99
100            M[y*w + x] = (double)p1[x*srcBytesPerPixel] / UCHAR_MAX;
101        }
102    }
103
104
105    /* set the filter matrix */
106    for (i = 0; i < 5; i++)
107        for (j = 0; j < 5; j++)
108            convMatrix[i][j] = 0;
109
110    /* see PRCTraceEdge.h for the filter correspondences */
111    switch (filterType)
112    {
113    case 1: NSLog(@"Pixel difference");
114        convMatrix[1][1] = 0;
115        convMatrix[1][2] = -1;
116        convMatrix[1][3] = 0;
117        convMatrix[2][1] = 0;
118        convMatrix[2][2] = 2;
119        convMatrix[2][3] = -1;
120        break;
121    case 2: NSLog(@"Separated pixel difference");
122        convMatrix[1][1] = 0;
123        convMatrix[1][2] = -1;
124        convMatrix[1][3] = 0;
125        convMatrix[2][1] = 1;
126        convMatrix[2][2] = 0;
127        convMatrix[2][3] = -1;
128        convMatrix[3][1] = 0;
129        convMatrix[3][2] = 1;
130        convMatrix[3][3] = 0;
131        break;
132    case 3: NSLog(@"Roberts");
133        convMatrix[1][1] = -1;
134        convMatrix[1][2] = 0;
135        convMatrix[1][3] = -1;
136        convMatrix[2][1] = 0;
137        convMatrix[2][2] = 2;
138        convMatrix[2][3] = 0;
139        break;
140    case 4: NSLog(@"Prewitt");
141        convMatrix[1][1] = 0;
142        convMatrix[1][2] = -1;
143        convMatrix[1][3] = -2;
144        convMatrix[2][1] = 1;
145        convMatrix[2][2] = 0;
146        convMatrix[2][3] = -1;
147        convMatrix[3][1] = 2;
148        convMatrix[3][2] = 1;
149        convMatrix[3][3] = 0;
150        break;
151    case 5: NSLog(@"Sobel");
152        convMatrix[1][1] = 0;
153        convMatrix[1][2] = -2;
154        convMatrix[1][3] = -2;
155        convMatrix[2][1] = 2;
156        convMatrix[2][2] = 0;
157        convMatrix[2][3] = -2;
158        convMatrix[3][1] = 2;
159        convMatrix[3][2] = 2;
160        convMatrix[3][3] = 0;
161        break;
162    case 6: NSLog(@"Abdou x");
163        convMatrix[0][0] = 1;
164        convMatrix[0][1] = 1;
165        convMatrix[0][2] = 0;
166        convMatrix[0][3] = -1;
167        convMatrix[0][4] = -1;
168        convMatrix[1][0] = 1;
169        convMatrix[1][1] = 2;
170        convMatrix[1][2] = 0;
171        convMatrix[1][3] = -2;
172        convMatrix[1][4] = -1;
173        convMatrix[2][0] = 1;
174        convMatrix[2][1] = 2;
175        convMatrix[2][2] = 0;
176        convMatrix[2][3] = -2;
177        convMatrix[2][4] = -1;
178        convMatrix[3][0] = 1;
179        convMatrix[3][1] = 2;
180        convMatrix[3][2] = 0;
181        convMatrix[3][3] = -2;
182        convMatrix[3][4] = -1;
183        convMatrix[4][0] = 1;
184        convMatrix[4][1] = 1;
185        convMatrix[4][2] = 0;
186        convMatrix[4][3] = -1;
187        convMatrix[4][4] = -1;
188        break;
189    case 7: NSLog(@"Laplacian 1");
190        convMatrix[1][1] = 0;
191        convMatrix[1][2] = -1;
192        convMatrix[1][3] = 0;
193        convMatrix[2][1] = -1;
194        convMatrix[2][2] = 4;
195        convMatrix[2][3] = -1;
196        convMatrix[3][1] = 0;
197        convMatrix[3][2] = -1;
198        convMatrix[3][3] = 0;
199        break;
200    case 8: NSLog(@"Laplacian 2");
201        convMatrix[1][1] = -1;
202        convMatrix[1][2] = 2;
203        convMatrix[1][3] = -1;
204        convMatrix[2][1] = 2;
205        convMatrix[2][2] = -4;
206        convMatrix[2][3] = 2;
207        convMatrix[3][1] = -1;
208        convMatrix[3][2] = 2;
209        convMatrix[3][3] = -1;
210        break;
211    case 9: NSLog(@"Laplacian Prewitt");
212        convMatrix[1][1] = -1;
213        convMatrix[1][2] = -1;
214        convMatrix[1][3] = -1;
215        convMatrix[2][1] = -1;
216        convMatrix[2][2] = 8;
217        convMatrix[2][3] = -1;
218        convMatrix[3][1] = -1;
219        convMatrix[3][2] = -1;
220        convMatrix[3][3] = -1;
221        break;
222    default:
223        NSLog(@"Unknown filter type for Trace Edges.");
224    }
225
226
227    /* the core */
228    for (y = 0 + 2; y < h - 3; y++)
229        for (x = 0 + 2; x < w - 3; x++)
230        {
231            convSum = 0;
232            for (i = -2; i <= 2; i++)
233                for (j = -2; j <= 2; j++)
234                    convSum += convMatrix[i+2][j+2] * M[(y+i) * w + (x+j)];
235            N[y*w + x] = convSum;
236        }
237
238    /* allocate destination image and its representation */
239    destImage = [[PRImage alloc] initWithSize:NSMakeSize(w, h)];
240    destImageRep = [[NSBitmapImageRep alloc]
241                    initWithBitmapDataPlanes:NULL
242                    pixelsWide:w
243                    pixelsHigh:h
244                    bitsPerSample:8
245                    samplesPerPixel:destSamplesPerPixel
246                    hasAlpha:NO
247                    isPlanar:NO
248                    colorSpaceName:NSCalibratedWhiteColorSpace
249                    bytesPerRow:0
250                    bitsPerPixel:0];
251
252    destData = [destImageRep bitmapData];
253    destBytesPerRow = [destImageRep bytesPerRow];
254    destBytesPerPixel = [destImageRep bitsPerPixel] / 8;
255
256    /* copy the image back from the float matrix */
257    if (useThreshold)
258    {
259        if (useZeroCross)
260        {   /* zero crossing */
261            for (y = 0; y < h; y++)
262            {
263                p1 = destData +  (y * destBytesPerRow);
264                for (x = 0; x < w; x++)
265                {
266                    float temp;
267                    temp = fabs(N[y*w + x]);
268                    if (temp < thresholdLevel)
269                        p1[x*destBytesPerPixel] = UCHAR_MAX;
270                    else
271                        p1[x*destBytesPerPixel] = 0;
272                }
273            }
274        } else
275        {   /* no zero crossing */
276            for (y = 0; y < h; y++)
277            {
278                p1 = destData +  (y * destBytesPerRow);
279                for (x = 0; x < w; x++)
280                {
281                    if (N[y*w + x] > thresholdLevel)
282                        p1[x*destBytesPerPixel] = 0;
283                    else
284                        p1[x*destBytesPerPixel] = UCHAR_MAX;
285                }
286            }
287        }
288    } else
289    { /* thresholding to prevent clipping */
290        for (y = 0; y < h; y++)
291        {
292            p1 = destData +  (y * destBytesPerRow);
293            for (x = 0; x < w; x++)
294            {
295                float temp;
296                temp = fabs(N[y*w + x]);
297                if (temp < 0)
298                    p1[x*destBytesPerPixel] = 0;
299                else if (temp < 1)
300                    p1[x*destBytesPerPixel] = (unsigned char) rint(temp * UCHAR_MAX);
301                else
302                    p1[x*destBytesPerPixel] = UCHAR_MAX;
303            }
304        }
305    }
306
307    /* let's free the float data */
308    free(M);
309    free(N);
310
311    [destImage setBitmapRep:destImageRep];
312    [destImageRep release];
313    [destImage autorelease];
314    return destImage;
315}
316
317@end
318