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

Revision:
1:8b8a77b7d715
Parent:
0:d3bf1776d1c6
diff -r d3bf1776d1c6 -r 8b8a77b7d715 main.cpp
--- a/main.cpp	Sun May 01 19:12:13 2016 +0000
+++ b/main.cpp	Tue May 03 08:52:52 2016 +0000
@@ -1,113 +1,1177 @@
 #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 BUFFER_SIZE         ((uint32_t)0x0100)
-#define WRITE_READ_ADDR     ((uint32_t)0x0800)
+#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;
+
 
-void FillBuffer(uint32_t *pBuffer, uint32_t BufferLength, uint32_t Offset);
-uint8_t CompareBuffer(uint32_t* pBuffer1, uint32_t* pBuffer2, uint16_t BufferLength);
+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;
+}
 
-int main()
+void matrix_convolution(Image *src, Image *dst, double **mask, int width)
 {
-    uint32_t WriteBuffer[BUFFER_SIZE];
-    uint32_t ReadBuffer[BUFFER_SIZE];
-    FMC_SDRAM_CommandTypeDef SDRAMCommandStructure;
-  
-    pc.printf("\n\nSDRAM demo started\n");
-    led_red = 0;
+    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;
+}
 
-    // Fill the write buffer
-    FillBuffer(WriteBuffer, BUFFER_SIZE, 0xA244250F);
-  
-    // Write buffer
-    sdram.WriteData(SDRAM_DEVICE_ADDR + WRITE_READ_ADDR, WriteBuffer, BUFFER_SIZE);
-    pc.printf("Write data DONE\n");
+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;
+}
 
-    // Issue self-refresh command to SDRAM device
-    SDRAMCommandStructure.CommandMode            = FMC_SDRAM_CMD_SELFREFRESH_MODE;
+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;
-    SDRAMCommandStructure.AutoRefreshNumber      = 1;
-    SDRAMCommandStructure.ModeRegisterDefinition = 0;
-    if (sdram.Sendcmd(&SDRAMCommandStructure) != HAL_OK) 
+
+    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
     {
-      led_red = 1;
-      error("BSP_SDRAM_Sendcmd FAILED\n");
-    }
-  
-    // SDRAM memory read back access
-    SDRAMCommandStructure.CommandMode = FMC_SDRAM_CMD_NORMAL_MODE;
-    if (sdram.Sendcmd(&SDRAMCommandStructure) != HAL_OK) 
-    {
-      led_red = 1;
-      error("BSP_SDRAM_Sendcmd FAILED\n"); 
+        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);
     }
 
-    while(1) {
-      
-      // Read back data from the SDRAM memory
-      sdram.ReadData(SDRAM_DEVICE_ADDR + WRITE_READ_ADDR, ReadBuffer, BUFFER_SIZE); 
-      pc.printf("\nRead data DONE\n");
-
-      // Checking data integrity 
-      if (CompareBuffer(WriteBuffer, ReadBuffer, BUFFER_SIZE) != 0)
-      {
-        led_red = !led_red;
-        pc.printf("Write/Read buffers are different\n");
-      }
-      else
-      {
-        led_green = !led_green;
-        pc.printf("Write/Read buffers are identical\n");
-      }
-  
-      wait(1);
-    }
+    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);
 }
-
-/**
-  * @brief  Fills buffer with user predefined data.
-  * @param  pBuffer: pointer on the buffer to fill
-  * @param  BufferLength: size of the buffer to fill
-  * @param  Value: first value to fill on the buffer
-  * @retval None
-  */
-void FillBuffer(uint32_t *pBuffer, uint32_t BufferLength, uint32_t Value)
-{
-  uint32_t tmpIndex = 0;
-
-  /* Put in global buffer different values */
-  for (tmpIndex = 0; tmpIndex < BufferLength; tmpIndex++ )
-  {
-    pBuffer[tmpIndex] = tmpIndex + Value;
-  }
-} 
-
-/**
-  * @brief  Compares two buffers.
-  * @param  pBuffer1, pBuffer2: buffers to be compared.
-  * @param  BufferLength: buffer's length
-  * @retval 0: pBuffer2 identical to pBuffer1
-  *         1: pBuffer2 differs from pBuffer1
-  */
-uint8_t CompareBuffer(uint32_t* pBuffer1, uint32_t* pBuffer2, uint16_t BufferLength)
-{
-  while (BufferLength--)
-  {
-    if (*pBuffer1 != *pBuffer2)
-    {
-      return 1;
-    }
-
-    pBuffer1++;
-    pBuffer2++;
-  }
-
-  return 0;
-}