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 Brady Greiner

Committer:
wsquan171
Date:
Tue May 03 08:52:52 2016 +0000
Revision:
1:8b8a77b7d715
Parent:
0:d3bf1776d1c6
Final demo for doing gauge needle detection on F429.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
grein028 0:d3bf1776d1c6 1 #include "mbed.h"
grein028 0:d3bf1776d1c6 2 #include "SDRAM_DISCO_F429ZI.h"
wsquan171 1:8b8a77b7d715 3 #include <stdio.h>
wsquan171 1:8b8a77b7d715 4 #include <stdlib.h>
wsquan171 1:8b8a77b7d715 5 #include <string.h>
wsquan171 1:8b8a77b7d715 6 #include <math.h>
wsquan171 1:8b8a77b7d715 7 #include "SDFileSystem.h"
wsquan171 1:8b8a77b7d715 8
wsquan171 1:8b8a77b7d715 9 #define M_PI 3.14159265358979323846
grein028 0:d3bf1776d1c6 10
grein028 0:d3bf1776d1c6 11 SDRAM_DISCO_F429ZI sdram;
wsquan171 1:8b8a77b7d715 12 SDFileSystem sd(PC_12, PC_11, PC_10, PB_2, "sd");// MOSI, MISO, SCK, CS
grein028 0:d3bf1776d1c6 13 DigitalOut led_green(LED1);
grein028 0:d3bf1776d1c6 14 DigitalOut led_red(LED2);
wsquan171 1:8b8a77b7d715 15 SPI spi(PC_12, PC_11, PC_10); // mosi, miso, sclk
wsquan171 1:8b8a77b7d715 16 DigitalOut cs(PB_2);
grein028 0:d3bf1776d1c6 17 Serial pc(USBTX, USBRX);
grein028 0:d3bf1776d1c6 18
wsquan171 1:8b8a77b7d715 19 #define line_limit 10 // limit on # of lines to be returned by HoughLines
wsquan171 1:8b8a77b7d715 20
wsquan171 1:8b8a77b7d715 21
wsquan171 1:8b8a77b7d715 22 typedef struct
wsquan171 1:8b8a77b7d715 23 {
wsquan171 1:8b8a77b7d715 24 int x;
wsquan171 1:8b8a77b7d715 25 int y;
wsquan171 1:8b8a77b7d715 26 } ptvec;
wsquan171 1:8b8a77b7d715 27
wsquan171 1:8b8a77b7d715 28 typedef struct
wsquan171 1:8b8a77b7d715 29 {
wsquan171 1:8b8a77b7d715 30 float rho, theta;
wsquan171 1:8b8a77b7d715 31 double a, b;
wsquan171 1:8b8a77b7d715 32 double x0, y0;
wsquan171 1:8b8a77b7d715 33 ptvec ptvec1, ptvec2;
wsquan171 1:8b8a77b7d715 34 unsigned int strength;
wsquan171 1:8b8a77b7d715 35 } trans_line;
wsquan171 1:8b8a77b7d715 36
wsquan171 1:8b8a77b7d715 37 typedef struct
wsquan171 1:8b8a77b7d715 38 {
wsquan171 1:8b8a77b7d715 39 unsigned int rows;
wsquan171 1:8b8a77b7d715 40 unsigned int cols;
wsquan171 1:8b8a77b7d715 41 unsigned int channels;
wsquan171 1:8b8a77b7d715 42 uint32_t data;
wsquan171 1:8b8a77b7d715 43 } Image;
wsquan171 1:8b8a77b7d715 44
wsquan171 1:8b8a77b7d715 45 typedef struct
wsquan171 1:8b8a77b7d715 46 {
wsquan171 1:8b8a77b7d715 47 int rho;
wsquan171 1:8b8a77b7d715 48 int theta;
wsquan171 1:8b8a77b7d715 49 unsigned int strength;
wsquan171 1:8b8a77b7d715 50 } line_polar;
wsquan171 1:8b8a77b7d715 51
grein028 0:d3bf1776d1c6 52
wsquan171 1:8b8a77b7d715 53 int bmpread(char *filename, Image *dst)
wsquan171 1:8b8a77b7d715 54 {
wsquan171 1:8b8a77b7d715 55 pc.printf("Reading Image file: %s\r\n", filename);
wsquan171 1:8b8a77b7d715 56 FILE *file;
wsquan171 1:8b8a77b7d715 57 unsigned char tempSize4[4];
wsquan171 1:8b8a77b7d715 58 unsigned int width, height;
wsquan171 1:8b8a77b7d715 59 unsigned short planes, bpp;
wsquan171 1:8b8a77b7d715 60
wsquan171 1:8b8a77b7d715 61 if ((file = fopen(filename, "rb"))==NULL)
wsquan171 1:8b8a77b7d715 62 {
wsquan171 1:8b8a77b7d715 63 pc.printf("File Not Found : %s\r\n", filename);
wsquan171 1:8b8a77b7d715 64 return 0;
wsquan171 1:8b8a77b7d715 65 }
wsquan171 1:8b8a77b7d715 66
wsquan171 1:8b8a77b7d715 67 // seek through the bmp header, up to the width/height:
wsquan171 1:8b8a77b7d715 68 fseek(file, 18, SEEK_CUR);
wsquan171 1:8b8a77b7d715 69 if ( fread( tempSize4, 1, 4, file) < 4 )
wsquan171 1:8b8a77b7d715 70 return 0;
wsquan171 1:8b8a77b7d715 71 width = (tempSize4[3]<<24) | (tempSize4[2]<<16) | (tempSize4[1]<<8) | tempSize4[0]; // big endian
wsquan171 1:8b8a77b7d715 72 if ( fread( tempSize4, 1, 4, file) < 4 )
wsquan171 1:8b8a77b7d715 73 return 0;
wsquan171 1:8b8a77b7d715 74 height = (tempSize4[3]<<24) | (tempSize4[2]<<16) | (tempSize4[1]<<8) | tempSize4[0];;
wsquan171 1:8b8a77b7d715 75
wsquan171 1:8b8a77b7d715 76 pc.printf("width: %d, height: %d\r\n", width, height);
wsquan171 1:8b8a77b7d715 77
wsquan171 1:8b8a77b7d715 78 dst->rows = height;
wsquan171 1:8b8a77b7d715 79 dst->cols = width;
wsquan171 1:8b8a77b7d715 80
wsquan171 1:8b8a77b7d715 81
wsquan171 1:8b8a77b7d715 82 if ( fread( tempSize4, 1, 2, file) < 2 )
wsquan171 1:8b8a77b7d715 83 return 0;
wsquan171 1:8b8a77b7d715 84 planes = (tempSize4[1]<<8) | tempSize4[0]; // big endian
wsquan171 1:8b8a77b7d715 85 dst->channels = planes;
wsquan171 1:8b8a77b7d715 86
wsquan171 1:8b8a77b7d715 87 if ( fread( tempSize4, 1, 2, file) < 2 )
wsquan171 1:8b8a77b7d715 88 return 0;
wsquan171 1:8b8a77b7d715 89 bpp = (tempSize4[1]<<8) | tempSize4[0]; // big endian
wsquan171 1:8b8a77b7d715 90 pc.printf("planes: %d, bpp: %d\r\n", planes, bpp);
wsquan171 1:8b8a77b7d715 91
wsquan171 1:8b8a77b7d715 92 fseek(file, 24, SEEK_CUR);
wsquan171 1:8b8a77b7d715 93 unsigned char *readline;
wsquan171 1:8b8a77b7d715 94 unsigned int padding=0; while ((width*3+padding) % 4!=0) padding++;
wsquan171 1:8b8a77b7d715 95 unsigned int linewidth = width * 3 + padding;
wsquan171 1:8b8a77b7d715 96 readline = (unsigned char*)malloc(linewidth * sizeof(unsigned char));
wsquan171 1:8b8a77b7d715 97 unsigned char R, G, B;
wsquan171 1:8b8a77b7d715 98 int i, j;
wsquan171 1:8b8a77b7d715 99
wsquan171 1:8b8a77b7d715 100 dst->data = 1;
wsquan171 1:8b8a77b7d715 101 unsigned char *dst_buf= (unsigned char *) malloc(width * sizeof(unsigned char));
wsquan171 1:8b8a77b7d715 102 uint32_t buffer[width/4];
wsquan171 1:8b8a77b7d715 103
wsquan171 1:8b8a77b7d715 104 pc.printf("Converting to grayscale and Storing to SDRAM\r\n");
wsquan171 1:8b8a77b7d715 105 for(i = height-1; i >= 0; i--)
wsquan171 1:8b8a77b7d715 106 {
wsquan171 1:8b8a77b7d715 107 fread(readline, sizeof(unsigned char), linewidth, file);
wsquan171 1:8b8a77b7d715 108 for(j = 0; j < width; j++)
wsquan171 1:8b8a77b7d715 109 {
wsquan171 1:8b8a77b7d715 110 B = readline[3*j];
wsquan171 1:8b8a77b7d715 111 G = readline[3*j+1];
wsquan171 1:8b8a77b7d715 112 R = readline[3*j+2];
wsquan171 1:8b8a77b7d715 113 dst_buf[j] = (0.299*R) + (0.587*G) + (0.114*B);
wsquan171 1:8b8a77b7d715 114 }
wsquan171 1:8b8a77b7d715 115 for(j = 0; j < width/4; j++)
wsquan171 1:8b8a77b7d715 116 {
wsquan171 1:8b8a77b7d715 117 buffer[j] = (16777216*dst_buf[j*4]) + (65536*dst_buf[j*4+1]) + (dst_buf[j*4+2]*256) + dst_buf[j*4+3];
wsquan171 1:8b8a77b7d715 118 }
wsquan171 1:8b8a77b7d715 119 sdram.WriteData(SDRAM_DEVICE_ADDR + dst->data + (i*width), buffer, width/4);
wsquan171 1:8b8a77b7d715 120 j = height / 10;
wsquan171 1:8b8a77b7d715 121 if(i%j == 0)
wsquan171 1:8b8a77b7d715 122 pc.printf("%d%%...",(100 - (i/j*10)));
wsquan171 1:8b8a77b7d715 123 }
wsquan171 1:8b8a77b7d715 124 pc.printf("\r\nImage loaded\r\n");
wsquan171 1:8b8a77b7d715 125 free(readline);
wsquan171 1:8b8a77b7d715 126 free(dst_buf);
wsquan171 1:8b8a77b7d715 127 fclose(file);
wsquan171 1:8b8a77b7d715 128
wsquan171 1:8b8a77b7d715 129 return 1;
wsquan171 1:8b8a77b7d715 130 }
wsquan171 1:8b8a77b7d715 131
wsquan171 1:8b8a77b7d715 132 int Round(double number)
wsquan171 1:8b8a77b7d715 133 {
wsquan171 1:8b8a77b7d715 134 double diff_down, diff_up;
wsquan171 1:8b8a77b7d715 135 diff_down = number - (int)number;
wsquan171 1:8b8a77b7d715 136 diff_up = 1 - diff_down;
wsquan171 1:8b8a77b7d715 137 if(diff_down < diff_up)
wsquan171 1:8b8a77b7d715 138 return (int)number;
wsquan171 1:8b8a77b7d715 139 else
wsquan171 1:8b8a77b7d715 140 return 1+(int)number;
wsquan171 1:8b8a77b7d715 141 }
grein028 0:d3bf1776d1c6 142
wsquan171 1:8b8a77b7d715 143 void matrix_convolution(Image *src, Image *dst, double **mask, int width)
grein028 0:d3bf1776d1c6 144 {
wsquan171 1:8b8a77b7d715 145 unsigned char lines[width][src->cols];
wsquan171 1:8b8a77b7d715 146 unsigned char result[src->cols];
wsquan171 1:8b8a77b7d715 147 uint32_t buffer[src->cols/4];
wsquan171 1:8b8a77b7d715 148 int i, j, k, r, c, actual_r, actual_c, scope;
wsquan171 1:8b8a77b7d715 149 double gaussian_sum;
wsquan171 1:8b8a77b7d715 150 //int flag = 1;
wsquan171 1:8b8a77b7d715 151 scope = width/2;
wsquan171 1:8b8a77b7d715 152 for(i = 0; i < src->rows; i++)
wsquan171 1:8b8a77b7d715 153 {
wsquan171 1:8b8a77b7d715 154 if(i < scope || (src->rows - i <= scope))
wsquan171 1:8b8a77b7d715 155 {
wsquan171 1:8b8a77b7d715 156 sdram.ReadData(i*(src->cols) + SDRAM_DEVICE_ADDR + (src->data), buffer, src->cols/4);
wsquan171 1:8b8a77b7d715 157 sdram.WriteData(i*(src->cols) + SDRAM_DEVICE_ADDR + (dst->data), buffer, src->cols/4);
wsquan171 1:8b8a77b7d715 158 continue;
wsquan171 1:8b8a77b7d715 159 }
wsquan171 1:8b8a77b7d715 160 for(j = 0; j < width; j++)
wsquan171 1:8b8a77b7d715 161 {
wsquan171 1:8b8a77b7d715 162 sdram.ReadData((i-scope+j)*(src->cols) + SDRAM_DEVICE_ADDR + (src->data), buffer, src->cols/4);
wsquan171 1:8b8a77b7d715 163 for(k = 0; k < src->cols/4; k++)
wsquan171 1:8b8a77b7d715 164 {
wsquan171 1:8b8a77b7d715 165 lines[j][k*4+3] = buffer[k];
wsquan171 1:8b8a77b7d715 166 lines[j][k*4+2] = buffer[k] >> 8;
wsquan171 1:8b8a77b7d715 167 lines[j][k*4+1] = buffer[k] >> 16;
wsquan171 1:8b8a77b7d715 168 lines[j][k*4] = buffer[k] >> 24;
wsquan171 1:8b8a77b7d715 169 }
wsquan171 1:8b8a77b7d715 170 }
wsquan171 1:8b8a77b7d715 171 //pc.printf("line %d, data = %d\r\n", i, (int)lines[scope][0]);
wsquan171 1:8b8a77b7d715 172
wsquan171 1:8b8a77b7d715 173 for(j = 0; j < src->cols; j++)
wsquan171 1:8b8a77b7d715 174 {
wsquan171 1:8b8a77b7d715 175 if( (j < scope) || (src->cols - j <= scope))
wsquan171 1:8b8a77b7d715 176 {
wsquan171 1:8b8a77b7d715 177 result[j] = lines[scope][j];
wsquan171 1:8b8a77b7d715 178 continue;
wsquan171 1:8b8a77b7d715 179 }
wsquan171 1:8b8a77b7d715 180 gaussian_sum = 0;
wsquan171 1:8b8a77b7d715 181 for(r = 0; r < width; r++)
wsquan171 1:8b8a77b7d715 182 {
wsquan171 1:8b8a77b7d715 183 for(c = 0; c < width; c++)
wsquan171 1:8b8a77b7d715 184 {
wsquan171 1:8b8a77b7d715 185 actual_r = r;
wsquan171 1:8b8a77b7d715 186 actual_c = j + c - scope;
wsquan171 1:8b8a77b7d715 187 /*
wsquan171 1:8b8a77b7d715 188 if(flag == 1)
wsquan171 1:8b8a77b7d715 189 {
wsquan171 1:8b8a77b7d715 190 pc.printf("%d, %d mask = %.4f, value = %d\r\n", r,c,mask[r][c],(int)lines[actual_r][actual_c]);
wsquan171 1:8b8a77b7d715 191 }
wsquan171 1:8b8a77b7d715 192 */
wsquan171 1:8b8a77b7d715 193 double op1 = mask[r][c];
wsquan171 1:8b8a77b7d715 194 double op2 = (double) lines[actual_r][actual_c];
wsquan171 1:8b8a77b7d715 195 double temp = op1 * op2;
wsquan171 1:8b8a77b7d715 196 gaussian_sum = gaussian_sum + temp;
wsquan171 1:8b8a77b7d715 197 }
wsquan171 1:8b8a77b7d715 198 }
wsquan171 1:8b8a77b7d715 199 //flag = 0;
wsquan171 1:8b8a77b7d715 200 if(gaussian_sum > 255) gaussian_sum = 255;
wsquan171 1:8b8a77b7d715 201 if(gaussian_sum < 0) gaussian_sum = 0;
wsquan171 1:8b8a77b7d715 202 result[j] = (unsigned char)Round(gaussian_sum);
wsquan171 1:8b8a77b7d715 203 //pc.printf("%d ", result[j]);
wsquan171 1:8b8a77b7d715 204 }
wsquan171 1:8b8a77b7d715 205 //pc.printf("\r\n");
wsquan171 1:8b8a77b7d715 206 for(j = 0; j < src->cols/4; j++)
wsquan171 1:8b8a77b7d715 207 {
wsquan171 1:8b8a77b7d715 208 buffer[j] = (16777216*result[j*4]) + (65536*result[j*4+1]) + (result[j*4+2]*256) + result[j*4+3];
wsquan171 1:8b8a77b7d715 209 }
wsquan171 1:8b8a77b7d715 210 sdram.WriteData(SDRAM_DEVICE_ADDR + dst->data + (i*src->cols), buffer, src->cols/4);
wsquan171 1:8b8a77b7d715 211 j = src->rows / 10;
wsquan171 1:8b8a77b7d715 212 if(i%j == 0)
wsquan171 1:8b8a77b7d715 213 pc.printf("%d%%...",i/j*10);
wsquan171 1:8b8a77b7d715 214 }
wsquan171 1:8b8a77b7d715 215 pc.printf("\r\n");
wsquan171 1:8b8a77b7d715 216 return;
wsquan171 1:8b8a77b7d715 217 }
wsquan171 1:8b8a77b7d715 218
wsquan171 1:8b8a77b7d715 219
wsquan171 1:8b8a77b7d715 220 void myGaussianBlur(Image *src, Image *dst, int width, double sigma)
wsquan171 1:8b8a77b7d715 221 {
wsquan171 1:8b8a77b7d715 222 // build the Gaussian Kernal
wsquan171 1:8b8a77b7d715 223 // ref: http://stackoverflow.com/questions/8204645/implementing-gaussian-blur-how-to-calculate-convolution-matrix-kernel
wsquan171 1:8b8a77b7d715 224
wsquan171 1:8b8a77b7d715 225 pc.printf("\r\nGaussian Blur Module Invoked\r\n");
wsquan171 1:8b8a77b7d715 226 dst->rows = src->rows;
wsquan171 1:8b8a77b7d715 227 dst->cols = src->cols;
wsquan171 1:8b8a77b7d715 228 dst->channels = src->channels;
wsquan171 1:8b8a77b7d715 229 dst->data = src->data + (src->rows * src->cols);
wsquan171 1:8b8a77b7d715 230
wsquan171 1:8b8a77b7d715 231 int W = width;
wsquan171 1:8b8a77b7d715 232 int i, x, y;
wsquan171 1:8b8a77b7d715 233 double **kernel;
wsquan171 1:8b8a77b7d715 234 kernel = (double **) malloc(W * sizeof(double *));
wsquan171 1:8b8a77b7d715 235 for(i = 0; i < W; i++)
wsquan171 1:8b8a77b7d715 236 kernel[i] = (double *) malloc(W * sizeof(double));
wsquan171 1:8b8a77b7d715 237 double mean = W/2;
wsquan171 1:8b8a77b7d715 238 double sum = 0.0; // For accumulating the kernel values
wsquan171 1:8b8a77b7d715 239 for (x = 0; x < W; ++x)
wsquan171 1:8b8a77b7d715 240 {
wsquan171 1:8b8a77b7d715 241 for (y = 0; y < W; ++y)
wsquan171 1:8b8a77b7d715 242 {
wsquan171 1:8b8a77b7d715 243 kernel[x][y] = exp( -0.5 * (pow((x-mean)/sigma, 2.0) + pow((y-mean)/sigma,2.0)) )
wsquan171 1:8b8a77b7d715 244 / (2 * M_PI * sigma * sigma);
wsquan171 1:8b8a77b7d715 245
wsquan171 1:8b8a77b7d715 246 // Accumulate the kernel values
wsquan171 1:8b8a77b7d715 247 sum += kernel[x][y];
wsquan171 1:8b8a77b7d715 248 }
wsquan171 1:8b8a77b7d715 249 }
wsquan171 1:8b8a77b7d715 250 // Normalize the kernel
wsquan171 1:8b8a77b7d715 251 //pc.printf("Gaussian Kernal in use:\r\n");
wsquan171 1:8b8a77b7d715 252 for (x = 0; x < W; ++x)
wsquan171 1:8b8a77b7d715 253 {
wsquan171 1:8b8a77b7d715 254 for (y = 0; y < W; ++y)
wsquan171 1:8b8a77b7d715 255 {
wsquan171 1:8b8a77b7d715 256 kernel[x][y] /= sum;
wsquan171 1:8b8a77b7d715 257 //pc.printf("%.7f ", kernel[x][y]);
wsquan171 1:8b8a77b7d715 258 }
wsquan171 1:8b8a77b7d715 259 //pc.printf("\r\n");
wsquan171 1:8b8a77b7d715 260 }
wsquan171 1:8b8a77b7d715 261
wsquan171 1:8b8a77b7d715 262 // do matrix convolution
wsquan171 1:8b8a77b7d715 263 matrix_convolution(src, dst, kernel, width);
wsquan171 1:8b8a77b7d715 264
wsquan171 1:8b8a77b7d715 265
wsquan171 1:8b8a77b7d715 266 for(i = 0; i < W; i++)
wsquan171 1:8b8a77b7d715 267 free(kernel[i]);
wsquan171 1:8b8a77b7d715 268 free(kernel);
wsquan171 1:8b8a77b7d715 269 return;
wsquan171 1:8b8a77b7d715 270 }
grein028 0:d3bf1776d1c6 271
wsquan171 1:8b8a77b7d715 272 void myCanny(Image *src, Image *dst, double min_threshold, double max_threshold)
wsquan171 1:8b8a77b7d715 273 {
wsquan171 1:8b8a77b7d715 274 // ref:
wsquan171 1:8b8a77b7d715 275 // http://dasl.mem.drexel.edu/alumni/bGreen/www.pages.drexel.edu/_weg22/can_tut.html
wsquan171 1:8b8a77b7d715 276 // https://en.wikipedia.org/wiki/Canny_edge_detector
wsquan171 1:8b8a77b7d715 277
wsquan171 1:8b8a77b7d715 278 pc.printf("\r\nCanny Edge Detection Module Invoked\r\n");
wsquan171 1:8b8a77b7d715 279
wsquan171 1:8b8a77b7d715 280 dst->rows = src->rows;
wsquan171 1:8b8a77b7d715 281 dst->cols = src->cols;
wsquan171 1:8b8a77b7d715 282 dst->channels = src->channels;
wsquan171 1:8b8a77b7d715 283 dst->data = src->data + (src->rows * src->cols);
wsquan171 1:8b8a77b7d715 284
wsquan171 1:8b8a77b7d715 285 // de-noise using local Gaussian Blur, simulating size = 5, sigma = 1.4
wsquan171 1:8b8a77b7d715 286 pc.printf("Smoothing image...\r\n");
wsquan171 1:8b8a77b7d715 287 double mask[5][5] =
wsquan171 1:8b8a77b7d715 288 {
wsquan171 1:8b8a77b7d715 289 {2, 4, 5, 4, 2},
wsquan171 1:8b8a77b7d715 290 {4, 9, 12, 9, 4},
wsquan171 1:8b8a77b7d715 291 {5, 12, 15, 12, 5},
wsquan171 1:8b8a77b7d715 292 {4, 9, 12, 9, 4},
wsquan171 1:8b8a77b7d715 293 {2, 4, 5, 4, 2}
wsquan171 1:8b8a77b7d715 294 };
wsquan171 1:8b8a77b7d715 295 double **kernel;
wsquan171 1:8b8a77b7d715 296 int i, j;
wsquan171 1:8b8a77b7d715 297 kernel = (double **) malloc(5 * sizeof(double *));
wsquan171 1:8b8a77b7d715 298 for(i = 0; i < 5; i++)
wsquan171 1:8b8a77b7d715 299 kernel[i] = (double *) malloc(5 * sizeof(double));
wsquan171 1:8b8a77b7d715 300 for(i = 0; i < 5; i++)
wsquan171 1:8b8a77b7d715 301 {
wsquan171 1:8b8a77b7d715 302 for(j = 0; j < 5; j++)
wsquan171 1:8b8a77b7d715 303 {
wsquan171 1:8b8a77b7d715 304 kernel[i][j] = mask[i][j] / 159;
wsquan171 1:8b8a77b7d715 305 //printf("%.7f ", kernel[i][j]);
wsquan171 1:8b8a77b7d715 306 }
wsquan171 1:8b8a77b7d715 307 //printf("\n");
wsquan171 1:8b8a77b7d715 308 }
wsquan171 1:8b8a77b7d715 309 matrix_convolution(src, dst, kernel, 5);
wsquan171 1:8b8a77b7d715 310 for(i = 0; i < 5; i++)
wsquan171 1:8b8a77b7d715 311 free(kernel[i]);
wsquan171 1:8b8a77b7d715 312 free(kernel);
wsquan171 1:8b8a77b7d715 313
wsquan171 1:8b8a77b7d715 314 // find gradient x and gradient y
wsquan171 1:8b8a77b7d715 315 pc.printf("Finding gradient in x and y for each pixel...\r\n");
wsquan171 1:8b8a77b7d715 316 Image Gx, Gy; // Gx and Gy are used to store x-gradient and y-gradient
wsquan171 1:8b8a77b7d715 317 Gx.cols = src->cols;
wsquan171 1:8b8a77b7d715 318 Gx.rows = src->rows;
wsquan171 1:8b8a77b7d715 319 Gx.data = dst->data + (dst->cols * dst->rows);
wsquan171 1:8b8a77b7d715 320 Gy.cols = src->cols;
wsquan171 1:8b8a77b7d715 321 Gy.rows = src->rows;
wsquan171 1:8b8a77b7d715 322 Gy.data = Gx.data + (dst->cols * dst->rows);
wsquan171 1:8b8a77b7d715 323
wsquan171 1:8b8a77b7d715 324 // size-3 Sobel mask
wsquan171 1:8b8a77b7d715 325 double sobel_x[3][3] = {{-1, 0, 1}, {-2, 0, 2}, {-1, 0, 1}};
wsquan171 1:8b8a77b7d715 326 double sobel_y[3][3] = {{-1, -2, -1}, {0, 0, 0}, {1, 2, 1}};
wsquan171 1:8b8a77b7d715 327 double **mask_x, **mask_y;
wsquan171 1:8b8a77b7d715 328 mask_x = (double **) malloc(3 * sizeof(double *));
wsquan171 1:8b8a77b7d715 329 for(i = 0; i < 3; i++)
wsquan171 1:8b8a77b7d715 330 mask_x[i] = (double *) malloc(3 * sizeof(double));
wsquan171 1:8b8a77b7d715 331 mask_y = (double **) malloc(3 * sizeof(double *));
wsquan171 1:8b8a77b7d715 332 for(i = 0; i < 3; i++)
wsquan171 1:8b8a77b7d715 333 mask_y[i] = (double *) malloc(3 * sizeof(double));
wsquan171 1:8b8a77b7d715 334 for(i = 0; i < 3; i++)
wsquan171 1:8b8a77b7d715 335 {
wsquan171 1:8b8a77b7d715 336 for(j = 0; j < 3; j++)
wsquan171 1:8b8a77b7d715 337 {
wsquan171 1:8b8a77b7d715 338 mask_x[i][j] = sobel_x[i][j];
wsquan171 1:8b8a77b7d715 339 mask_y[i][j] = sobel_y[i][j];
wsquan171 1:8b8a77b7d715 340 }
wsquan171 1:8b8a77b7d715 341 }
wsquan171 1:8b8a77b7d715 342 pc.printf("in x: ");
wsquan171 1:8b8a77b7d715 343 matrix_convolution(src, &Gx, mask_x, 3);
wsquan171 1:8b8a77b7d715 344 pc.printf("in y: ");
wsquan171 1:8b8a77b7d715 345 matrix_convolution(src, &Gy, mask_y, 3);
wsquan171 1:8b8a77b7d715 346 for(i = 0; i < 3; i++)
wsquan171 1:8b8a77b7d715 347 {
wsquan171 1:8b8a77b7d715 348 free(mask_x[i]);
wsquan171 1:8b8a77b7d715 349 free(mask_y[i]);
wsquan171 1:8b8a77b7d715 350 }
wsquan171 1:8b8a77b7d715 351 free(mask_x);
wsquan171 1:8b8a77b7d715 352 free(mask_y);
wsquan171 1:8b8a77b7d715 353 //pc.printf("dst at %d, Gx at %d, Gy at %d\r\n", dst->data, Gx.data, Gy.data);
wsquan171 1:8b8a77b7d715 354
wsquan171 1:8b8a77b7d715 355 // calculate gradient strength (store in Gx) and direction (store in Gy)
wsquan171 1:8b8a77b7d715 356 pc.printf("Calculating gradient strength and direction...\r\n");
wsquan171 1:8b8a77b7d715 357 double gx_val, gy_val;
wsquan171 1:8b8a77b7d715 358 double arctan_val;
wsquan171 1:8b8a77b7d715 359 int over_threshold_count = 0; // number used for adaptive non-max suppression
wsquan171 1:8b8a77b7d715 360 uint32_t buffer[src->cols/4];
wsquan171 1:8b8a77b7d715 361 unsigned char Gx_buf[src->cols], Gy_buf[src->cols];
wsquan171 1:8b8a77b7d715 362 //pc.printf("%d %d %d %d %d %d", src->rows, src->cols, Gx.cols, Gx.rows, Gy.cols, Gy.rows);
wsquan171 1:8b8a77b7d715 363 for(i = 0; i < src->rows; i++)
wsquan171 1:8b8a77b7d715 364 {
wsquan171 1:8b8a77b7d715 365 sdram.ReadData(i*(Gx.cols) + SDRAM_DEVICE_ADDR + (Gx.data), buffer, Gx.cols/4);
wsquan171 1:8b8a77b7d715 366 for(j = 0; j < Gx.cols/4; j++)
wsquan171 1:8b8a77b7d715 367 {
wsquan171 1:8b8a77b7d715 368 Gx_buf[j*4+3] = buffer[j];
wsquan171 1:8b8a77b7d715 369 Gx_buf[j*4+2] = buffer[j] >> 8;
wsquan171 1:8b8a77b7d715 370 Gx_buf[j*4+1] = buffer[j] >> 16;
wsquan171 1:8b8a77b7d715 371 Gx_buf[j*4] = buffer[j] >> 24;
wsquan171 1:8b8a77b7d715 372 }
wsquan171 1:8b8a77b7d715 373 sdram.ReadData(i*(Gy.cols) + SDRAM_DEVICE_ADDR + (Gy.data), buffer, Gy.cols/4);
wsquan171 1:8b8a77b7d715 374 for(j = 0; j < Gy.cols/4; j++)
wsquan171 1:8b8a77b7d715 375 {
wsquan171 1:8b8a77b7d715 376 Gy_buf[j*4+3] = buffer[j];
wsquan171 1:8b8a77b7d715 377 Gy_buf[j*4+2] = buffer[j] >> 8;
wsquan171 1:8b8a77b7d715 378 Gy_buf[j*4+1] = buffer[j] >> 16;
wsquan171 1:8b8a77b7d715 379 Gy_buf[j*4] = buffer[j] >> 24;
wsquan171 1:8b8a77b7d715 380 }
wsquan171 1:8b8a77b7d715 381
wsquan171 1:8b8a77b7d715 382 for(j = 0; j < src->cols; j++)
wsquan171 1:8b8a77b7d715 383 {
wsquan171 1:8b8a77b7d715 384 gx_val = Gx_buf[j];
wsquan171 1:8b8a77b7d715 385 gy_val = Gy_buf[j];
wsquan171 1:8b8a77b7d715 386
wsquan171 1:8b8a77b7d715 387 Gx_buf[j] = (unsigned char)sqrt(pow(gx_val, 2) + pow(gy_val, 2));
wsquan171 1:8b8a77b7d715 388 if(Gx_buf[j] > max_threshold)
wsquan171 1:8b8a77b7d715 389 over_threshold_count++;
wsquan171 1:8b8a77b7d715 390 arctan_val = atan(gy_val / gx_val);
wsquan171 1:8b8a77b7d715 391 if(arctan_val >= 0.41421 && arctan_val < 2.414421) // 22.5 to 67.5 deg
wsquan171 1:8b8a77b7d715 392 Gy_buf[j] = 45;
wsquan171 1:8b8a77b7d715 393 else if(arctan_val >= 2.41421 || arctan_val < -2.41421) // 67.5 to 112.5 deg
wsquan171 1:8b8a77b7d715 394 Gy_buf[j] = 90;
wsquan171 1:8b8a77b7d715 395 else if(arctan_val >= -2.41421 && arctan_val < 0.41421) // 112.5 to 157.5 deg
wsquan171 1:8b8a77b7d715 396 Gy_buf[j] = 135;
wsquan171 1:8b8a77b7d715 397 else // 0 to 22.5 and 157.5 to 180 deg
wsquan171 1:8b8a77b7d715 398 Gy_buf[j] = 0;
wsquan171 1:8b8a77b7d715 399 }
wsquan171 1:8b8a77b7d715 400
wsquan171 1:8b8a77b7d715 401 for(j = 0; j < Gx.cols/4; j++)
wsquan171 1:8b8a77b7d715 402 {
wsquan171 1:8b8a77b7d715 403 buffer[j] = (16777216*Gx_buf[j*4]) + (65536*Gx_buf[j*4+1]) + (Gx_buf[j*4+2]*256) + Gx_buf[j*4+3];
wsquan171 1:8b8a77b7d715 404 }
wsquan171 1:8b8a77b7d715 405 sdram.WriteData(SDRAM_DEVICE_ADDR + Gx.data + (i*Gx.cols), buffer, Gx.cols/4);
wsquan171 1:8b8a77b7d715 406 for(j = 0; j < Gy.cols/4; j++)
wsquan171 1:8b8a77b7d715 407 {
wsquan171 1:8b8a77b7d715 408 buffer[j] = (16777216*Gy_buf[j*4]) + (65536*Gy_buf[j*4+1]) + (Gy_buf[j*4+2]*256) + Gy_buf[j*4+3];
wsquan171 1:8b8a77b7d715 409 }
wsquan171 1:8b8a77b7d715 410 sdram.WriteData(SDRAM_DEVICE_ADDR + Gy.data + (i*Gy.cols), buffer, Gy.cols/4);
wsquan171 1:8b8a77b7d715 411 }
wsquan171 1:8b8a77b7d715 412
wsquan171 1:8b8a77b7d715 413 // Non-maximum suppression. Aim is to shrink the edge width
wsquan171 1:8b8a77b7d715 414 pc.printf("Non-maximum suppression...\r\n");
wsquan171 1:8b8a77b7d715 415 int mask_radius = 1; // hard-coded parameter
wsquan171 1:8b8a77b7d715 416 int r, c, not_max_flag, vertical_neighbor;
wsquan171 1:8b8a77b7d715 417 Image sup_matrix; // maxtrix used to keep track of local maxima stutas for each pixel
wsquan171 1:8b8a77b7d715 418 sup_matrix.cols = dst->cols;
wsquan171 1:8b8a77b7d715 419 sup_matrix.rows = dst->rows;
wsquan171 1:8b8a77b7d715 420 sup_matrix.data = Gy.data + (dst->cols * dst->rows);
wsquan171 1:8b8a77b7d715 421 double skip_sup_ratio = (double)over_threshold_count/8800; // 10000 pixels should be found in a 800x600 pic
wsquan171 1:8b8a77b7d715 422 if(skip_sup_ratio < 1)
wsquan171 1:8b8a77b7d715 423 skip_sup_ratio = 1;
wsquan171 1:8b8a77b7d715 424 else if(skip_sup_ratio > 1.8)
wsquan171 1:8b8a77b7d715 425 skip_sup_ratio = 1.8;
wsquan171 1:8b8a77b7d715 426 pc.printf("Over max threshold count: %d\r\n", over_threshold_count);
wsquan171 1:8b8a77b7d715 427 //pc.printf("Skip suppression ratio: %.2f\r\n", skip_sup_ratio);
wsquan171 1:8b8a77b7d715 428 unsigned char sup_buf[dst->cols], Gx_buf_array[3][dst->cols], Gy_buf_array[3][dst->cols];
wsquan171 1:8b8a77b7d715 429 for(i = mask_radius; i < src->rows - mask_radius; i++)
wsquan171 1:8b8a77b7d715 430 {
wsquan171 1:8b8a77b7d715 431 sup_buf[j] = 0; // in sup_matrix, 0 is default value
wsquan171 1:8b8a77b7d715 432 for(r = 0; r < 3; r++)
wsquan171 1:8b8a77b7d715 433 {
wsquan171 1:8b8a77b7d715 434 sdram.ReadData((i+r-1)*(Gx.cols) + SDRAM_DEVICE_ADDR + (Gx.data), buffer, Gx.cols/4);
wsquan171 1:8b8a77b7d715 435 for(j = 0; j < Gx.cols/4; j++)
wsquan171 1:8b8a77b7d715 436 {
wsquan171 1:8b8a77b7d715 437 Gx_buf_array[r][j*4+3] = buffer[j];
wsquan171 1:8b8a77b7d715 438 Gx_buf_array[r][j*4+2] = buffer[j] >> 8;
wsquan171 1:8b8a77b7d715 439 Gx_buf_array[r][j*4+1] = buffer[j] >> 16;
wsquan171 1:8b8a77b7d715 440 Gx_buf_array[r][j*4] = buffer[j] >> 24;
wsquan171 1:8b8a77b7d715 441 }
wsquan171 1:8b8a77b7d715 442 }
wsquan171 1:8b8a77b7d715 443 for(r = 0; r < 3; r++)
wsquan171 1:8b8a77b7d715 444 {
wsquan171 1:8b8a77b7d715 445 sdram.ReadData((i+r-1)*(Gy.cols) + SDRAM_DEVICE_ADDR + (Gy.data), buffer, Gy.cols/4);
wsquan171 1:8b8a77b7d715 446 for(j = 0; j < Gy.cols/4; j++)
wsquan171 1:8b8a77b7d715 447 {
wsquan171 1:8b8a77b7d715 448 Gy_buf_array[r][j*4+3] = buffer[j];
wsquan171 1:8b8a77b7d715 449 Gy_buf_array[r][j*4+2] = buffer[j] >> 8;
wsquan171 1:8b8a77b7d715 450 Gy_buf_array[r][j*4+1] = buffer[j] >> 16;
wsquan171 1:8b8a77b7d715 451 Gy_buf_array[r][j*4] = buffer[j] >> 24;
wsquan171 1:8b8a77b7d715 452 }
wsquan171 1:8b8a77b7d715 453 }
wsquan171 1:8b8a77b7d715 454
wsquan171 1:8b8a77b7d715 455 for(j = mask_radius; j < src->cols - mask_radius; j++)
wsquan171 1:8b8a77b7d715 456 {
wsquan171 1:8b8a77b7d715 457 if( j < mask_radius || (j >= src->rows - mask_radius))
wsquan171 1:8b8a77b7d715 458 {
wsquan171 1:8b8a77b7d715 459 sup_buf[j] = 1;
wsquan171 1:8b8a77b7d715 460 continue;
wsquan171 1:8b8a77b7d715 461 }
wsquan171 1:8b8a77b7d715 462 not_max_flag = 0;
wsquan171 1:8b8a77b7d715 463
wsquan171 1:8b8a77b7d715 464 // pixels with significant high magnitute will never be suppressed
wsquan171 1:8b8a77b7d715 465 if(Gx_buf_array[1][j] > 1.8 * max_threshold)
wsquan171 1:8b8a77b7d715 466 {
wsquan171 1:8b8a77b7d715 467 sup_buf[j] = 2; // in sup matrix, 2 means maxima's
wsquan171 1:8b8a77b7d715 468 continue;
wsquan171 1:8b8a77b7d715 469 }
wsquan171 1:8b8a77b7d715 470
wsquan171 1:8b8a77b7d715 471 // for other pixels, compare its gradient magnitute with its neighbor
wsquan171 1:8b8a77b7d715 472 for(r = -mask_radius; r <= mask_radius; r++)
wsquan171 1:8b8a77b7d715 473 {
wsquan171 1:8b8a77b7d715 474 if(not_max_flag == 1)
wsquan171 1:8b8a77b7d715 475 break;
wsquan171 1:8b8a77b7d715 476 for(c = -mask_radius; c <= mask_radius; c++)
wsquan171 1:8b8a77b7d715 477 {
wsquan171 1:8b8a77b7d715 478 vertical_neighbor = 0;
wsquan171 1:8b8a77b7d715 479 // only neighbors vertical to the gradient direction will be checked
wsquan171 1:8b8a77b7d715 480 switch (Gy_buf_array[1][j])
wsquan171 1:8b8a77b7d715 481 {
wsquan171 1:8b8a77b7d715 482 case 0:
wsquan171 1:8b8a77b7d715 483 if(c == 0 && (r == -1 || r ==1))
wsquan171 1:8b8a77b7d715 484 vertical_neighbor = 1;
wsquan171 1:8b8a77b7d715 485 break;
wsquan171 1:8b8a77b7d715 486 case 45:
wsquan171 1:8b8a77b7d715 487 if((r == 1 && c == -1) || (r == -1 && c == 1))
wsquan171 1:8b8a77b7d715 488 vertical_neighbor = 1;
wsquan171 1:8b8a77b7d715 489 break;
wsquan171 1:8b8a77b7d715 490 case 90:
wsquan171 1:8b8a77b7d715 491 if(r == 0 && (c == -1 || c == 1))
wsquan171 1:8b8a77b7d715 492 vertical_neighbor = 1;
wsquan171 1:8b8a77b7d715 493 case 135:
wsquan171 1:8b8a77b7d715 494 if((r == 1 && c == 1) || (r == -1 && c == -1))
wsquan171 1:8b8a77b7d715 495 vertical_neighbor = 1;
wsquan171 1:8b8a77b7d715 496 default:
wsquan171 1:8b8a77b7d715 497 break;
wsquan171 1:8b8a77b7d715 498 }
wsquan171 1:8b8a77b7d715 499 if(vertical_neighbor != 1)
wsquan171 1:8b8a77b7d715 500 continue;
wsquan171 1:8b8a77b7d715 501
wsquan171 1:8b8a77b7d715 502 if(Gy_buf_array[1][j] == Gy_buf_array[1+r][j+c])
wsquan171 1:8b8a77b7d715 503 {
wsquan171 1:8b8a77b7d715 504 if(Gx_buf_array[1][j] < Gx_buf_array[1+r][j+c])
wsquan171 1:8b8a77b7d715 505 {
wsquan171 1:8b8a77b7d715 506 not_max_flag = 1;
wsquan171 1:8b8a77b7d715 507 sup_buf[j] = 1;
wsquan171 1:8b8a77b7d715 508 break;
wsquan171 1:8b8a77b7d715 509 }
wsquan171 1:8b8a77b7d715 510 }
wsquan171 1:8b8a77b7d715 511 }
wsquan171 1:8b8a77b7d715 512 }
wsquan171 1:8b8a77b7d715 513 }
wsquan171 1:8b8a77b7d715 514
wsquan171 1:8b8a77b7d715 515 for(j = 0; j < sup_matrix.cols/4; j++)
wsquan171 1:8b8a77b7d715 516 {
wsquan171 1:8b8a77b7d715 517 buffer[j] = (16777216*sup_buf[j*4]) + (65536*sup_buf[j*4+1]) + (sup_buf[j*4+2]*256) + sup_buf[j*4+3];
wsquan171 1:8b8a77b7d715 518 }
wsquan171 1:8b8a77b7d715 519 sdram.WriteData(SDRAM_DEVICE_ADDR + sup_matrix.data + (i*sup_matrix.cols), buffer, sup_matrix.cols/4);
wsquan171 1:8b8a77b7d715 520 }
wsquan171 1:8b8a77b7d715 521
wsquan171 1:8b8a77b7d715 522 // Gy is no longer used
wsquan171 1:8b8a77b7d715 523
wsquan171 1:8b8a77b7d715 524 // Double threshold
wsquan171 1:8b8a77b7d715 525 pc.printf("Double threshold...\r\n");
wsquan171 1:8b8a77b7d715 526 unsigned char dst_buf[dst->cols];
wsquan171 1:8b8a77b7d715 527 int a = 0, b = 0, d = 0;
wsquan171 1:8b8a77b7d715 528 for(i = 0; i < src->rows; i++)
wsquan171 1:8b8a77b7d715 529 {
wsquan171 1:8b8a77b7d715 530 sdram.ReadData(i*(Gx.cols) + SDRAM_DEVICE_ADDR + (Gx.data), buffer, Gx.cols/4);
wsquan171 1:8b8a77b7d715 531 for(j = 0; j < Gx.cols/4; j++)
wsquan171 1:8b8a77b7d715 532 {
wsquan171 1:8b8a77b7d715 533 Gx_buf[j*4+3] = buffer[j];
wsquan171 1:8b8a77b7d715 534 Gx_buf[j*4+2] = buffer[j] >> 8;
wsquan171 1:8b8a77b7d715 535 Gx_buf[j*4+1] = buffer[j] >> 16;
wsquan171 1:8b8a77b7d715 536 Gx_buf[j*4] = buffer[j] >> 24;
wsquan171 1:8b8a77b7d715 537 }
wsquan171 1:8b8a77b7d715 538 sdram.ReadData(i*(sup_matrix.cols) + SDRAM_DEVICE_ADDR + (sup_matrix.data), buffer, sup_matrix.cols/4);
wsquan171 1:8b8a77b7d715 539 for(j = 0; j < Gx.cols/4; j++)
wsquan171 1:8b8a77b7d715 540 {
wsquan171 1:8b8a77b7d715 541 sup_buf[j*4+3] = buffer[j];
wsquan171 1:8b8a77b7d715 542 sup_buf[j*4+2] = buffer[j] >> 8;
wsquan171 1:8b8a77b7d715 543 sup_buf[j*4+1] = buffer[j] >> 16;
wsquan171 1:8b8a77b7d715 544 sup_buf[j*4] = buffer[j] >> 24;
wsquan171 1:8b8a77b7d715 545 }
wsquan171 1:8b8a77b7d715 546
wsquan171 1:8b8a77b7d715 547 for(j = 0; j < src->cols; j++)
wsquan171 1:8b8a77b7d715 548 {
wsquan171 1:8b8a77b7d715 549 if(i == 0 || j == 0 || i == src->rows-1 || j == src->cols-1)
wsquan171 1:8b8a77b7d715 550 {
wsquan171 1:8b8a77b7d715 551 dst_buf[j] = 0;
wsquan171 1:8b8a77b7d715 552 b++;
wsquan171 1:8b8a77b7d715 553 continue;
wsquan171 1:8b8a77b7d715 554 }
wsquan171 1:8b8a77b7d715 555 if(sup_buf[j] == 1)
wsquan171 1:8b8a77b7d715 556 Gx_buf[j] = 0;
wsquan171 1:8b8a77b7d715 557 if(Gx_buf[j] > max_threshold) // strong edge
wsquan171 1:8b8a77b7d715 558 {
wsquan171 1:8b8a77b7d715 559 dst_buf[j] = 255;
wsquan171 1:8b8a77b7d715 560 a++;
wsquan171 1:8b8a77b7d715 561 }
wsquan171 1:8b8a77b7d715 562 else if(Gx_buf[j] < min_threshold) // weak edge
wsquan171 1:8b8a77b7d715 563 {
wsquan171 1:8b8a77b7d715 564 dst_buf[j] = 0;
wsquan171 1:8b8a77b7d715 565 b++;
wsquan171 1:8b8a77b7d715 566 }
wsquan171 1:8b8a77b7d715 567 else // in middle: later check if it is connected to a strong edge
wsquan171 1:8b8a77b7d715 568 {
wsquan171 1:8b8a77b7d715 569 dst_buf[j] = Gx_buf[j];
wsquan171 1:8b8a77b7d715 570 d++;
wsquan171 1:8b8a77b7d715 571 }
wsquan171 1:8b8a77b7d715 572 }
wsquan171 1:8b8a77b7d715 573 for(j = 0; j < dst->cols/4; j++)
wsquan171 1:8b8a77b7d715 574 {
wsquan171 1:8b8a77b7d715 575 buffer[j] = (16777216*dst_buf[j*4]) + (65536*dst_buf[j*4+1]) + (dst_buf[j*4+2]*256) + dst_buf[j*4+3];
wsquan171 1:8b8a77b7d715 576 }
wsquan171 1:8b8a77b7d715 577 sdram.WriteData(SDRAM_DEVICE_ADDR + dst->data + (i*dst->cols), buffer, dst->cols/4);
wsquan171 1:8b8a77b7d715 578 }
wsquan171 1:8b8a77b7d715 579 pc.printf("strong = %d, weak = %d, middle = %d\r\n", a, b, d);
wsquan171 1:8b8a77b7d715 580
wsquan171 1:8b8a77b7d715 581 int k;
wsquan171 1:8b8a77b7d715 582 // Gx and sup_matrix is no longer used
wsquan171 1:8b8a77b7d715 583
wsquan171 1:8b8a77b7d715 584 // pixel which has magnitude in-middle will be preserved if connected to a strong edge
wsquan171 1:8b8a77b7d715 585 pc.printf("Edge tracking by hysteresis...\r\n");
wsquan171 1:8b8a77b7d715 586 short min_neighbor;
wsquan171 1:8b8a77b7d715 587 unsigned char dst_buf_array[3][dst->cols];
wsquan171 1:8b8a77b7d715 588 for(i = 0; i < 10; i++)
wsquan171 1:8b8a77b7d715 589 {
wsquan171 1:8b8a77b7d715 590 for(j = 1; j < dst->rows-1; j++)
wsquan171 1:8b8a77b7d715 591 {
wsquan171 1:8b8a77b7d715 592 for(r = 0; r < 3; r++)
wsquan171 1:8b8a77b7d715 593 {
wsquan171 1:8b8a77b7d715 594 sdram.ReadData((i+r-1)*(dst->cols) + SDRAM_DEVICE_ADDR + (dst->data), buffer, dst->cols/4);
wsquan171 1:8b8a77b7d715 595 for(k = 0; k < dst->cols/4; k++)
wsquan171 1:8b8a77b7d715 596 {
wsquan171 1:8b8a77b7d715 597 dst_buf_array[r][k*4+3] = buffer[k];
wsquan171 1:8b8a77b7d715 598 dst_buf_array[r][k*4+2] = buffer[k] >> 8;
wsquan171 1:8b8a77b7d715 599 dst_buf_array[r][k*4+1] = buffer[k] >> 16;
wsquan171 1:8b8a77b7d715 600 dst_buf_array[r][k*4] = buffer[k] >> 24;
wsquan171 1:8b8a77b7d715 601 }
wsquan171 1:8b8a77b7d715 602 }
wsquan171 1:8b8a77b7d715 603 for(k = 1; k < dst->cols - 1; k++)
wsquan171 1:8b8a77b7d715 604 {
wsquan171 1:8b8a77b7d715 605 if(dst_buf_array[1][j] == 0 || dst_buf_array[1][j] == 255)
wsquan171 1:8b8a77b7d715 606 continue;
wsquan171 1:8b8a77b7d715 607 min_neighbor = 0;
wsquan171 1:8b8a77b7d715 608 for(r = -1; r <= 1; r++)
wsquan171 1:8b8a77b7d715 609 {
wsquan171 1:8b8a77b7d715 610 for(c = -1; c <= 1; c++)
wsquan171 1:8b8a77b7d715 611 {
wsquan171 1:8b8a77b7d715 612 if(dst_buf_array[1+r][j+c] > max_threshold)
wsquan171 1:8b8a77b7d715 613 dst_buf_array[1][j] = 255;
wsquan171 1:8b8a77b7d715 614 else if(dst_buf_array[1+r][j+c] < min_threshold)
wsquan171 1:8b8a77b7d715 615 min_neighbor++;
wsquan171 1:8b8a77b7d715 616 }
wsquan171 1:8b8a77b7d715 617 }
wsquan171 1:8b8a77b7d715 618 if(min_neighbor == 8)
wsquan171 1:8b8a77b7d715 619 dst_buf_array[1][j] = 0;
wsquan171 1:8b8a77b7d715 620 }
wsquan171 1:8b8a77b7d715 621 for(k = 0; k < dst->cols/4; k++)
wsquan171 1:8b8a77b7d715 622 {
wsquan171 1:8b8a77b7d715 623 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];
wsquan171 1:8b8a77b7d715 624 }
wsquan171 1:8b8a77b7d715 625 sdram.WriteData(SDRAM_DEVICE_ADDR + dst->data + (i*dst->cols), buffer, dst->cols/4);
wsquan171 1:8b8a77b7d715 626 }
wsquan171 1:8b8a77b7d715 627 }
wsquan171 1:8b8a77b7d715 628
wsquan171 1:8b8a77b7d715 629 // those can't reach to a strong edge in 10 pixels will be supressed
wsquan171 1:8b8a77b7d715 630 for(i = 0; i < src->rows; i++)
wsquan171 1:8b8a77b7d715 631 {
wsquan171 1:8b8a77b7d715 632 sdram.ReadData((i)*(dst->cols) + SDRAM_DEVICE_ADDR + (dst->data), buffer, dst->cols/4);
wsquan171 1:8b8a77b7d715 633 for(k = 0; k < dst->cols/4; k++)
wsquan171 1:8b8a77b7d715 634 {
wsquan171 1:8b8a77b7d715 635 dst_buf[k*4+3] = buffer[k];
wsquan171 1:8b8a77b7d715 636 dst_buf[k*4+2] = buffer[k] >> 8;
wsquan171 1:8b8a77b7d715 637 dst_buf[k*4+1] = buffer[k] >> 16;
wsquan171 1:8b8a77b7d715 638 dst_buf[k*4] = buffer[k] >> 24;
wsquan171 1:8b8a77b7d715 639 }
wsquan171 1:8b8a77b7d715 640 for(j = 0; j < src->cols; j++)
wsquan171 1:8b8a77b7d715 641 {
wsquan171 1:8b8a77b7d715 642 if((dst_buf[j] == 255) || (dst_buf[j] == 0))
wsquan171 1:8b8a77b7d715 643 continue;
wsquan171 1:8b8a77b7d715 644 else
wsquan171 1:8b8a77b7d715 645 dst_buf[j] = 0;
wsquan171 1:8b8a77b7d715 646 }
wsquan171 1:8b8a77b7d715 647 for(j = 0; j < dst->cols/4; j++)
wsquan171 1:8b8a77b7d715 648 {
wsquan171 1:8b8a77b7d715 649 buffer[j] = (16777216*dst_buf[j*4]) + (65536*dst_buf[j*4+1]) + (dst_buf[j*4+2]*256) + dst_buf[j*4+3];
wsquan171 1:8b8a77b7d715 650 }
wsquan171 1:8b8a77b7d715 651 sdram.WriteData(SDRAM_DEVICE_ADDR + dst->data + (i*dst->cols), buffer, dst->cols/4);
wsquan171 1:8b8a77b7d715 652 }
wsquan171 1:8b8a77b7d715 653
wsquan171 1:8b8a77b7d715 654 int zero = 0, max = 0, in_middle = 0;
wsquan171 1:8b8a77b7d715 655 for(i = 0; i < src->rows; ++i)
wsquan171 1:8b8a77b7d715 656 {
wsquan171 1:8b8a77b7d715 657
wsquan171 1:8b8a77b7d715 658 sdram.ReadData((i)*(dst->cols) + SDRAM_DEVICE_ADDR + (dst->data), buffer, dst->cols/4);
wsquan171 1:8b8a77b7d715 659 for(k = 0; k < dst->cols/4; k++)
wsquan171 1:8b8a77b7d715 660 {
wsquan171 1:8b8a77b7d715 661 dst_buf[k*4+3] = buffer[k];
wsquan171 1:8b8a77b7d715 662 dst_buf[k*4+2] = buffer[k] >> 8;
wsquan171 1:8b8a77b7d715 663 dst_buf[k*4+1] = buffer[k] >> 16;
wsquan171 1:8b8a77b7d715 664 dst_buf[k*4] = buffer[k] >> 24;
wsquan171 1:8b8a77b7d715 665 }
wsquan171 1:8b8a77b7d715 666
wsquan171 1:8b8a77b7d715 667 for ( j = 0; j < src->cols; ++j)
wsquan171 1:8b8a77b7d715 668 {
wsquan171 1:8b8a77b7d715 669 if(dst_buf[j] == 0)
wsquan171 1:8b8a77b7d715 670 zero++;
wsquan171 1:8b8a77b7d715 671 else if(dst_buf[j] == 255)
wsquan171 1:8b8a77b7d715 672 max++;
wsquan171 1:8b8a77b7d715 673 else
wsquan171 1:8b8a77b7d715 674 in_middle++;
wsquan171 1:8b8a77b7d715 675 }
wsquan171 1:8b8a77b7d715 676 }
wsquan171 1:8b8a77b7d715 677 pc.printf("zero = %ld, max = %ld, in_middle = %ld\r\n", zero, max, in_middle);
wsquan171 1:8b8a77b7d715 678 pc.printf("SDRAM peak usage %ld\r\n", sup_matrix.data + (src->cols * src->rows) -1);
wsquan171 1:8b8a77b7d715 679
wsquan171 1:8b8a77b7d715 680 return;
wsquan171 1:8b8a77b7d715 681 }
grein028 0:d3bf1776d1c6 682
wsquan171 1:8b8a77b7d715 683 int myHoughLines(Image *src, line_polar *line_list, int threshold, int number)
wsquan171 1:8b8a77b7d715 684 {
wsquan171 1:8b8a77b7d715 685 // ref:
wsquan171 1:8b8a77b7d715 686 // http://www.keymolen.com/2013/05/hough-transformation-c-implementation.html
wsquan171 1:8b8a77b7d715 687 // https://en.wikipedia.org/wiki/Hough_transform
wsquan171 1:8b8a77b7d715 688
wsquan171 1:8b8a77b7d715 689 pc.printf("\r\nHoughLines Module Invoked\r\n");
wsquan171 1:8b8a77b7d715 690
wsquan171 1:8b8a77b7d715 691 // initialize the accumulator for lines. rho and theta are resolutions in distance and angle (all set to 1)
wsquan171 1:8b8a77b7d715 692 unsigned int dimension_r, dimension_t, i, j;
wsquan171 1:8b8a77b7d715 693 //if(src->channels != 1) // only accept grayscal images
wsquan171 1:8b8a77b7d715 694 // return -1;
wsquan171 1:8b8a77b7d715 695 if(src->rows >= src->cols)
wsquan171 1:8b8a77b7d715 696 dimension_r = src->rows;
wsquan171 1:8b8a77b7d715 697 else
wsquan171 1:8b8a77b7d715 698 dimension_r = src->cols;
wsquan171 1:8b8a77b7d715 699 dimension_r = (unsigned int) ((double)dimension_r * sqrt((double)2)) + 1;
wsquan171 1:8b8a77b7d715 700 dimension_t = 360;
wsquan171 1:8b8a77b7d715 701
wsquan171 1:8b8a77b7d715 702 uint32_t error_count = 0, addr = src->data + (src->rows * src->cols);
wsquan171 1:8b8a77b7d715 703 pc.printf("accumulator start at %ld\r\n", addr-1);
wsquan171 1:8b8a77b7d715 704 uint32_t *accumulator = (uint32_t*)malloc(dimension_t*sizeof(uint32_t));
wsquan171 1:8b8a77b7d715 705 for(i = 0; i < dimension_r; i++)
wsquan171 1:8b8a77b7d715 706 {
wsquan171 1:8b8a77b7d715 707 for(j = 0; j < dimension_t; j++)
wsquan171 1:8b8a77b7d715 708 accumulator[j] = 0;
wsquan171 1:8b8a77b7d715 709 sdram.WriteData(SDRAM_DEVICE_ADDR + (4*i*dimension_t) + addr, accumulator, dimension_t);
wsquan171 1:8b8a77b7d715 710 }
wsquan171 1:8b8a77b7d715 711 for(i = 0; i < dimension_r;i++)
wsquan171 1:8b8a77b7d715 712 {
wsquan171 1:8b8a77b7d715 713 sdram.ReadData(SDRAM_DEVICE_ADDR + (4*i*dimension_t) + addr, accumulator, dimension_t);
wsquan171 1:8b8a77b7d715 714 for(j = 0; j < dimension_t; j++)
wsquan171 1:8b8a77b7d715 715 {
wsquan171 1:8b8a77b7d715 716 if(accumulator[j] != 0)
wsquan171 1:8b8a77b7d715 717 {
wsquan171 1:8b8a77b7d715 718 error_count++;
wsquan171 1:8b8a77b7d715 719 }
wsquan171 1:8b8a77b7d715 720 }
wsquan171 1:8b8a77b7d715 721 }
wsquan171 1:8b8a77b7d715 722 pc.printf("error_count = %ld\r\n", error_count);
wsquan171 1:8b8a77b7d715 723
wsquan171 1:8b8a77b7d715 724 pc.printf("Accumulator dimension: %d(rho) by %d(theta)\r\n", dimension_r, dimension_t);
wsquan171 1:8b8a77b7d715 725
wsquan171 1:8b8a77b7d715 726 // scan through all edge pixels.
wsquan171 1:8b8a77b7d715 727 int t, neg_r = 0, round_up = 0, round_down = 0, pixel_count = 0;
wsquan171 1:8b8a77b7d715 728 double new_x, new_y, theta_rad, r;
wsquan171 1:8b8a77b7d715 729 unsigned char src_buf[src->cols];
wsquan171 1:8b8a77b7d715 730 uint32_t buffer[src->cols/4], tmp_accu;
wsquan171 1:8b8a77b7d715 731
wsquan171 1:8b8a77b7d715 732 for(i = 0; i < src->rows; i++)
wsquan171 1:8b8a77b7d715 733 {
wsquan171 1:8b8a77b7d715 734 sdram.ReadData(i*(src->cols) + SDRAM_DEVICE_ADDR + (src->data), buffer, src->cols/4);
wsquan171 1:8b8a77b7d715 735 for(j = 0; j < src->cols/4; j++)
wsquan171 1:8b8a77b7d715 736 {
wsquan171 1:8b8a77b7d715 737 src_buf[j*4+3] = buffer[j];
wsquan171 1:8b8a77b7d715 738 src_buf[j*4+2] = buffer[j] >> 8;
wsquan171 1:8b8a77b7d715 739 src_buf[j*4+1] = buffer[j] >> 16;
wsquan171 1:8b8a77b7d715 740 src_buf[j*4] = buffer[j] >> 24;
wsquan171 1:8b8a77b7d715 741 }
wsquan171 1:8b8a77b7d715 742 for(j = 0; j < src->cols; j++)
wsquan171 1:8b8a77b7d715 743 {
wsquan171 1:8b8a77b7d715 744 if(src_buf[j] != 255) // only consider edge pixels (who have value of 255)
wsquan171 1:8b8a77b7d715 745 continue;
wsquan171 1:8b8a77b7d715 746 pixel_count++;
wsquan171 1:8b8a77b7d715 747 for(t = 0; t < dimension_t; t++) // for each theta, calc the corresponding rho
wsquan171 1:8b8a77b7d715 748 {
wsquan171 1:8b8a77b7d715 749 new_x = (double)i;
wsquan171 1:8b8a77b7d715 750 new_y = (double)j;
wsquan171 1:8b8a77b7d715 751 theta_rad = (double)t * (M_PI / 180);
wsquan171 1:8b8a77b7d715 752 r = (new_y * cos(theta_rad)) + (new_x * sin(theta_rad));
wsquan171 1:8b8a77b7d715 753
wsquan171 1:8b8a77b7d715 754 if(r <= 0) // negative rho = positive rho, they are duplicated lines that can be discarded
wsquan171 1:8b8a77b7d715 755 {
wsquan171 1:8b8a77b7d715 756 neg_r++;
wsquan171 1:8b8a77b7d715 757 continue;
wsquan171 1:8b8a77b7d715 758 }
wsquan171 1:8b8a77b7d715 759
wsquan171 1:8b8a77b7d715 760 if( r - (int)r >= 0.5) // round up to the bin
wsquan171 1:8b8a77b7d715 761 {
wsquan171 1:8b8a77b7d715 762 round_up++;
wsquan171 1:8b8a77b7d715 763 sdram.ReadData(4*dimension_t*((int)r +1) + SDRAM_DEVICE_ADDR + (addr) + (4*t), &tmp_accu, 1);
wsquan171 1:8b8a77b7d715 764 tmp_accu++;
wsquan171 1:8b8a77b7d715 765 if(tmp_accu > 503640)
wsquan171 1:8b8a77b7d715 766 printf("(%d, %d)\r\n", (int)r +1, t);
wsquan171 1:8b8a77b7d715 767 sdram.WriteData(4*dimension_t*((int)r +1) + SDRAM_DEVICE_ADDR + (addr) + (4*t), &tmp_accu, 1);
wsquan171 1:8b8a77b7d715 768 }
wsquan171 1:8b8a77b7d715 769 else // round down to the bin
wsquan171 1:8b8a77b7d715 770 {
wsquan171 1:8b8a77b7d715 771 round_down++;
wsquan171 1:8b8a77b7d715 772 sdram.ReadData(4*dimension_t*((int)r) + SDRAM_DEVICE_ADDR + (addr) + (4*t), &tmp_accu, 1);
wsquan171 1:8b8a77b7d715 773 tmp_accu++;
wsquan171 1:8b8a77b7d715 774 sdram.WriteData(4*dimension_t*((int)r) + SDRAM_DEVICE_ADDR + (addr) + (4*t), &tmp_accu, 1);
wsquan171 1:8b8a77b7d715 775 }
wsquan171 1:8b8a77b7d715 776 }
wsquan171 1:8b8a77b7d715 777 }
wsquan171 1:8b8a77b7d715 778 j = src->rows / 10;
wsquan171 1:8b8a77b7d715 779 if(i%j == 0)
wsquan171 1:8b8a77b7d715 780 pc.printf("%d%%...",i/j*10);
wsquan171 1:8b8a77b7d715 781 }
wsquan171 1:8b8a77b7d715 782 pc.printf("\r\n");
wsquan171 1:8b8a77b7d715 783 pc.printf("negatve rho = %d\r\nround up = %d, round down = %d\r\n", neg_r, round_up, round_down);
wsquan171 1:8b8a77b7d715 784 pc.printf("pixel count = %d\r\n", pixel_count);
wsquan171 1:8b8a77b7d715 785 // find top x lines in rho-theta space
wsquan171 1:8b8a77b7d715 786 int weakest = 0, count = 0, weakest_pt = 0;
wsquan171 1:8b8a77b7d715 787 line_polar local_line_list[number];
wsquan171 1:8b8a77b7d715 788 for(i = 0; i < dimension_r; i++)
wsquan171 1:8b8a77b7d715 789 {
wsquan171 1:8b8a77b7d715 790 sdram.ReadData(4*i*dimension_t + SDRAM_DEVICE_ADDR + (addr), accumulator, dimension_t);
wsquan171 1:8b8a77b7d715 791 for(j = 0; j < dimension_t; j++)
wsquan171 1:8b8a77b7d715 792 {
wsquan171 1:8b8a77b7d715 793 if(accumulator[j] < threshold || accumulator[j] <= weakest)
wsquan171 1:8b8a77b7d715 794 continue;
wsquan171 1:8b8a77b7d715 795
wsquan171 1:8b8a77b7d715 796 local_line_list[weakest_pt].rho = i;
wsquan171 1:8b8a77b7d715 797 local_line_list[weakest_pt].theta = j;
wsquan171 1:8b8a77b7d715 798 sdram.ReadData(4*dimension_t*i + SDRAM_DEVICE_ADDR + (addr) + (4*j), &tmp_accu, 1);
wsquan171 1:8b8a77b7d715 799 local_line_list[weakest_pt].strength = tmp_accu;
wsquan171 1:8b8a77b7d715 800 count++;
wsquan171 1:8b8a77b7d715 801
wsquan171 1:8b8a77b7d715 802 if(count < number)
wsquan171 1:8b8a77b7d715 803 {
wsquan171 1:8b8a77b7d715 804 weakest_pt++;
wsquan171 1:8b8a77b7d715 805 weakest = 0;
wsquan171 1:8b8a77b7d715 806 }
wsquan171 1:8b8a77b7d715 807 else
wsquan171 1:8b8a77b7d715 808 {
wsquan171 1:8b8a77b7d715 809 weakest = local_line_list[0].strength;
wsquan171 1:8b8a77b7d715 810 weakest_pt = 0;
wsquan171 1:8b8a77b7d715 811 for(t = 1; t < number; t++)
wsquan171 1:8b8a77b7d715 812 {
wsquan171 1:8b8a77b7d715 813 if(local_line_list[t].strength < weakest)
wsquan171 1:8b8a77b7d715 814 {
wsquan171 1:8b8a77b7d715 815 weakest = local_line_list[t].strength;
wsquan171 1:8b8a77b7d715 816 weakest_pt = t;
wsquan171 1:8b8a77b7d715 817 }
wsquan171 1:8b8a77b7d715 818 }
wsquan171 1:8b8a77b7d715 819 }
wsquan171 1:8b8a77b7d715 820 }
wsquan171 1:8b8a77b7d715 821 }
wsquan171 1:8b8a77b7d715 822
wsquan171 1:8b8a77b7d715 823 // copy those found lines baack to main's stack
wsquan171 1:8b8a77b7d715 824 memcpy(line_list, local_line_list, number*sizeof(line_polar));
wsquan171 1:8b8a77b7d715 825
wsquan171 1:8b8a77b7d715 826 for(i = 0; i < number; i++)
wsquan171 1:8b8a77b7d715 827 {
wsquan171 1:8b8a77b7d715 828 if(count == i)
wsquan171 1:8b8a77b7d715 829 break;
wsquan171 1:8b8a77b7d715 830 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);
wsquan171 1:8b8a77b7d715 831 }
wsquan171 1:8b8a77b7d715 832 printf("\r\n");
wsquan171 1:8b8a77b7d715 833 free(accumulator);
wsquan171 1:8b8a77b7d715 834 // return the actual number of lines found above threshold
wsquan171 1:8b8a77b7d715 835 return count;
wsquan171 1:8b8a77b7d715 836 }
wsquan171 1:8b8a77b7d715 837
wsquan171 1:8b8a77b7d715 838 int det(ptvec a, ptvec b)
wsquan171 1:8b8a77b7d715 839 {
wsquan171 1:8b8a77b7d715 840 return (a.x * b.y) - (a.y * b.x);
wsquan171 1:8b8a77b7d715 841 }
wsquan171 1:8b8a77b7d715 842
wsquan171 1:8b8a77b7d715 843 int line_intersection(trans_line line1, trans_line line2, ptvec *result)
wsquan171 1:8b8a77b7d715 844 {
wsquan171 1:8b8a77b7d715 845 ptvec xdiff, ydiff, d;
wsquan171 1:8b8a77b7d715 846 xdiff.x = line1.ptvec1.x - line1.ptvec2.x;
wsquan171 1:8b8a77b7d715 847 xdiff.y = line2.ptvec1.x - line2.ptvec2.x;
wsquan171 1:8b8a77b7d715 848 ydiff.x = line1.ptvec1.y - line1.ptvec2.y;
wsquan171 1:8b8a77b7d715 849 ydiff.y = line2.ptvec1.y - line2.ptvec2.y;
wsquan171 1:8b8a77b7d715 850
wsquan171 1:8b8a77b7d715 851 int div = det(xdiff, ydiff);
wsquan171 1:8b8a77b7d715 852 if(div == 0)
wsquan171 1:8b8a77b7d715 853 {
wsquan171 1:8b8a77b7d715 854 // Lines don't intersect
wsquan171 1:8b8a77b7d715 855 return 0;
wsquan171 1:8b8a77b7d715 856 }
wsquan171 1:8b8a77b7d715 857
wsquan171 1:8b8a77b7d715 858 d.x = det(line1.ptvec1, line1.ptvec2);
wsquan171 1:8b8a77b7d715 859 d.y = det(line2.ptvec1, line2.ptvec2);
wsquan171 1:8b8a77b7d715 860 result->x = (int) det(d, xdiff) / div;
wsquan171 1:8b8a77b7d715 861 result->y = (int) det(d, ydiff) / div;
wsquan171 1:8b8a77b7d715 862 return 1;
wsquan171 1:8b8a77b7d715 863 }
wsquan171 1:8b8a77b7d715 864
wsquan171 1:8b8a77b7d715 865 double LineAnalysis(trans_line *line_list, int line_count, int dim_x, int dim_y)
wsquan171 1:8b8a77b7d715 866 {
wsquan171 1:8b8a77b7d715 867 printf("Needle Position Detection Module Invoked.\r\n");
wsquan171 1:8b8a77b7d715 868 int strongest_pt = 0, i = 0, j = 0, upper_count = 0, lower_count = 0;
wsquan171 1:8b8a77b7d715 869 int upper_bin[line_count];
wsquan171 1:8b8a77b7d715 870 int lower_bin[line_count];
wsquan171 1:8b8a77b7d715 871 double average_deg = 0, diff, theta_deg;
wsquan171 1:8b8a77b7d715 872
wsquan171 1:8b8a77b7d715 873 // find the line with highest confidence
wsquan171 1:8b8a77b7d715 874 for(i = 0; i < line_count; i++)
wsquan171 1:8b8a77b7d715 875 {
wsquan171 1:8b8a77b7d715 876 if(line_list[i].strength > line_list[strongest_pt].strength)
wsquan171 1:8b8a77b7d715 877 strongest_pt = i;
wsquan171 1:8b8a77b7d715 878 }
wsquan171 1:8b8a77b7d715 879 printf("Line used as reference of de-noise: rho = %d, ", (int)line_list[strongest_pt].rho);
wsquan171 1:8b8a77b7d715 880 printf("theta = %d\r\n", (int) (line_list[strongest_pt].theta * 180 / M_PI));
wsquan171 1:8b8a77b7d715 881
wsquan171 1:8b8a77b7d715 882 // delete lines whose theta varies > 10 deg from the strongest line
wsquan171 1:8b8a77b7d715 883 for(i = 0; i < line_count; i++)
wsquan171 1:8b8a77b7d715 884 {
wsquan171 1:8b8a77b7d715 885 diff = 180 * line_list[strongest_pt].theta / M_PI;
wsquan171 1:8b8a77b7d715 886 diff = diff - (180 * line_list[i].theta / M_PI);
wsquan171 1:8b8a77b7d715 887 if(diff >= 10 || diff <= -10)
wsquan171 1:8b8a77b7d715 888 {
wsquan171 1:8b8a77b7d715 889 printf("Line with confidence of %d is suppressed.\r\n", line_list[i].strength);
wsquan171 1:8b8a77b7d715 890 line_list[i].strength = 0;
wsquan171 1:8b8a77b7d715 891 continue;
wsquan171 1:8b8a77b7d715 892 }
wsquan171 1:8b8a77b7d715 893 average_deg = (180 * line_list[i].theta / M_PI) + average_deg;
wsquan171 1:8b8a77b7d715 894 j++;
wsquan171 1:8b8a77b7d715 895 }
wsquan171 1:8b8a77b7d715 896 average_deg = average_deg / j;
wsquan171 1:8b8a77b7d715 897 printf("Theta used to divide lines into bins: %.2f\r\n", average_deg);
wsquan171 1:8b8a77b7d715 898
wsquan171 1:8b8a77b7d715 899 // categorize all lines into 2 bins depends on whether its theta is higher than average.
wsquan171 1:8b8a77b7d715 900 for(i = 0; i < line_count; i++)
wsquan171 1:8b8a77b7d715 901 {
wsquan171 1:8b8a77b7d715 902 if(line_list[i].strength == 0)
wsquan171 1:8b8a77b7d715 903 continue;
wsquan171 1:8b8a77b7d715 904
wsquan171 1:8b8a77b7d715 905 theta_deg = line_list[i].theta * 180 / M_PI;
wsquan171 1:8b8a77b7d715 906 if(theta_deg > average_deg)
wsquan171 1:8b8a77b7d715 907 {
wsquan171 1:8b8a77b7d715 908 upper_bin[upper_count] = i;
wsquan171 1:8b8a77b7d715 909 upper_count++;
wsquan171 1:8b8a77b7d715 910 }
wsquan171 1:8b8a77b7d715 911 else
wsquan171 1:8b8a77b7d715 912 {
wsquan171 1:8b8a77b7d715 913 lower_bin[lower_count] = i;
wsquan171 1:8b8a77b7d715 914 lower_count++;
wsquan171 1:8b8a77b7d715 915 }
wsquan171 1:8b8a77b7d715 916 }
wsquan171 1:8b8a77b7d715 917
wsquan171 1:8b8a77b7d715 918 // choose the bin with minority of lines.
wsquan171 1:8b8a77b7d715 919 // find a line in the bin with highest / lowest theta if we chose upper / lower bin
wsquan171 1:8b8a77b7d715 920 // the chosen line is considered as the line mostly diverted from the other lines but yet near needle
wsquan171 1:8b8a77b7d715 921 // find all intersections between this line and the lines in the bin with majority of lines
wsquan171 1:8b8a77b7d715 922 // since this line is well seperated from others, points found should be far enough from gauge center
wsquan171 1:8b8a77b7d715 923 int ref_pt = 0, inter_count = 0;
wsquan171 1:8b8a77b7d715 924 ptvec intersect_list[line_count];
wsquan171 1:8b8a77b7d715 925 if(lower_count < upper_count)
wsquan171 1:8b8a77b7d715 926 {
wsquan171 1:8b8a77b7d715 927 for(i = 0; i < lower_count; i++)
wsquan171 1:8b8a77b7d715 928 {
wsquan171 1:8b8a77b7d715 929 if(line_list[lower_bin[i]].theta < line_list[lower_bin[ref_pt]].theta)
wsquan171 1:8b8a77b7d715 930 ref_pt = i;
wsquan171 1:8b8a77b7d715 931 }
wsquan171 1:8b8a77b7d715 932 average_deg = (180 * line_list[lower_bin[ref_pt]].theta / M_PI);
wsquan171 1:8b8a77b7d715 933 printf("Line in lower_bin is used to find intersections:\r\n");
wsquan171 1:8b8a77b7d715 934 printf("rho = %d, ", (int) (line_list[lower_bin[ref_pt]].rho));
wsquan171 1:8b8a77b7d715 935 printf("theta = %d ", (int) (line_list[lower_bin[ref_pt]].theta * 180 / (M_PI)));
wsquan171 1:8b8a77b7d715 936 printf("strength = %d\r\n", (int) (line_list[lower_bin[ref_pt]].strength));
wsquan171 1:8b8a77b7d715 937 for(i = 0; i < upper_count; i++)
wsquan171 1:8b8a77b7d715 938 {
wsquan171 1:8b8a77b7d715 939 if(line_intersection(line_list[lower_bin[ref_pt]], line_list[upper_bin[i]], &intersect_list[inter_count]) == 1)
wsquan171 1:8b8a77b7d715 940 inter_count++;
wsquan171 1:8b8a77b7d715 941 }
wsquan171 1:8b8a77b7d715 942 ref_pt = 0;
wsquan171 1:8b8a77b7d715 943 for(i = 0; i < upper_count; i++)
wsquan171 1:8b8a77b7d715 944 {
wsquan171 1:8b8a77b7d715 945 if(line_list[upper_bin[i]].theta > line_list[upper_bin[ref_pt]].theta)
wsquan171 1:8b8a77b7d715 946 ref_pt = i;
wsquan171 1:8b8a77b7d715 947 }
wsquan171 1:8b8a77b7d715 948 average_deg = (180 * line_list[upper_bin[ref_pt]].theta / M_PI) + average_deg;
wsquan171 1:8b8a77b7d715 949 average_deg = average_deg / 2;
wsquan171 1:8b8a77b7d715 950 }
wsquan171 1:8b8a77b7d715 951 else
wsquan171 1:8b8a77b7d715 952 {
wsquan171 1:8b8a77b7d715 953 for(i = 0; i < upper_count; i++)
wsquan171 1:8b8a77b7d715 954 {
wsquan171 1:8b8a77b7d715 955 if(line_list[upper_bin[i]].theta > line_list[upper_bin[ref_pt]].theta)
wsquan171 1:8b8a77b7d715 956 ref_pt = i;
wsquan171 1:8b8a77b7d715 957 }
wsquan171 1:8b8a77b7d715 958 average_deg = (180 * line_list[upper_bin[ref_pt]].theta / M_PI);
wsquan171 1:8b8a77b7d715 959 printf("Line in upper_bin is used to find intersections:\r\n");
wsquan171 1:8b8a77b7d715 960 printf("rho = %d, ", (int) (line_list[upper_bin[ref_pt]].rho));
wsquan171 1:8b8a77b7d715 961 printf("theta = %d ", (int) (line_list[upper_bin[ref_pt]].theta * 180 / (M_PI)));
wsquan171 1:8b8a77b7d715 962 printf("strength = %d\r\n", (int) (line_list[upper_bin[ref_pt]].strength));
wsquan171 1:8b8a77b7d715 963 for(i = 0; i < lower_count; i++)
wsquan171 1:8b8a77b7d715 964 {
wsquan171 1:8b8a77b7d715 965 if(line_intersection(line_list[upper_bin[ref_pt]], line_list[lower_bin[i]], &intersect_list[inter_count]) == 1)
wsquan171 1:8b8a77b7d715 966 inter_count++;
wsquan171 1:8b8a77b7d715 967 }
wsquan171 1:8b8a77b7d715 968 ref_pt = 0;
wsquan171 1:8b8a77b7d715 969 for(i = 0; i < lower_count; i++)
wsquan171 1:8b8a77b7d715 970 {
wsquan171 1:8b8a77b7d715 971 if(line_list[lower_bin[i]].theta < line_list[lower_bin[ref_pt]].theta)
wsquan171 1:8b8a77b7d715 972 ref_pt = i;
wsquan171 1:8b8a77b7d715 973 }
wsquan171 1:8b8a77b7d715 974 average_deg = (180 * line_list[lower_bin[ref_pt]].theta / M_PI) + average_deg;
wsquan171 1:8b8a77b7d715 975 average_deg = average_deg / 2;
wsquan171 1:8b8a77b7d715 976 }
wsquan171 1:8b8a77b7d715 977
wsquan171 1:8b8a77b7d715 978 // categorize all intersections points into 4 bins
wsquan171 1:8b8a77b7d715 979 // ----------------> x
wsquan171 1:8b8a77b7d715 980 // | |
wsquan171 1:8b8a77b7d715 981 // | III | IV
wsquan171 1:8b8a77b7d715 982 // | |
wsquan171 1:8b8a77b7d715 983 // |---------------
wsquan171 1:8b8a77b7d715 984 // | |
wsquan171 1:8b8a77b7d715 985 // | II | I
wsquan171 1:8b8a77b7d715 986 // v |
wsquan171 1:8b8a77b7d715 987 // y
wsquan171 1:8b8a77b7d715 988 // the reason is that the center of the pic should be close to the center of the gauge.
wsquan171 1:8b8a77b7d715 989 // the angle of the needle is already detected. we don't need actually find the center
wsquan171 1:8b8a77b7d715 990 // to tell the direction of the needle. the distribution of intersections can provide
wsquan171 1:8b8a77b7d715 991 // enough info to do the desicion.
wsquan171 1:8b8a77b7d715 992
wsquan171 1:8b8a77b7d715 993 int cor_count[4] = {0, 0, 0, 0};
wsquan171 1:8b8a77b7d715 994 int var_x = 0, var_y = 0;
wsquan171 1:8b8a77b7d715 995 printf("Intersections:\r\n");
wsquan171 1:8b8a77b7d715 996 for(i = 0; i < inter_count; i++)
wsquan171 1:8b8a77b7d715 997 {
wsquan171 1:8b8a77b7d715 998 if(intersect_list[i].x > dim_x /2)
wsquan171 1:8b8a77b7d715 999 {
wsquan171 1:8b8a77b7d715 1000 if(intersect_list[i].y > dim_y/2)
wsquan171 1:8b8a77b7d715 1001 cor_count[0]++;
wsquan171 1:8b8a77b7d715 1002 else
wsquan171 1:8b8a77b7d715 1003 cor_count[3]++;
wsquan171 1:8b8a77b7d715 1004 }
wsquan171 1:8b8a77b7d715 1005 else
wsquan171 1:8b8a77b7d715 1006 {
wsquan171 1:8b8a77b7d715 1007 if(intersect_list[i].y > dim_y/2)
wsquan171 1:8b8a77b7d715 1008 cor_count[1]++;
wsquan171 1:8b8a77b7d715 1009 else
wsquan171 1:8b8a77b7d715 1010 cor_count[2]++;
wsquan171 1:8b8a77b7d715 1011 }
wsquan171 1:8b8a77b7d715 1012 var_x = var_x + abs(intersect_list[i].x - dim_x/2);
wsquan171 1:8b8a77b7d715 1013 var_y = var_y + abs(intersect_list[i].y - dim_y/2);
wsquan171 1:8b8a77b7d715 1014 printf("(%d, %d)\r\n", intersect_list[i].x, intersect_list[i].y);
wsquan171 1:8b8a77b7d715 1015 }
wsquan171 1:8b8a77b7d715 1016 printf("Variation in x: %d, in y: %d\r\n", var_x, var_y);
wsquan171 1:8b8a77b7d715 1017
wsquan171 1:8b8a77b7d715 1018 int most_cor_pt = 0;
wsquan171 1:8b8a77b7d715 1019 printf("Intersection points distribution: \r\n");
wsquan171 1:8b8a77b7d715 1020 for(i = 0; i < 4; i++)
wsquan171 1:8b8a77b7d715 1021 {
wsquan171 1:8b8a77b7d715 1022 if(cor_count[i] > cor_count[most_cor_pt])
wsquan171 1:8b8a77b7d715 1023 most_cor_pt = i;
wsquan171 1:8b8a77b7d715 1024 printf("Cor %d: %d\r\n", i+1, cor_count[i]);
wsquan171 1:8b8a77b7d715 1025 }
wsquan171 1:8b8a77b7d715 1026
wsquan171 1:8b8a77b7d715 1027 int correct_vote = 0, revert_vote = 0;
wsquan171 1:8b8a77b7d715 1028 if(average_deg >= 0 && average_deg < 90) // intersections are expected to be in II or IV
wsquan171 1:8b8a77b7d715 1029 {
wsquan171 1:8b8a77b7d715 1030 if(var_x < var_y) // variance in x is smaller, I+II should > III+IV
wsquan171 1:8b8a77b7d715 1031 {
wsquan171 1:8b8a77b7d715 1032 if((cor_count[0]+cor_count[1]) > (cor_count[2]+cor_count[3]))
wsquan171 1:8b8a77b7d715 1033 correct_vote++;
wsquan171 1:8b8a77b7d715 1034 else
wsquan171 1:8b8a77b7d715 1035 revert_vote++;
wsquan171 1:8b8a77b7d715 1036 }
wsquan171 1:8b8a77b7d715 1037 else // variance in y is smaller, II+III should > IV+I
wsquan171 1:8b8a77b7d715 1038 {
wsquan171 1:8b8a77b7d715 1039 if((cor_count[1]+cor_count[2]) > (cor_count[3]+cor_count[0]))
wsquan171 1:8b8a77b7d715 1040 correct_vote++;
wsquan171 1:8b8a77b7d715 1041 else
wsquan171 1:8b8a77b7d715 1042 revert_vote++;
wsquan171 1:8b8a77b7d715 1043 }
wsquan171 1:8b8a77b7d715 1044 if(correct_vote > revert_vote)
wsquan171 1:8b8a77b7d715 1045 printf("Original detected angle is correct.\r\n");
wsquan171 1:8b8a77b7d715 1046 else
wsquan171 1:8b8a77b7d715 1047 {
wsquan171 1:8b8a77b7d715 1048 average_deg = average_deg + 180;
wsquan171 1:8b8a77b7d715 1049 printf("Reverted angle is correct.\r\n");
wsquan171 1:8b8a77b7d715 1050 }
wsquan171 1:8b8a77b7d715 1051 }
wsquan171 1:8b8a77b7d715 1052 else if(average_deg >= 90 && average_deg < 180) // intersections are expected to be in III or I
wsquan171 1:8b8a77b7d715 1053 {
wsquan171 1:8b8a77b7d715 1054 if(var_x < var_y) // variance in x is smaller, I+II should < III+IV
wsquan171 1:8b8a77b7d715 1055 {
wsquan171 1:8b8a77b7d715 1056 if((cor_count[0]+cor_count[1]) < (cor_count[2]+cor_count[3]))
wsquan171 1:8b8a77b7d715 1057 correct_vote++;
wsquan171 1:8b8a77b7d715 1058 else
wsquan171 1:8b8a77b7d715 1059 revert_vote++;
wsquan171 1:8b8a77b7d715 1060 }
wsquan171 1:8b8a77b7d715 1061 else // variance in y is smaller, II+III should > IV+I
wsquan171 1:8b8a77b7d715 1062 {
wsquan171 1:8b8a77b7d715 1063 if((cor_count[1]+cor_count[2]) > (cor_count[3]+cor_count[0]))
wsquan171 1:8b8a77b7d715 1064 correct_vote++;
wsquan171 1:8b8a77b7d715 1065 else
wsquan171 1:8b8a77b7d715 1066 revert_vote++;
wsquan171 1:8b8a77b7d715 1067 }
wsquan171 1:8b8a77b7d715 1068 if(correct_vote > revert_vote)
wsquan171 1:8b8a77b7d715 1069 printf("Original detected angle is correct.\r\n");
wsquan171 1:8b8a77b7d715 1070 else
wsquan171 1:8b8a77b7d715 1071 {
wsquan171 1:8b8a77b7d715 1072 average_deg = average_deg + 180;
wsquan171 1:8b8a77b7d715 1073 printf("Reverted angle is correct.\r\n");
wsquan171 1:8b8a77b7d715 1074 }
wsquan171 1:8b8a77b7d715 1075 }
wsquan171 1:8b8a77b7d715 1076 else if(average_deg >= 180 && average_deg < 270) // intersections are expected to be in II or IV
wsquan171 1:8b8a77b7d715 1077 {
wsquan171 1:8b8a77b7d715 1078 if(var_x < var_y) // variance in x is smaller, I+II should < III+IV
wsquan171 1:8b8a77b7d715 1079 {
wsquan171 1:8b8a77b7d715 1080 if((cor_count[0]+cor_count[1]) < (cor_count[2]+cor_count[3]))
wsquan171 1:8b8a77b7d715 1081 correct_vote++;
wsquan171 1:8b8a77b7d715 1082 else
wsquan171 1:8b8a77b7d715 1083 revert_vote++;
wsquan171 1:8b8a77b7d715 1084 }
wsquan171 1:8b8a77b7d715 1085 else // variance in y is smaller, II+III should < IV+I
wsquan171 1:8b8a77b7d715 1086 {
wsquan171 1:8b8a77b7d715 1087 if((cor_count[1]+cor_count[2]) < (cor_count[3]+cor_count[0]))
wsquan171 1:8b8a77b7d715 1088 correct_vote++;
wsquan171 1:8b8a77b7d715 1089 else
wsquan171 1:8b8a77b7d715 1090 revert_vote++;
wsquan171 1:8b8a77b7d715 1091 }
wsquan171 1:8b8a77b7d715 1092 if(correct_vote > revert_vote)
wsquan171 1:8b8a77b7d715 1093 printf("Original detected angle is correct.\r\n");
wsquan171 1:8b8a77b7d715 1094 else
wsquan171 1:8b8a77b7d715 1095 {
wsquan171 1:8b8a77b7d715 1096 average_deg = average_deg - 180;
wsquan171 1:8b8a77b7d715 1097 printf("Reverted angle is correct.\r\n");
wsquan171 1:8b8a77b7d715 1098 }
wsquan171 1:8b8a77b7d715 1099 }
wsquan171 1:8b8a77b7d715 1100 else if(average_deg >= 270 && average_deg < 360) // intersections are expected to be in III or I
wsquan171 1:8b8a77b7d715 1101 {
wsquan171 1:8b8a77b7d715 1102 if(var_x < var_y) // variance in x is smaller, I+II should > III+IV
wsquan171 1:8b8a77b7d715 1103 {
wsquan171 1:8b8a77b7d715 1104 if((cor_count[0]+cor_count[1]) > (cor_count[2]+cor_count[3]))
wsquan171 1:8b8a77b7d715 1105 correct_vote++;
wsquan171 1:8b8a77b7d715 1106 else
wsquan171 1:8b8a77b7d715 1107 revert_vote++;
wsquan171 1:8b8a77b7d715 1108 }
wsquan171 1:8b8a77b7d715 1109 else // variance in y is smaller, II+III should < IV+I
wsquan171 1:8b8a77b7d715 1110 {
wsquan171 1:8b8a77b7d715 1111 if((cor_count[1]+cor_count[2]) < (cor_count[3]+cor_count[0]))
wsquan171 1:8b8a77b7d715 1112 correct_vote++;
wsquan171 1:8b8a77b7d715 1113 else
wsquan171 1:8b8a77b7d715 1114 revert_vote++;
wsquan171 1:8b8a77b7d715 1115 }
wsquan171 1:8b8a77b7d715 1116 if(correct_vote > revert_vote)
wsquan171 1:8b8a77b7d715 1117 printf("Original detected angle is correct.\r\n");
wsquan171 1:8b8a77b7d715 1118 else
wsquan171 1:8b8a77b7d715 1119 {
wsquan171 1:8b8a77b7d715 1120 average_deg = average_deg - 180;
wsquan171 1:8b8a77b7d715 1121 printf("Reverted angle is correct.\r\n");
wsquan171 1:8b8a77b7d715 1122 }
wsquan171 1:8b8a77b7d715 1123 }
wsquan171 1:8b8a77b7d715 1124 else
wsquan171 1:8b8a77b7d715 1125 {
wsquan171 1:8b8a77b7d715 1126 printf("Detection failed. The result may not be correct.\r\n");
wsquan171 1:8b8a77b7d715 1127 }
wsquan171 1:8b8a77b7d715 1128 return average_deg;
wsquan171 1:8b8a77b7d715 1129 }
wsquan171 1:8b8a77b7d715 1130
wsquan171 1:8b8a77b7d715 1131
wsquan171 1:8b8a77b7d715 1132 int main(void)
wsquan171 1:8b8a77b7d715 1133 {
wsquan171 1:8b8a77b7d715 1134 pc.baud(115200);
wsquan171 1:8b8a77b7d715 1135 int i, j;
wsquan171 1:8b8a77b7d715 1136 led_red = 0;
wsquan171 1:8b8a77b7d715 1137 led_green = 0;
wsquan171 1:8b8a77b7d715 1138
wsquan171 1:8b8a77b7d715 1139 FMC_SDRAM_CommandTypeDef SDRAMCommandStructure;
grein028 0:d3bf1776d1c6 1140 SDRAMCommandStructure.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;
wsquan171 1:8b8a77b7d715 1141
wsquan171 1:8b8a77b7d715 1142 pc.printf("\r\nSDRAM demo started\r\n");
wsquan171 1:8b8a77b7d715 1143
wsquan171 1:8b8a77b7d715 1144 Image src, dst, bin;
wsquan171 1:8b8a77b7d715 1145 if (bmpread("/sd/1.bmp", &src) == 0)
wsquan171 1:8b8a77b7d715 1146 return 0;
wsquan171 1:8b8a77b7d715 1147
wsquan171 1:8b8a77b7d715 1148 // smooth the picture using GaussianBlur: void GaussianBlur(Image *src, Image *dst, int width, double sigma)
wsquan171 1:8b8a77b7d715 1149 myGaussianBlur(&src, &dst, 5, 1.5);
wsquan171 1:8b8a77b7d715 1150 myCanny(&dst, &bin, 20, 60);
wsquan171 1:8b8a77b7d715 1151 line_polar my_line_list[10];
wsquan171 1:8b8a77b7d715 1152 int line_count;
wsquan171 1:8b8a77b7d715 1153 line_count = myHoughLines(&bin, my_line_list, 40, line_limit);
wsquan171 1:8b8a77b7d715 1154 if(line_count >= line_limit)
wsquan171 1:8b8a77b7d715 1155 line_count = line_limit;
wsquan171 1:8b8a77b7d715 1156
wsquan171 1:8b8a77b7d715 1157 trans_line line_list[line_limit];
wsquan171 1:8b8a77b7d715 1158 unsigned int m = src.rows, n = src.cols;
wsquan171 1:8b8a77b7d715 1159 for(i = 0; i < line_count; i++) // combined for loops at #64, #73, #83, #92, #101
grein028 0:d3bf1776d1c6 1160 {
wsquan171 1:8b8a77b7d715 1161 line_list[i].rho = my_line_list[i].rho;
wsquan171 1:8b8a77b7d715 1162 line_list[i].theta = my_line_list[i].theta * (M_PI / 180);
wsquan171 1:8b8a77b7d715 1163 line_list[i].strength = my_line_list[i].strength;
wsquan171 1:8b8a77b7d715 1164 line_list[i].a = cos(line_list[i].theta);
wsquan171 1:8b8a77b7d715 1165 line_list[i].b = sin(line_list[i].theta);
wsquan171 1:8b8a77b7d715 1166 line_list[i].x0 = line_list[i].a * line_list[i].rho;
wsquan171 1:8b8a77b7d715 1167 line_list[i].y0 = line_list[i].b * line_list[i].rho;
wsquan171 1:8b8a77b7d715 1168 line_list[i].ptvec1.x = Round(line_list[i].x0 + (m+n)*(-line_list[i].b));
wsquan171 1:8b8a77b7d715 1169 line_list[i].ptvec1.y = Round(line_list[i].y0 + (m+n)*line_list[i].a);
wsquan171 1:8b8a77b7d715 1170 line_list[i].ptvec2.x = Round(line_list[i].x0 - (m+n)*(-line_list[i].b));
wsquan171 1:8b8a77b7d715 1171 line_list[i].ptvec2.y = Round(line_list[i].y0 - (m+n)*line_list[i].a);
grein028 0:d3bf1776d1c6 1172 }
grein028 0:d3bf1776d1c6 1173
wsquan171 1:8b8a77b7d715 1174 double angle = LineAnalysis(line_list, line_count, src.cols, src.rows);
wsquan171 1:8b8a77b7d715 1175 double value = (angle - 42) / 270 * 50;
wsquan171 1:8b8a77b7d715 1176 pc.printf("Detected angle = %.2f\r\nEstimated value = %.2f\r\n", angle, value);
grein028 0:d3bf1776d1c6 1177 }