forked from pjreddie/vision-hw4
-
Notifications
You must be signed in to change notification settings - Fork 0
/
harris_image.c
252 lines (228 loc) · 7.33 KB
/
harris_image.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <assert.h>
#include "image.h"
#include "matrix.h"
#include <time.h>
// Frees an array of descriptors.
// descriptor *d: the array.
// int n: number of elements in array.
void free_descriptors(descriptor *d, int n)
{
int i;
for(i = 0; i < n; ++i){
free(d[i].data);
}
free(d);
}
// Create a feature descriptor for an index in an image.
// image im: source image.
// int i: index in image for the pixel we want to describe.
// returns: descriptor for that index.
descriptor describe_index(image im, int i)
{
int w = 5;
descriptor d;
d.p.x = i%im.w;
d.p.y = i/im.w;
d.data = calloc(w*w*im.c, sizeof(float));
d.n = w*w*im.c;
int c, dx, dy;
int count = 0;
// If you want you can experiment with other descriptors
// This subtracts the central value from neighbors
// to compensate some for exposure/lighting changes.
for(c = 0; c < im.c; ++c){
float cval = im.data[c*im.w*im.h + i];
for(dx = -w/2; dx < (w+1)/2; ++dx){
for(dy = -w/2; dy < (w+1)/2; ++dy){
float val = get_pixel(im, i%im.w+dx, i/im.w+dy, c);
d.data[count++] = cval - val;
}
}
}
return d;
}
// Marks the spot of a point in an image.
// image im: image to mark.
// ponit p: spot to mark in the image.
void mark_spot(image im, point p)
{
int x = p.x;
int y = p.y;
int i;
for(i = -9; i < 10; ++i){
set_pixel(im, x+i, y, 0, 1);
set_pixel(im, x, y+i, 0, 1);
set_pixel(im, x+i, y, 1, 0);
set_pixel(im, x, y+i, 1, 0);
set_pixel(im, x+i, y, 2, 1);
set_pixel(im, x, y+i, 2, 1);
}
}
// Marks corners denoted by an array of descriptors.
// image im: image to mark.
// descriptor *d: corners in the image.
// int n: number of descriptors to mark.
void mark_corners(image im, descriptor *d, int n)
{
int i;
for(i = 0; i < n; ++i){
mark_spot(im, d[i].p);
}
}
// Creates a 1d Gaussian filter.
// float sigma: standard deviation of Gaussian.
// returns: single row image of the filter.
image make_1d_gaussian(float sigma)
{
// TODO: optional, make separable 1d Gaussian.
return make_image(1,1,1);
}
// Smooths an image using separable Gaussian filter.
// image im: image to smooth.
// float sigma: std dev. for Gaussian.
// returns: smoothed image.
image smooth_image(image im, float sigma)
{
if(1){
image g = make_gaussian_filter(sigma);
image s = convolve_image(im, g, 1);
free_image(g);
return s;
} else {
// TODO: optional, use two convolutions with 1d gaussian filter.
// If you implement, disable the above if check.
return copy_image(im);
}
}
// Calculate the structure matrix of an image.
// image im: the input image.
// float sigma: std dev. to use for weighted sum.
// returns: structure matrix. 1st channel is Ix^2, 2nd channel is Iy^2,
// third channel is IxIy.
image structure_matrix(image im, float sigma)
{
image S = make_image(im.w, im.h, 3);
image gx = make_gx_filter();
image gy = make_gy_filter();
image Ix = convolve_image(im, gx, 0);
image Iy = convolve_image(im, gy, 0);
for (int i = 0; i < im.w; i++) {
for (int j = 0; j < im.h; j++) {
float Ix_val = get_pixel(Ix, i, j, 0);
float Iy_val = get_pixel(Iy, i, j, 0);
set_pixel(S, i, j, 0, Ix_val*Ix_val);
set_pixel(S, i, j, 1, Iy_val*Iy_val);
set_pixel(S, i, j, 2, Ix_val*Iy_val);
}
}
image result = smooth_image(S, sigma);
free_image(S);
free_image(gx);
free_image(gy);
free_image(Ix);
free_image(Iy);
return result;
}
// Estimate the cornerness of each pixel given a structure matrix S.
// image S: structure matrix for an image.
// returns: a response map of cornerness calculations.
image cornerness_response(image S)
{
image R = make_image(S.w, S.h, 1);
// TODO: fill in R, "cornerness" for each pixel using the structure matrix.
// We'll use formulation det(S) - alpha * trace(S)^2, alpha = .06.
for (int i = 0; i < S.w; i++) {
for (int j = 0; j < S.h; j++) {
float a = get_pixel(S, i, j, 0);
float b = get_pixel(S, i, j, 2);
float c = get_pixel(S, i, j, 2);
float d = get_pixel(S, i, j, 1);
set_pixel(R, i, j, 0, a*d-b*c - .06*(a+d)*(a+d));
}
}
return R;
}
// Perform non-max supression on an image of feature responses.
// image im: 1-channel image of feature responses.
// int w: distance to look for larger responses.
// returns: image with only local-maxima responses within w pixels.
image nms_image(image im, int w)
{
image r = copy_image(im);
// TODO: perform NMS on the response map.
// for every pixel in the image:
// for neighbors within w:
// if neighbor response greater than pixel response:
// set response to be very low (I use -999999 [why not 0??])
for (int i = 0; i < im.w; i++) {
for (int j = 0; j < im.h; j++) {
float val = get_pixel(im, i, j, 0);
for (int k = 0; k < 2*w+1; k++) {
for (int l = 0; l < 2*w+1; l++) {
float neighbor = get_pixel(im, i+k-w, j+l-w, 0);
if (neighbor > val) {
set_pixel(r, i, j, 0, -999999);
k = 2*w+1;
break;
}
}
}
}
}
return r;
}
// Perform harris corner detection and extract features from the corners.
// image im: input image.
// float sigma: std. dev for harris.
// float thresh: threshold for cornerness.
// int nms: distance to look for local-maxes in response map.
// int *n: pointer to number of corners detected, should fill in.
// returns: array of descriptors of the corners in the image.
descriptor *harris_corner_detector(image im, float sigma, float thresh, int nms, int *n)
{
// Calculate structure matrix
image S = structure_matrix(im, sigma);
// Estimate cornerness
image R = cornerness_response(S);
// Run NMS on the responses
image Rnms = nms_image(R, nms);
//TODO: count number of responses over threshold
int count = 0; // change this
for (int i = 0; i < Rnms.w; i++) {
for (int j = 0; j < Rnms.h; j++) {
if (get_pixel(Rnms, i, j, 0) > thresh) {
count++;
}
}
}
*n = count; // <- set *n equal to number of corners in image.
descriptor *d = calloc(count, sizeof(descriptor));
//TODO: fill in array *d with descriptors of corners, use describe_index.
count = 0;
for (int i = 0; i < Rnms.w; i++) {
for (int j = 0; j < Rnms.h; j++) {
if (get_pixel(Rnms, i, j, 0) > thresh) {
d[count++] = describe_index(im, j*Rnms.w+i);
}
}
}
free_image(S);
free_image(R);
free_image(Rnms);
return d;
}
// Find and draw corners on an image.
// image im: input image.
// float sigma: std. dev for harris.
// float thresh: threshold for cornerness.
// int nms: distance to look for local-maxes in response map.
void detect_and_draw_corners(image im, float sigma, float thresh, int nms)
{
int n = 0;
descriptor *d = harris_corner_detector(im, sigma, thresh, nms, &n);
mark_corners(im, d, n);
}