Final demo of doing Gauge Needle Detection on F429.
Dependencies: BSP_DISCO_F429ZI SDFileSystem SDRAM_DISCO_F429ZI mbed
Fork of DISCO-F429ZI_SDRAM_demo by
main.cpp
- Committer:
- wsquan171
- Date:
- 2016-05-03
- Revision:
- 1:8b8a77b7d715
- Parent:
- 0:d3bf1776d1c6
File content as of revision 1:8b8a77b7d715:
#include "mbed.h" #include "SDRAM_DISCO_F429ZI.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include "SDFileSystem.h" #define M_PI 3.14159265358979323846 SDRAM_DISCO_F429ZI sdram; SDFileSystem sd(PC_12, PC_11, PC_10, PB_2, "sd");// MOSI, MISO, SCK, CS DigitalOut led_green(LED1); DigitalOut led_red(LED2); SPI spi(PC_12, PC_11, PC_10); // mosi, miso, sclk DigitalOut cs(PB_2); Serial pc(USBTX, USBRX); #define line_limit 10 // limit on # of lines to be returned by HoughLines typedef struct { int x; int y; } ptvec; typedef struct { float rho, theta; double a, b; double x0, y0; ptvec ptvec1, ptvec2; unsigned int strength; } trans_line; typedef struct { unsigned int rows; unsigned int cols; unsigned int channels; uint32_t data; } Image; typedef struct { int rho; int theta; unsigned int strength; } line_polar; int bmpread(char *filename, Image *dst) { pc.printf("Reading Image file: %s\r\n", filename); FILE *file; unsigned char tempSize4[4]; unsigned int width, height; unsigned short planes, bpp; if ((file = fopen(filename, "rb"))==NULL) { pc.printf("File Not Found : %s\r\n", filename); return 0; } // seek through the bmp header, up to the width/height: fseek(file, 18, SEEK_CUR); if ( fread( tempSize4, 1, 4, file) < 4 ) return 0; width = (tempSize4[3]<<24) | (tempSize4[2]<<16) | (tempSize4[1]<<8) | tempSize4[0]; // big endian if ( fread( tempSize4, 1, 4, file) < 4 ) return 0; height = (tempSize4[3]<<24) | (tempSize4[2]<<16) | (tempSize4[1]<<8) | tempSize4[0];; pc.printf("width: %d, height: %d\r\n", width, height); dst->rows = height; dst->cols = width; if ( fread( tempSize4, 1, 2, file) < 2 ) return 0; planes = (tempSize4[1]<<8) | tempSize4[0]; // big endian dst->channels = planes; if ( fread( tempSize4, 1, 2, file) < 2 ) return 0; bpp = (tempSize4[1]<<8) | tempSize4[0]; // big endian pc.printf("planes: %d, bpp: %d\r\n", planes, bpp); fseek(file, 24, SEEK_CUR); unsigned char *readline; unsigned int padding=0; while ((width*3+padding) % 4!=0) padding++; unsigned int linewidth = width * 3 + padding; readline = (unsigned char*)malloc(linewidth * sizeof(unsigned char)); unsigned char R, G, B; int i, j; dst->data = 1; unsigned char *dst_buf= (unsigned char *) malloc(width * sizeof(unsigned char)); uint32_t buffer[width/4]; pc.printf("Converting to grayscale and Storing to SDRAM\r\n"); for(i = height-1; i >= 0; i--) { fread(readline, sizeof(unsigned char), linewidth, file); for(j = 0; j < width; j++) { B = readline[3*j]; G = readline[3*j+1]; R = readline[3*j+2]; dst_buf[j] = (0.299*R) + (0.587*G) + (0.114*B); } for(j = 0; j < width/4; j++) { buffer[j] = (16777216*dst_buf[j*4]) + (65536*dst_buf[j*4+1]) + (dst_buf[j*4+2]*256) + dst_buf[j*4+3]; } sdram.WriteData(SDRAM_DEVICE_ADDR + dst->data + (i*width), buffer, width/4); j = height / 10; if(i%j == 0) pc.printf("%d%%...",(100 - (i/j*10))); } pc.printf("\r\nImage loaded\r\n"); free(readline); free(dst_buf); fclose(file); return 1; } int Round(double number) { double diff_down, diff_up; diff_down = number - (int)number; diff_up = 1 - diff_down; if(diff_down < diff_up) return (int)number; else return 1+(int)number; } void matrix_convolution(Image *src, Image *dst, double **mask, int width) { unsigned char lines[width][src->cols]; unsigned char result[src->cols]; uint32_t buffer[src->cols/4]; int i, j, k, r, c, actual_r, actual_c, scope; double gaussian_sum; //int flag = 1; scope = width/2; for(i = 0; i < src->rows; i++) { if(i < scope || (src->rows - i <= scope)) { sdram.ReadData(i*(src->cols) + SDRAM_DEVICE_ADDR + (src->data), buffer, src->cols/4); sdram.WriteData(i*(src->cols) + SDRAM_DEVICE_ADDR + (dst->data), buffer, src->cols/4); continue; } for(j = 0; j < width; j++) { sdram.ReadData((i-scope+j)*(src->cols) + SDRAM_DEVICE_ADDR + (src->data), buffer, src->cols/4); for(k = 0; k < src->cols/4; k++) { lines[j][k*4+3] = buffer[k]; lines[j][k*4+2] = buffer[k] >> 8; lines[j][k*4+1] = buffer[k] >> 16; lines[j][k*4] = buffer[k] >> 24; } } //pc.printf("line %d, data = %d\r\n", i, (int)lines[scope][0]); for(j = 0; j < src->cols; j++) { if( (j < scope) || (src->cols - j <= scope)) { result[j] = lines[scope][j]; continue; } gaussian_sum = 0; for(r = 0; r < width; r++) { for(c = 0; c < width; c++) { actual_r = r; actual_c = j + c - scope; /* if(flag == 1) { pc.printf("%d, %d mask = %.4f, value = %d\r\n", r,c,mask[r][c],(int)lines[actual_r][actual_c]); } */ double op1 = mask[r][c]; double op2 = (double) lines[actual_r][actual_c]; double temp = op1 * op2; gaussian_sum = gaussian_sum + temp; } } //flag = 0; if(gaussian_sum > 255) gaussian_sum = 255; if(gaussian_sum < 0) gaussian_sum = 0; result[j] = (unsigned char)Round(gaussian_sum); //pc.printf("%d ", result[j]); } //pc.printf("\r\n"); for(j = 0; j < src->cols/4; j++) { buffer[j] = (16777216*result[j*4]) + (65536*result[j*4+1]) + (result[j*4+2]*256) + result[j*4+3]; } sdram.WriteData(SDRAM_DEVICE_ADDR + dst->data + (i*src->cols), buffer, src->cols/4); j = src->rows / 10; if(i%j == 0) pc.printf("%d%%...",i/j*10); } pc.printf("\r\n"); return; } void myGaussianBlur(Image *src, Image *dst, int width, double sigma) { // build the Gaussian Kernal // ref: http://stackoverflow.com/questions/8204645/implementing-gaussian-blur-how-to-calculate-convolution-matrix-kernel pc.printf("\r\nGaussian Blur Module Invoked\r\n"); dst->rows = src->rows; dst->cols = src->cols; dst->channels = src->channels; dst->data = src->data + (src->rows * src->cols); int W = width; int i, x, y; double **kernel; kernel = (double **) malloc(W * sizeof(double *)); for(i = 0; i < W; i++) kernel[i] = (double *) malloc(W * sizeof(double)); double mean = W/2; double sum = 0.0; // For accumulating the kernel values for (x = 0; x < W; ++x) { for (y = 0; y < W; ++y) { kernel[x][y] = exp( -0.5 * (pow((x-mean)/sigma, 2.0) + pow((y-mean)/sigma,2.0)) ) / (2 * M_PI * sigma * sigma); // Accumulate the kernel values sum += kernel[x][y]; } } // Normalize the kernel //pc.printf("Gaussian Kernal in use:\r\n"); for (x = 0; x < W; ++x) { for (y = 0; y < W; ++y) { kernel[x][y] /= sum; //pc.printf("%.7f ", kernel[x][y]); } //pc.printf("\r\n"); } // do matrix convolution matrix_convolution(src, dst, kernel, width); for(i = 0; i < W; i++) free(kernel[i]); free(kernel); return; } void myCanny(Image *src, Image *dst, double min_threshold, double max_threshold) { // ref: // http://dasl.mem.drexel.edu/alumni/bGreen/www.pages.drexel.edu/_weg22/can_tut.html // https://en.wikipedia.org/wiki/Canny_edge_detector pc.printf("\r\nCanny Edge Detection Module Invoked\r\n"); dst->rows = src->rows; dst->cols = src->cols; dst->channels = src->channels; dst->data = src->data + (src->rows * src->cols); // de-noise using local Gaussian Blur, simulating size = 5, sigma = 1.4 pc.printf("Smoothing image...\r\n"); double mask[5][5] = { {2, 4, 5, 4, 2}, {4, 9, 12, 9, 4}, {5, 12, 15, 12, 5}, {4, 9, 12, 9, 4}, {2, 4, 5, 4, 2} }; double **kernel; int i, j; kernel = (double **) malloc(5 * sizeof(double *)); for(i = 0; i < 5; i++) kernel[i] = (double *) malloc(5 * sizeof(double)); for(i = 0; i < 5; i++) { for(j = 0; j < 5; j++) { kernel[i][j] = mask[i][j] / 159; //printf("%.7f ", kernel[i][j]); } //printf("\n"); } matrix_convolution(src, dst, kernel, 5); for(i = 0; i < 5; i++) free(kernel[i]); free(kernel); // find gradient x and gradient y pc.printf("Finding gradient in x and y for each pixel...\r\n"); Image Gx, Gy; // Gx and Gy are used to store x-gradient and y-gradient Gx.cols = src->cols; Gx.rows = src->rows; Gx.data = dst->data + (dst->cols * dst->rows); Gy.cols = src->cols; Gy.rows = src->rows; Gy.data = Gx.data + (dst->cols * dst->rows); // size-3 Sobel mask double sobel_x[3][3] = {{-1, 0, 1}, {-2, 0, 2}, {-1, 0, 1}}; double sobel_y[3][3] = {{-1, -2, -1}, {0, 0, 0}, {1, 2, 1}}; double **mask_x, **mask_y; mask_x = (double **) malloc(3 * sizeof(double *)); for(i = 0; i < 3; i++) mask_x[i] = (double *) malloc(3 * sizeof(double)); mask_y = (double **) malloc(3 * sizeof(double *)); for(i = 0; i < 3; i++) mask_y[i] = (double *) malloc(3 * sizeof(double)); for(i = 0; i < 3; i++) { for(j = 0; j < 3; j++) { mask_x[i][j] = sobel_x[i][j]; mask_y[i][j] = sobel_y[i][j]; } } pc.printf("in x: "); matrix_convolution(src, &Gx, mask_x, 3); pc.printf("in y: "); matrix_convolution(src, &Gy, mask_y, 3); for(i = 0; i < 3; i++) { free(mask_x[i]); free(mask_y[i]); } free(mask_x); free(mask_y); //pc.printf("dst at %d, Gx at %d, Gy at %d\r\n", dst->data, Gx.data, Gy.data); // calculate gradient strength (store in Gx) and direction (store in Gy) pc.printf("Calculating gradient strength and direction...\r\n"); double gx_val, gy_val; double arctan_val; int over_threshold_count = 0; // number used for adaptive non-max suppression uint32_t buffer[src->cols/4]; unsigned char Gx_buf[src->cols], Gy_buf[src->cols]; //pc.printf("%d %d %d %d %d %d", src->rows, src->cols, Gx.cols, Gx.rows, Gy.cols, Gy.rows); for(i = 0; i < src->rows; i++) { sdram.ReadData(i*(Gx.cols) + SDRAM_DEVICE_ADDR + (Gx.data), buffer, Gx.cols/4); for(j = 0; j < Gx.cols/4; j++) { Gx_buf[j*4+3] = buffer[j]; Gx_buf[j*4+2] = buffer[j] >> 8; Gx_buf[j*4+1] = buffer[j] >> 16; Gx_buf[j*4] = buffer[j] >> 24; } sdram.ReadData(i*(Gy.cols) + SDRAM_DEVICE_ADDR + (Gy.data), buffer, Gy.cols/4); for(j = 0; j < Gy.cols/4; j++) { Gy_buf[j*4+3] = buffer[j]; Gy_buf[j*4+2] = buffer[j] >> 8; Gy_buf[j*4+1] = buffer[j] >> 16; Gy_buf[j*4] = buffer[j] >> 24; } for(j = 0; j < src->cols; j++) { gx_val = Gx_buf[j]; gy_val = Gy_buf[j]; Gx_buf[j] = (unsigned char)sqrt(pow(gx_val, 2) + pow(gy_val, 2)); if(Gx_buf[j] > max_threshold) over_threshold_count++; arctan_val = atan(gy_val / gx_val); if(arctan_val >= 0.41421 && arctan_val < 2.414421) // 22.5 to 67.5 deg Gy_buf[j] = 45; else if(arctan_val >= 2.41421 || arctan_val < -2.41421) // 67.5 to 112.5 deg Gy_buf[j] = 90; else if(arctan_val >= -2.41421 && arctan_val < 0.41421) // 112.5 to 157.5 deg Gy_buf[j] = 135; else // 0 to 22.5 and 157.5 to 180 deg Gy_buf[j] = 0; } for(j = 0; j < Gx.cols/4; j++) { buffer[j] = (16777216*Gx_buf[j*4]) + (65536*Gx_buf[j*4+1]) + (Gx_buf[j*4+2]*256) + Gx_buf[j*4+3]; } sdram.WriteData(SDRAM_DEVICE_ADDR + Gx.data + (i*Gx.cols), buffer, Gx.cols/4); for(j = 0; j < Gy.cols/4; j++) { buffer[j] = (16777216*Gy_buf[j*4]) + (65536*Gy_buf[j*4+1]) + (Gy_buf[j*4+2]*256) + Gy_buf[j*4+3]; } sdram.WriteData(SDRAM_DEVICE_ADDR + Gy.data + (i*Gy.cols), buffer, Gy.cols/4); } // Non-maximum suppression. Aim is to shrink the edge width pc.printf("Non-maximum suppression...\r\n"); int mask_radius = 1; // hard-coded parameter int r, c, not_max_flag, vertical_neighbor; Image sup_matrix; // maxtrix used to keep track of local maxima stutas for each pixel sup_matrix.cols = dst->cols; sup_matrix.rows = dst->rows; sup_matrix.data = Gy.data + (dst->cols * dst->rows); double skip_sup_ratio = (double)over_threshold_count/8800; // 10000 pixels should be found in a 800x600 pic if(skip_sup_ratio < 1) skip_sup_ratio = 1; else if(skip_sup_ratio > 1.8) skip_sup_ratio = 1.8; pc.printf("Over max threshold count: %d\r\n", over_threshold_count); //pc.printf("Skip suppression ratio: %.2f\r\n", skip_sup_ratio); unsigned char sup_buf[dst->cols], Gx_buf_array[3][dst->cols], Gy_buf_array[3][dst->cols]; for(i = mask_radius; i < src->rows - mask_radius; i++) { sup_buf[j] = 0; // in sup_matrix, 0 is default value for(r = 0; r < 3; r++) { sdram.ReadData((i+r-1)*(Gx.cols) + SDRAM_DEVICE_ADDR + (Gx.data), buffer, Gx.cols/4); for(j = 0; j < Gx.cols/4; j++) { Gx_buf_array[r][j*4+3] = buffer[j]; Gx_buf_array[r][j*4+2] = buffer[j] >> 8; Gx_buf_array[r][j*4+1] = buffer[j] >> 16; Gx_buf_array[r][j*4] = buffer[j] >> 24; } } for(r = 0; r < 3; r++) { sdram.ReadData((i+r-1)*(Gy.cols) + SDRAM_DEVICE_ADDR + (Gy.data), buffer, Gy.cols/4); for(j = 0; j < Gy.cols/4; j++) { Gy_buf_array[r][j*4+3] = buffer[j]; Gy_buf_array[r][j*4+2] = buffer[j] >> 8; Gy_buf_array[r][j*4+1] = buffer[j] >> 16; Gy_buf_array[r][j*4] = buffer[j] >> 24; } } for(j = mask_radius; j < src->cols - mask_radius; j++) { if( j < mask_radius || (j >= src->rows - mask_radius)) { sup_buf[j] = 1; continue; } not_max_flag = 0; // pixels with significant high magnitute will never be suppressed if(Gx_buf_array[1][j] > 1.8 * max_threshold) { sup_buf[j] = 2; // in sup matrix, 2 means maxima's continue; } // for other pixels, compare its gradient magnitute with its neighbor for(r = -mask_radius; r <= mask_radius; r++) { if(not_max_flag == 1) break; for(c = -mask_radius; c <= mask_radius; c++) { vertical_neighbor = 0; // only neighbors vertical to the gradient direction will be checked switch (Gy_buf_array[1][j]) { case 0: if(c == 0 && (r == -1 || r ==1)) vertical_neighbor = 1; break; case 45: if((r == 1 && c == -1) || (r == -1 && c == 1)) vertical_neighbor = 1; break; case 90: if(r == 0 && (c == -1 || c == 1)) vertical_neighbor = 1; case 135: if((r == 1 && c == 1) || (r == -1 && c == -1)) vertical_neighbor = 1; default: break; } if(vertical_neighbor != 1) continue; if(Gy_buf_array[1][j] == Gy_buf_array[1+r][j+c]) { if(Gx_buf_array[1][j] < Gx_buf_array[1+r][j+c]) { not_max_flag = 1; sup_buf[j] = 1; break; } } } } } for(j = 0; j < sup_matrix.cols/4; j++) { buffer[j] = (16777216*sup_buf[j*4]) + (65536*sup_buf[j*4+1]) + (sup_buf[j*4+2]*256) + sup_buf[j*4+3]; } sdram.WriteData(SDRAM_DEVICE_ADDR + sup_matrix.data + (i*sup_matrix.cols), buffer, sup_matrix.cols/4); } // Gy is no longer used // Double threshold pc.printf("Double threshold...\r\n"); unsigned char dst_buf[dst->cols]; int a = 0, b = 0, d = 0; for(i = 0; i < src->rows; i++) { sdram.ReadData(i*(Gx.cols) + SDRAM_DEVICE_ADDR + (Gx.data), buffer, Gx.cols/4); for(j = 0; j < Gx.cols/4; j++) { Gx_buf[j*4+3] = buffer[j]; Gx_buf[j*4+2] = buffer[j] >> 8; Gx_buf[j*4+1] = buffer[j] >> 16; Gx_buf[j*4] = buffer[j] >> 24; } sdram.ReadData(i*(sup_matrix.cols) + SDRAM_DEVICE_ADDR + (sup_matrix.data), buffer, sup_matrix.cols/4); for(j = 0; j < Gx.cols/4; j++) { sup_buf[j*4+3] = buffer[j]; sup_buf[j*4+2] = buffer[j] >> 8; sup_buf[j*4+1] = buffer[j] >> 16; sup_buf[j*4] = buffer[j] >> 24; } for(j = 0; j < src->cols; j++) { if(i == 0 || j == 0 || i == src->rows-1 || j == src->cols-1) { dst_buf[j] = 0; b++; continue; } if(sup_buf[j] == 1) Gx_buf[j] = 0; if(Gx_buf[j] > max_threshold) // strong edge { dst_buf[j] = 255; a++; } else if(Gx_buf[j] < min_threshold) // weak edge { dst_buf[j] = 0; b++; } else // in middle: later check if it is connected to a strong edge { dst_buf[j] = Gx_buf[j]; d++; } } for(j = 0; j < dst->cols/4; j++) { buffer[j] = (16777216*dst_buf[j*4]) + (65536*dst_buf[j*4+1]) + (dst_buf[j*4+2]*256) + dst_buf[j*4+3]; } sdram.WriteData(SDRAM_DEVICE_ADDR + dst->data + (i*dst->cols), buffer, dst->cols/4); } pc.printf("strong = %d, weak = %d, middle = %d\r\n", a, b, d); int k; // Gx and sup_matrix is no longer used // pixel which has magnitude in-middle will be preserved if connected to a strong edge pc.printf("Edge tracking by hysteresis...\r\n"); short min_neighbor; unsigned char dst_buf_array[3][dst->cols]; for(i = 0; i < 10; i++) { for(j = 1; j < dst->rows-1; j++) { for(r = 0; r < 3; r++) { sdram.ReadData((i+r-1)*(dst->cols) + SDRAM_DEVICE_ADDR + (dst->data), buffer, dst->cols/4); for(k = 0; k < dst->cols/4; k++) { dst_buf_array[r][k*4+3] = buffer[k]; dst_buf_array[r][k*4+2] = buffer[k] >> 8; dst_buf_array[r][k*4+1] = buffer[k] >> 16; dst_buf_array[r][k*4] = buffer[k] >> 24; } } for(k = 1; k < dst->cols - 1; k++) { if(dst_buf_array[1][j] == 0 || dst_buf_array[1][j] == 255) continue; min_neighbor = 0; for(r = -1; r <= 1; r++) { for(c = -1; c <= 1; c++) { if(dst_buf_array[1+r][j+c] > max_threshold) dst_buf_array[1][j] = 255; else if(dst_buf_array[1+r][j+c] < min_threshold) min_neighbor++; } } if(min_neighbor == 8) dst_buf_array[1][j] = 0; } for(k = 0; k < dst->cols/4; k++) { buffer[j] = (16777216*dst_buf_array[1][k*4]) + (65536*dst_buf_array[1][k*4+1]) + (dst_buf_array[1][k*4+2]*256) + dst_buf_array[1][k*4+3]; } sdram.WriteData(SDRAM_DEVICE_ADDR + dst->data + (i*dst->cols), buffer, dst->cols/4); } } // those can't reach to a strong edge in 10 pixels will be supressed for(i = 0; i < src->rows; i++) { sdram.ReadData((i)*(dst->cols) + SDRAM_DEVICE_ADDR + (dst->data), buffer, dst->cols/4); for(k = 0; k < dst->cols/4; k++) { dst_buf[k*4+3] = buffer[k]; dst_buf[k*4+2] = buffer[k] >> 8; dst_buf[k*4+1] = buffer[k] >> 16; dst_buf[k*4] = buffer[k] >> 24; } for(j = 0; j < src->cols; j++) { if((dst_buf[j] == 255) || (dst_buf[j] == 0)) continue; else dst_buf[j] = 0; } for(j = 0; j < dst->cols/4; j++) { buffer[j] = (16777216*dst_buf[j*4]) + (65536*dst_buf[j*4+1]) + (dst_buf[j*4+2]*256) + dst_buf[j*4+3]; } sdram.WriteData(SDRAM_DEVICE_ADDR + dst->data + (i*dst->cols), buffer, dst->cols/4); } int zero = 0, max = 0, in_middle = 0; for(i = 0; i < src->rows; ++i) { sdram.ReadData((i)*(dst->cols) + SDRAM_DEVICE_ADDR + (dst->data), buffer, dst->cols/4); for(k = 0; k < dst->cols/4; k++) { dst_buf[k*4+3] = buffer[k]; dst_buf[k*4+2] = buffer[k] >> 8; dst_buf[k*4+1] = buffer[k] >> 16; dst_buf[k*4] = buffer[k] >> 24; } for ( j = 0; j < src->cols; ++j) { if(dst_buf[j] == 0) zero++; else if(dst_buf[j] == 255) max++; else in_middle++; } } pc.printf("zero = %ld, max = %ld, in_middle = %ld\r\n", zero, max, in_middle); pc.printf("SDRAM peak usage %ld\r\n", sup_matrix.data + (src->cols * src->rows) -1); return; } int myHoughLines(Image *src, line_polar *line_list, int threshold, int number) { // ref: // http://www.keymolen.com/2013/05/hough-transformation-c-implementation.html // https://en.wikipedia.org/wiki/Hough_transform pc.printf("\r\nHoughLines Module Invoked\r\n"); // initialize the accumulator for lines. rho and theta are resolutions in distance and angle (all set to 1) unsigned int dimension_r, dimension_t, i, j; //if(src->channels != 1) // only accept grayscal images // return -1; if(src->rows >= src->cols) dimension_r = src->rows; else dimension_r = src->cols; dimension_r = (unsigned int) ((double)dimension_r * sqrt((double)2)) + 1; dimension_t = 360; uint32_t error_count = 0, addr = src->data + (src->rows * src->cols); pc.printf("accumulator start at %ld\r\n", addr-1); uint32_t *accumulator = (uint32_t*)malloc(dimension_t*sizeof(uint32_t)); for(i = 0; i < dimension_r; i++) { for(j = 0; j < dimension_t; j++) accumulator[j] = 0; sdram.WriteData(SDRAM_DEVICE_ADDR + (4*i*dimension_t) + addr, accumulator, dimension_t); } for(i = 0; i < dimension_r;i++) { sdram.ReadData(SDRAM_DEVICE_ADDR + (4*i*dimension_t) + addr, accumulator, dimension_t); for(j = 0; j < dimension_t; j++) { if(accumulator[j] != 0) { error_count++; } } } pc.printf("error_count = %ld\r\n", error_count); pc.printf("Accumulator dimension: %d(rho) by %d(theta)\r\n", dimension_r, dimension_t); // scan through all edge pixels. int t, neg_r = 0, round_up = 0, round_down = 0, pixel_count = 0; double new_x, new_y, theta_rad, r; unsigned char src_buf[src->cols]; uint32_t buffer[src->cols/4], tmp_accu; for(i = 0; i < src->rows; i++) { sdram.ReadData(i*(src->cols) + SDRAM_DEVICE_ADDR + (src->data), buffer, src->cols/4); for(j = 0; j < src->cols/4; j++) { src_buf[j*4+3] = buffer[j]; src_buf[j*4+2] = buffer[j] >> 8; src_buf[j*4+1] = buffer[j] >> 16; src_buf[j*4] = buffer[j] >> 24; } for(j = 0; j < src->cols; j++) { if(src_buf[j] != 255) // only consider edge pixels (who have value of 255) continue; pixel_count++; for(t = 0; t < dimension_t; t++) // for each theta, calc the corresponding rho { new_x = (double)i; new_y = (double)j; theta_rad = (double)t * (M_PI / 180); r = (new_y * cos(theta_rad)) + (new_x * sin(theta_rad)); if(r <= 0) // negative rho = positive rho, they are duplicated lines that can be discarded { neg_r++; continue; } if( r - (int)r >= 0.5) // round up to the bin { round_up++; sdram.ReadData(4*dimension_t*((int)r +1) + SDRAM_DEVICE_ADDR + (addr) + (4*t), &tmp_accu, 1); tmp_accu++; if(tmp_accu > 503640) printf("(%d, %d)\r\n", (int)r +1, t); sdram.WriteData(4*dimension_t*((int)r +1) + SDRAM_DEVICE_ADDR + (addr) + (4*t), &tmp_accu, 1); } else // round down to the bin { round_down++; sdram.ReadData(4*dimension_t*((int)r) + SDRAM_DEVICE_ADDR + (addr) + (4*t), &tmp_accu, 1); tmp_accu++; sdram.WriteData(4*dimension_t*((int)r) + SDRAM_DEVICE_ADDR + (addr) + (4*t), &tmp_accu, 1); } } } j = src->rows / 10; if(i%j == 0) pc.printf("%d%%...",i/j*10); } pc.printf("\r\n"); pc.printf("negatve rho = %d\r\nround up = %d, round down = %d\r\n", neg_r, round_up, round_down); pc.printf("pixel count = %d\r\n", pixel_count); // find top x lines in rho-theta space int weakest = 0, count = 0, weakest_pt = 0; line_polar local_line_list[number]; for(i = 0; i < dimension_r; i++) { sdram.ReadData(4*i*dimension_t + SDRAM_DEVICE_ADDR + (addr), accumulator, dimension_t); for(j = 0; j < dimension_t; j++) { if(accumulator[j] < threshold || accumulator[j] <= weakest) continue; local_line_list[weakest_pt].rho = i; local_line_list[weakest_pt].theta = j; sdram.ReadData(4*dimension_t*i + SDRAM_DEVICE_ADDR + (addr) + (4*j), &tmp_accu, 1); local_line_list[weakest_pt].strength = tmp_accu; count++; if(count < number) { weakest_pt++; weakest = 0; } else { weakest = local_line_list[0].strength; weakest_pt = 0; for(t = 1; t < number; t++) { if(local_line_list[t].strength < weakest) { weakest = local_line_list[t].strength; weakest_pt = t; } } } } } // copy those found lines baack to main's stack memcpy(line_list, local_line_list, number*sizeof(line_polar)); for(i = 0; i < number; i++) { if(count == i) break; printf("line %d: rho = %d, theta = %d, strength = %d\r\n", i, local_line_list[i].rho, local_line_list[i].theta, local_line_list[i].strength); } printf("\r\n"); free(accumulator); // return the actual number of lines found above threshold return count; } int det(ptvec a, ptvec b) { return (a.x * b.y) - (a.y * b.x); } int line_intersection(trans_line line1, trans_line line2, ptvec *result) { ptvec xdiff, ydiff, d; xdiff.x = line1.ptvec1.x - line1.ptvec2.x; xdiff.y = line2.ptvec1.x - line2.ptvec2.x; ydiff.x = line1.ptvec1.y - line1.ptvec2.y; ydiff.y = line2.ptvec1.y - line2.ptvec2.y; int div = det(xdiff, ydiff); if(div == 0) { // Lines don't intersect return 0; } d.x = det(line1.ptvec1, line1.ptvec2); d.y = det(line2.ptvec1, line2.ptvec2); result->x = (int) det(d, xdiff) / div; result->y = (int) det(d, ydiff) / div; return 1; } double LineAnalysis(trans_line *line_list, int line_count, int dim_x, int dim_y) { printf("Needle Position Detection Module Invoked.\r\n"); int strongest_pt = 0, i = 0, j = 0, upper_count = 0, lower_count = 0; int upper_bin[line_count]; int lower_bin[line_count]; double average_deg = 0, diff, theta_deg; // find the line with highest confidence for(i = 0; i < line_count; i++) { if(line_list[i].strength > line_list[strongest_pt].strength) strongest_pt = i; } printf("Line used as reference of de-noise: rho = %d, ", (int)line_list[strongest_pt].rho); printf("theta = %d\r\n", (int) (line_list[strongest_pt].theta * 180 / M_PI)); // delete lines whose theta varies > 10 deg from the strongest line for(i = 0; i < line_count; i++) { diff = 180 * line_list[strongest_pt].theta / M_PI; diff = diff - (180 * line_list[i].theta / M_PI); if(diff >= 10 || diff <= -10) { printf("Line with confidence of %d is suppressed.\r\n", line_list[i].strength); line_list[i].strength = 0; continue; } average_deg = (180 * line_list[i].theta / M_PI) + average_deg; j++; } average_deg = average_deg / j; printf("Theta used to divide lines into bins: %.2f\r\n", average_deg); // categorize all lines into 2 bins depends on whether its theta is higher than average. for(i = 0; i < line_count; i++) { if(line_list[i].strength == 0) continue; theta_deg = line_list[i].theta * 180 / M_PI; if(theta_deg > average_deg) { upper_bin[upper_count] = i; upper_count++; } else { lower_bin[lower_count] = i; lower_count++; } } // choose the bin with minority of lines. // find a line in the bin with highest / lowest theta if we chose upper / lower bin // the chosen line is considered as the line mostly diverted from the other lines but yet near needle // find all intersections between this line and the lines in the bin with majority of lines // since this line is well seperated from others, points found should be far enough from gauge center int ref_pt = 0, inter_count = 0; ptvec intersect_list[line_count]; if(lower_count < upper_count) { for(i = 0; i < lower_count; i++) { if(line_list[lower_bin[i]].theta < line_list[lower_bin[ref_pt]].theta) ref_pt = i; } average_deg = (180 * line_list[lower_bin[ref_pt]].theta / M_PI); printf("Line in lower_bin is used to find intersections:\r\n"); printf("rho = %d, ", (int) (line_list[lower_bin[ref_pt]].rho)); printf("theta = %d ", (int) (line_list[lower_bin[ref_pt]].theta * 180 / (M_PI))); printf("strength = %d\r\n", (int) (line_list[lower_bin[ref_pt]].strength)); for(i = 0; i < upper_count; i++) { if(line_intersection(line_list[lower_bin[ref_pt]], line_list[upper_bin[i]], &intersect_list[inter_count]) == 1) inter_count++; } ref_pt = 0; for(i = 0; i < upper_count; i++) { if(line_list[upper_bin[i]].theta > line_list[upper_bin[ref_pt]].theta) ref_pt = i; } average_deg = (180 * line_list[upper_bin[ref_pt]].theta / M_PI) + average_deg; average_deg = average_deg / 2; } else { for(i = 0; i < upper_count; i++) { if(line_list[upper_bin[i]].theta > line_list[upper_bin[ref_pt]].theta) ref_pt = i; } average_deg = (180 * line_list[upper_bin[ref_pt]].theta / M_PI); printf("Line in upper_bin is used to find intersections:\r\n"); printf("rho = %d, ", (int) (line_list[upper_bin[ref_pt]].rho)); printf("theta = %d ", (int) (line_list[upper_bin[ref_pt]].theta * 180 / (M_PI))); printf("strength = %d\r\n", (int) (line_list[upper_bin[ref_pt]].strength)); for(i = 0; i < lower_count; i++) { if(line_intersection(line_list[upper_bin[ref_pt]], line_list[lower_bin[i]], &intersect_list[inter_count]) == 1) inter_count++; } ref_pt = 0; for(i = 0; i < lower_count; i++) { if(line_list[lower_bin[i]].theta < line_list[lower_bin[ref_pt]].theta) ref_pt = i; } average_deg = (180 * line_list[lower_bin[ref_pt]].theta / M_PI) + average_deg; average_deg = average_deg / 2; } // categorize all intersections points into 4 bins // ----------------> x // | | // | III | IV // | | // |--------------- // | | // | II | I // v | // y // the reason is that the center of the pic should be close to the center of the gauge. // the angle of the needle is already detected. we don't need actually find the center // to tell the direction of the needle. the distribution of intersections can provide // enough info to do the desicion. int cor_count[4] = {0, 0, 0, 0}; int var_x = 0, var_y = 0; printf("Intersections:\r\n"); for(i = 0; i < inter_count; i++) { if(intersect_list[i].x > dim_x /2) { if(intersect_list[i].y > dim_y/2) cor_count[0]++; else cor_count[3]++; } else { if(intersect_list[i].y > dim_y/2) cor_count[1]++; else cor_count[2]++; } var_x = var_x + abs(intersect_list[i].x - dim_x/2); var_y = var_y + abs(intersect_list[i].y - dim_y/2); printf("(%d, %d)\r\n", intersect_list[i].x, intersect_list[i].y); } printf("Variation in x: %d, in y: %d\r\n", var_x, var_y); int most_cor_pt = 0; printf("Intersection points distribution: \r\n"); for(i = 0; i < 4; i++) { if(cor_count[i] > cor_count[most_cor_pt]) most_cor_pt = i; printf("Cor %d: %d\r\n", i+1, cor_count[i]); } int correct_vote = 0, revert_vote = 0; if(average_deg >= 0 && average_deg < 90) // intersections are expected to be in II or IV { if(var_x < var_y) // variance in x is smaller, I+II should > III+IV { if((cor_count[0]+cor_count[1]) > (cor_count[2]+cor_count[3])) correct_vote++; else revert_vote++; } else // variance in y is smaller, II+III should > IV+I { if((cor_count[1]+cor_count[2]) > (cor_count[3]+cor_count[0])) correct_vote++; else revert_vote++; } if(correct_vote > revert_vote) printf("Original detected angle is correct.\r\n"); else { average_deg = average_deg + 180; printf("Reverted angle is correct.\r\n"); } } else if(average_deg >= 90 && average_deg < 180) // intersections are expected to be in III or I { if(var_x < var_y) // variance in x is smaller, I+II should < III+IV { if((cor_count[0]+cor_count[1]) < (cor_count[2]+cor_count[3])) correct_vote++; else revert_vote++; } else // variance in y is smaller, II+III should > IV+I { if((cor_count[1]+cor_count[2]) > (cor_count[3]+cor_count[0])) correct_vote++; else revert_vote++; } if(correct_vote > revert_vote) printf("Original detected angle is correct.\r\n"); else { average_deg = average_deg + 180; printf("Reverted angle is correct.\r\n"); } } else if(average_deg >= 180 && average_deg < 270) // intersections are expected to be in II or IV { if(var_x < var_y) // variance in x is smaller, I+II should < III+IV { if((cor_count[0]+cor_count[1]) < (cor_count[2]+cor_count[3])) correct_vote++; else revert_vote++; } else // variance in y is smaller, II+III should < IV+I { if((cor_count[1]+cor_count[2]) < (cor_count[3]+cor_count[0])) correct_vote++; else revert_vote++; } if(correct_vote > revert_vote) printf("Original detected angle is correct.\r\n"); else { average_deg = average_deg - 180; printf("Reverted angle is correct.\r\n"); } } else if(average_deg >= 270 && average_deg < 360) // intersections are expected to be in III or I { if(var_x < var_y) // variance in x is smaller, I+II should > III+IV { if((cor_count[0]+cor_count[1]) > (cor_count[2]+cor_count[3])) correct_vote++; else revert_vote++; } else // variance in y is smaller, II+III should < IV+I { if((cor_count[1]+cor_count[2]) < (cor_count[3]+cor_count[0])) correct_vote++; else revert_vote++; } if(correct_vote > revert_vote) printf("Original detected angle is correct.\r\n"); else { average_deg = average_deg - 180; printf("Reverted angle is correct.\r\n"); } } else { printf("Detection failed. The result may not be correct.\r\n"); } return average_deg; } int main(void) { pc.baud(115200); int i, j; led_red = 0; led_green = 0; FMC_SDRAM_CommandTypeDef SDRAMCommandStructure; SDRAMCommandStructure.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2; pc.printf("\r\nSDRAM demo started\r\n"); Image src, dst, bin; if (bmpread("/sd/1.bmp", &src) == 0) return 0; // smooth the picture using GaussianBlur: void GaussianBlur(Image *src, Image *dst, int width, double sigma) myGaussianBlur(&src, &dst, 5, 1.5); myCanny(&dst, &bin, 20, 60); line_polar my_line_list[10]; int line_count; line_count = myHoughLines(&bin, my_line_list, 40, line_limit); if(line_count >= line_limit) line_count = line_limit; trans_line line_list[line_limit]; unsigned int m = src.rows, n = src.cols; for(i = 0; i < line_count; i++) // combined for loops at #64, #73, #83, #92, #101 { line_list[i].rho = my_line_list[i].rho; line_list[i].theta = my_line_list[i].theta * (M_PI / 180); line_list[i].strength = my_line_list[i].strength; line_list[i].a = cos(line_list[i].theta); line_list[i].b = sin(line_list[i].theta); line_list[i].x0 = line_list[i].a * line_list[i].rho; line_list[i].y0 = line_list[i].b * line_list[i].rho; line_list[i].ptvec1.x = Round(line_list[i].x0 + (m+n)*(-line_list[i].b)); line_list[i].ptvec1.y = Round(line_list[i].y0 + (m+n)*line_list[i].a); line_list[i].ptvec2.x = Round(line_list[i].x0 - (m+n)*(-line_list[i].b)); line_list[i].ptvec2.y = Round(line_list[i].y0 - (m+n)*line_list[i].a); } double angle = LineAnalysis(line_list, line_count, src.cols, src.rows); double value = (angle - 42) / 270 * 50; pc.printf("Detected angle = %.2f\r\nEstimated value = %.2f\r\n", angle, value); }