Skip to content

Commit 2f1ef2b

Browse files
feat(filter): add edge detection (-D flag) using Sobel operator
1 parent 80ab27b commit 2f1ef2b

File tree

4 files changed

+153
-22
lines changed

4 files changed

+153
-22
lines changed

README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,43 @@ Otherwise, it is set to **black** (`R = G = B = 0`).
144144
This results in a high-contrast, two-tone image where all intermediate shades are eliminated — essentially a hard binary “black-and-white” conversion.
145145

146146

147+
### 7.) The Edge Detection (Sobel) Algorithm
148+
149+
The “edge detection” filter highlights sharp changes in pixel intensity, producing a sketch-like outline of the image.
150+
151+
For each pixel, the horizontal and vertical gradients are calculated using a 3×3 Sobel kernel applied to the surrounding pixels:
152+
153+
Horizontal (Gx):
154+
-1 0 1
155+
-2 0 2
156+
-1 0 1
157+
158+
Vertical (Gy):
159+
-1 -2 -1
160+
0 0 0
161+
1 2 1
162+
163+
The gradient magnitude for each color channel is then computed as:
164+
165+
value
166+
=
167+
𝐺
168+
𝑥
169+
2
170+
+
171+
𝐺
172+
𝑦
173+
2
174+
value=
175+
Gx
176+
2
177+
+Gy
178+
2
179+
180+
181+
182+
The result is clamped between 0 and 255 and replaces the original pixel value. This produces a monochrome image where edges are highlighted, giving a pencil-sketch effect.
183+
147184
---
148185

149186
### Usage

filter.c

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,27 @@
77
int main(int argc, char *argv[])
88
{
99
// Define allowable filters
10-
char *filters = "bgrsivt";
10+
char *filters = "bgrsivtd";
11+
12+
1113

1214

13-
char filterArr[argc-3];
14-
15-
// gets all filter flags and checks validity
16-
for(int i=0; i<argc; i++){
17-
char temp = getopt(argc,argv,filters);
18-
if(temp == -1) break;
19-
filterArr[i]= temp;
20-
if(filterArr[i] == '?') {
21-
printf("Invalid filter option");
22-
return 1;
23-
}
24-
}
25-
15+
char filterArr[argc - 3];
16+
int filterCount = 0;
2617

27-
// Ensure proper usage
28-
if (argc < optind + 2)
18+
while ((opt = getopt(argc, argv, filters)) != -1)
19+
{
20+
if (opt == '?')
2921
{
30-
printf("Usage: ./filter [flag] infile outfile\n");
31-
return 3;
22+
printf("Invalid filter option\n");
23+
return 2;
3224
}
25+
filterArr[filterCount++] = (char)opt;
26+
}
27+
28+
29+
30+
3331

3432
// Remember filenames
3533
char *infile = argv[optind];
@@ -131,10 +129,18 @@ int main(int argc, char *argv[])
131129
case 't':
132130
threshold(height, width, image);
133131
break;
134-
default:
135-
printf("%c", &filterArr[i]);
136-
break;
137-
132+
133+
case 'd': // Edge Detection
134+
detect_edges(height, width, image);
135+
break;
136+
137+
default:
138+
printf("Unknown filter: %c\n", filterArr[i]);
139+
free(image);
140+
fclose(inptr);
141+
fclose(outptr);
142+
return 7;
143+
138144
}
139145
}
140146
// Write outfile's BITMAPFILEHEADER

helpers.c

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#include "helpers.h"
22
#include <stdint.h>
33
#include <stdlib.h>
4+
#include <math.h>
5+
#include <stdio.h>
46
#include "bmp.h"
57
int min(int a,int b){
68
if(a<b) return a;
@@ -198,3 +200,78 @@ void threshold(int height, int width, RGBTRIPLE image[height][width])
198200
}
199201
}
200202
}
203+
204+
205+
// ----------------------
206+
// EDGE DETECTION (SOBEL)
207+
// ----------------------
208+
void detect_edges(int height, int width, RGBTRIPLE image[height][width])
209+
{
210+
// Temporary copy of the image
211+
RGBTRIPLE **copy = malloc(height * sizeof(RGBTRIPLE *));
212+
for (int i = 0; i < height; i++)
213+
copy[i] = malloc(width * sizeof(RGBTRIPLE));
214+
215+
for (int i = 0; i < height; i++)
216+
for (int j = 0; j < width; j++)
217+
copy[i][j] = image[i][j];
218+
219+
// Sobel kernels
220+
int Gx[3][3] = {
221+
{-1, 0, 1},
222+
{-2, 0, 2},
223+
{-1, 0, 1}
224+
};
225+
int Gy[3][3] = {
226+
{-1, -2, -1},
227+
{ 0, 0, 0},
228+
{ 1, 2, 1}
229+
};
230+
231+
for (int i = 0; i < height; i++)
232+
{
233+
for (int j = 0; j < width; j++)
234+
{
235+
int sumRx = 0, sumGx = 0, sumBx = 0;
236+
int sumRy = 0, sumGy = 0, sumBy = 0;
237+
238+
for (int di = -1; di <= 1; di++)
239+
{
240+
for (int dj = -1; dj <= 1; dj++)
241+
{
242+
int ni = i + di;
243+
int nj = j + dj;
244+
245+
if (ni >= 0 && ni < height && nj >= 0 && nj < width)
246+
{
247+
RGBTRIPLE pixel = copy[ni][nj];
248+
int kx = Gx[di + 1][dj + 1];
249+
int ky = Gy[di + 1][dj + 1];
250+
251+
sumRx += pixel.rgbtRed * kx;
252+
sumGx += pixel.rgbtGreen * kx;
253+
sumBx += pixel.rgbtBlue * kx;
254+
255+
sumRy += pixel.rgbtRed * ky;
256+
sumGy += pixel.rgbtGreen * ky;
257+
sumBy += pixel.rgbtBlue * ky;
258+
}
259+
}
260+
}
261+
262+
// Calculate gradient magnitude and clamp
263+
int red = min(max((int)round(sqrt(sumRx*sumRx + sumRy*sumRy)), 0), 255);
264+
int green = min(max((int)round(sqrt(sumGx*sumGx + sumGy*sumGy)), 0), 255);
265+
int blue = min(max((int)round(sqrt(sumBx*sumBx + sumBy*sumBy)), 0), 255);
266+
267+
image[i][j].rgbtRed = red;
268+
image[i][j].rgbtGreen = green;
269+
image[i][j].rgbtBlue = blue;
270+
}
271+
}
272+
273+
// Free temporary array
274+
for (int i = 0; i < height; i++)
275+
free(copy[i]);
276+
free(copy);
277+
}

helpers.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
#ifndef HELPERS_H
2+
#define HELPERS_H
3+
4+
#include <stdio.h>
5+
#include <math.h>
6+
17
#include "bmp.h"
28

39
// Convert image to grayscale
@@ -19,3 +25,8 @@ void vignette(int height, int width, RGBTRIPLE image[height][width]);
1925

2026
//Threshold Filter(Black & White)
2127
void threshold(int height, int width, RGBTRIPLE image[height][width]);
28+
29+
// **New: Edge Detection filter**
30+
void detect_edges(int height, int width, RGBTRIPLE image[height][width]);
31+
32+
#endif

0 commit comments

Comments
 (0)