#include #include #include #include #include #include #include "image_io.h" #include "ShiTomasi.h" #include #include float timedifference_msec(struct timeval t0, struct timeval t1) { return (t1.tv_sec - t0.tv_sec) * 1000.0f + (t1.tv_usec - t0.tv_usec) / 1000.0f; } int main(int argc, char **argv) { // path to the image to process char *filepath = NULL; // sigma of the gaussian distribution float sigma = 1.1; // size of a pixel 'neighborhood' int windowsize = 4; // # of features int max_features = 1024; // argument parsing logic if (argc == 4) { filepath = argv[1]; windowsize = atof(argv[2]); max_features = atoi(argv[3]); } else { help(NULL); } printf("detecting features for %s\n", filepath); printf("sigma = %0.3f, windowsize = %d, max_features = %d\n", sigma, windowsize, max_features); int width; int height; int kernel_width; int a; // calculate kernel width based on sigma a = (int)round(2.5 * sigma - .5); kernel_width = 2 * a + 1; // malloc and read the image to be processed float *original_image; read_imagef(filepath, &original_image, &width, &height); printf("image_size: [%d x %d] px\n", width, height); // malloc and generate the kernels float *gkernel = (float *)malloc(sizeof(float) * kernel_width); float *dkernel = (float *)malloc(sizeof(float) * kernel_width); gen_kernel(gkernel, dkernel, sigma, a, kernel_width); // create hgrad and vgrad and temp float *hgrad = (float *)malloc(sizeof(float) * width * height); float *vgrad = (float *)malloc(sizeof(float) * width * height); float *tmp_image = (float *)malloc(sizeof(float) * width * height); double timer_start_conv = omp_get_wtime(); // convolve to get the vgrad and hgrad convolve(gkernel, original_image, tmp_image, width, height, kernel_width, 1, a); convolve(dkernel, tmp_image, vgrad, width, height, 1, kernel_width, a); convolve(gkernel, original_image, tmp_image, width, height, 1, kernel_width, a); convolve(dkernel, tmp_image, hgrad, width, height, kernel_width, 1, a); double timer_elapsed_conv = omp_get_wtime() - timer_start_conv; free(tmp_image); free(gkernel); free(dkernel); // Compute the eigenvalues of each pixel's z matrix. After this we can free the gradients. data_wrapper_t *eigenvalues = (data_wrapper_t *)malloc(sizeof(data_wrapper_t) * width * height); struct timeval current_time, end_time; double timer_start_eigen = omp_get_wtime(); gettimeofday(¤t_time, NULL); compute_eigenvalues(hgrad, vgrad, height, width, windowsize, eigenvalues); double timer_elapsed_eigen = omp_get_wtime() - timer_start_eigen; gettimeofday(&end_time, NULL); // float elapsed = timedifference_msec(current_time, end_time); float elapsed = (end_time.tv_sec - current_time.tv_sec) * 1000.0f + (end_time.tv_usec - current_time.tv_usec) / 1000.0f; // long double elapsed_double = (float)elapsed / (float)1000000; printf("[gettimeofday] EigenValues Time %f\n", elapsed); // printf("[gettimeofday] EigenValues Time %ld\n", (long)elapsed_double); free(hgrad); free(vgrad); // Find the features based on the eigenvalues. data_wrapper_t *features; unsigned int features_count; features_count = find_features(eigenvalues, max_features, width, height, &features); free(eigenvalues); printf("%d features detected\n", features_count); printf("Convolution Time %f\n", timer_elapsed_conv); printf("EigenValues Time %f\n", timer_elapsed_eigen); // printf("\t"); // print_features(features, features_count); // Mark the features in the output image. draw_features(features, features_count, original_image, width, height); free(features); // Now we write the output. char corner_image[30]; sprintf(corner_image, "output_images/corners.pgm"); write_imagef(corner_image, original_image, width, height); // Free stuff leftover. free(original_image); return 0; } void draw_features(data_wrapper_t *features, unsigned int count, float *image, int image_width, int image_height) { int radius = image_width * 0.0025; for (int i = 0; i < count; ++i) { int x = features[i].x; int y = features[i].y; for (int k = -1 * radius; k <= radius; k++) { for (int m = -1 * radius; m <= radius; m++) { if ((x + k) >= 0 && (x + k) < image_height && (y + m) >= 0 && (y + m) < image_width) image[(x + k) * image_width + (y + m)] = 0; } } } } unsigned int find_features(data_wrapper_t *eigenvalues, int max_features, int image_width, int image_height, data_wrapper_t **features) { size_t image_size = image_height * image_width; // Sort eigenvalues in descending order while keeping their corresponding pixel index in the image. qsort(eigenvalues, image_height * image_width, sizeof *eigenvalues, sort_data_wrapper_value_desc); // Create the features buffer based on the max_features value (acts as a percentage of the image size). *features = (data_wrapper_t *)malloc(sizeof(data_wrapper_t) * max_features); // Fill the features buffer! unsigned int features_count = 0; const int ignore_x = 3; // ignore this many pixels rows from top/bottom of image const int ignore_y = 3; // ignore this many pixels columns from left/right of image for (int i = 0; i < image_size && features_count < max_features; ++i) { // Ignore top left, top right, bottom right, bottom left edges of image. if (eigenvalues[i].x <= ignore_x || eigenvalues[i].y <= ignore_y || eigenvalues[i].x >= image_width - 1 - ignore_x || eigenvalues[i].y >= image_height - 1 - ignore_y) { continue; } // Have to seed the first feature so we have a place to start. if (features_count == 0) { (*features)[0] = eigenvalues[i]; features_count++; } // Check if prospective feature is more than 8 manhattan distance away from any existing feature. int is_good = 1; for (int j = 0; j < features_count; ++j) { int manhattan = abs((*features)[j].x - eigenvalues[i].x) + abs((*features)[j].y - eigenvalues[i].y); if (manhattan <= 8) { is_good = 0; break; } } // If the prospective feature was at least 8 manhattan distance from all existing features, then we can add it. if (is_good) { (*features)[features_count] = eigenvalues[i]; features_count++; } } return features_count; } int sort_data_wrapper_value_desc(const void *a, const void *b) { const data_wrapper_t *aa = (const data_wrapper_t *)a; const data_wrapper_t *bb = (const data_wrapper_t *)b; return (aa->data < bb->data) - (aa->data > bb->data); } int sort_data_wrapper_index_asc(const void *a, const void *b) { const data_wrapper_t *aa = (const data_wrapper_t *)a; const data_wrapper_t *bb = (const data_wrapper_t *)b; if (aa->x == bb->x) return ((aa->y > bb->y) - (aa->y < bb->y)); else return ((aa->x > bb->x) - (aa->x < bb->x)); } void compute_eigenvalues(float *hgrad, float *vgrad, int image_height, int image_width, int windowsize, data_wrapper_t *eigenvalues) { int w = floor(windowsize / 2); int i, j, k, m, offseti, offsetj; float ixx_sum, iyy_sum, ixiy_sum; for (i = 0; i < image_height; i++) { for (j = 0; j < image_width; j++) { ixx_sum = 0; iyy_sum = 0; ixiy_sum = 0; for (k = 0; k < windowsize; k++) { for (m = 0; m < windowsize; m++) { offseti = -1 * w + k; offsetj = -1 * w + m; if (i + offseti >= 0 && i + offseti < image_height && j + offsetj >= 0 && j + offsetj < image_width) { ixx_sum += hgrad[(i + offseti) * image_width + (j + offsetj)] * hgrad[(i + offseti) * image_width + (j + offsetj)]; iyy_sum += vgrad[(i + offseti) * image_width + (j + offsetj)] * vgrad[(i + offseti) * image_width + (j + offsetj)]; ixiy_sum += hgrad[(i + offseti) * image_width + (j + offsetj)] * vgrad[(i + offseti) * image_width + (j + offsetj)]; } } } eigenvalues[i * image_width + j].x = i; eigenvalues[i * image_width + j].y = j; eigenvalues[i * image_width + j].data = min_eigenvalue(ixx_sum, ixiy_sum, ixiy_sum, iyy_sum); } } } float min_eigenvalue(float a, float b, float c, float d) { float ev_one = (a + d) / 2 + pow(((a + d) * (a + d)) / 4 - (a * d - b * c), 0.5); float ev_two = (a + d) / 2 - pow(((a + d) * (a + d)) / 4 - (a * d - b * c), 0.5); if (ev_one >= ev_two) { return ev_two; } else { return ev_one; } } void convolve(float *kernel, float *image, float *resultimage, int image_width, int image_height, int kernel_width, int kernel_height, int half) { float sum; int i, j, k, m, offsetj, offseti; // assign the kernel to the new array for (i = 0; i < image_height; i++) { for (j = 0; j < image_width; j++) { // reset tracker sum = 0.0; // for each item in the kernel for (k = 0; k < kernel_height; k++) { for (m = 0; m < kernel_width; m++) { offseti = -1 * (kernel_height / 2) + k; offsetj = -1 * (kernel_width / 2) + m; if (i + offseti >= 0 && i + offseti < image_height && j + offsetj >= 0 && j + offsetj < image_width) { sum += (float)(image[(i + offseti) * image_width + (j + offsetj)]) * kernel[k * kernel_width + m]; } } } // copy it back resultimage[i * image_width + j] = sum; } } } void gen_kernel(float *gkernel, float *dkernel, float sigma, int a, int w) { int i; float sum_gkern; float sum_dkern; sum_gkern = 0; sum_dkern = 0; for (i = 0; i < w; i++) { gkernel[i] = (float)exp((float)(-1.0 * (i - a) * (i - a)) / (2 * sigma * sigma)); dkernel[i] = (float)(-1 * (i - a)) * (float)exp((float)(-1.0 * (i - a) * (i - a)) / (2 * sigma * sigma)); sum_gkern = sum_gkern + gkernel[i]; sum_dkern = sum_dkern - (float)i * dkernel[i]; } // reverse the kernel by creating a new kernel, yes not ideal float *newkernel = (float *)malloc(sizeof(float) * w); for (i = 0; i < w; i++) { dkernel[i] = dkernel[i] / sum_dkern; gkernel[i] = gkernel[i] / sum_gkern; newkernel[w - i] = dkernel[i]; } // copy new kernel back in for (i = 0; i < w; i++) { dkernel[i] = newkernel[i + 1]; } free(newkernel); } void help(const char *err) { if (err != NULL) printf("%s\n", err); printf("Utilisation: ./ShiTomasi [Taille de la fenêtre] [Nombre de primitives] \n"); printf("arguments:\n"); printf("\tTaille de la fenêtre: taille du voisinage d'un pixel dans l'image\n"); printf("\tNombre de primitives: nombre de primitives à extraire\n"); exit(0); } void print_features(data_wrapper_t *features, unsigned int count) { // Sort the features by qsort(features, count, sizeof *features, sort_data_wrapper_index_asc); for (unsigned int i = 0; i < count; ++i) { if (i % 15 != 0 || i == 0) printf("(%d,%d) ", features[i].x, features[i].y); else printf("(%d,%d)\n\t", features[i].x, features[i].y); } printf("\n"); }