利用梯度方向在圖像上提取直線的方法

原始文獻:

J.Brian Burns, Allen R.Hanson,Edward M.Riseman, Extracting Straight Lines,IEEE Transactions on  Pattern Analysis and Machine Intelligence,(Volume:PAMI-8 ,  Issue: 4 ),1986

文章下載地址:http://ieeexplore.ieee.org/xpls/abs_all.jsp?arnumber=4767808&tag=1,如果無法下載IEEE的文章就去我的資源裏下載:http://download.csdn.net/detail/ihadl/6446169

作者主頁:http://www.ai.sri.com/people/burns/,這裏雖然列了這篇文章,但是沒有找到源代碼,只有一些作者的軟件(http://www.ai.sri.com/software_list/),看着都好高端的樣子,以後有時間可以下下來看看效果。

在pudn上下到一個該文章的源代碼(http://www.pudn.com/downloads204/sourcecode/graph/detail960709.html),不知道是原作者實現的還是後人實現的,總之最後歷盡千辛萬苦還是可以調試成功的,也是可用的,在此感謝代碼實現者!

然後就對該算法進行了研究,對程序進行了調試,前後花費近一個月時間,因此有必要總結一下。

算法的核心步驟:

Introduction中介紹了一些提取線的通用方法和存在的一些問題,其中針對的主要問題是大多數算法利用了像素梯度的大小,而沒有利用梯度的方向,因此文章主要利用梯度的方向來提取直線。主要四個步驟:
一、Group pixels into line-support regions based on similarity of gradient orientation
根據梯度的方向構造LSR.主要步驟包括:
(1)選擇梯度算子計算x和y方向的梯度,然後計算梯度的方向角度.
(2)利用固定間隔的區間對梯度角度結果進行分割.由於區域增長分割方法產生的偶然錯誤都會對結果產生巨大影響.因此使用一種固定分區的分割方法.首先將角度360度的角度範圍分割爲4個區間或者8個區間,然後將具體的角度歸併入分好的角度區間,最後再利用鄰域搜索的算法對角度區間的結果進行聚合.
(3)利用重疊間隔的區間對梯度角度結果進行分割.由於利用固定的區間會產生問題(比如不同的兩條直線由於相鄰且角度相近被聚合爲一條;一條直線很可能被分到兩個不同的LSR當分割區間恰巧將它分開).因此擴展到重疊區間的方法,先以0度爲起點,45度的間隔進行一次歸併,再以22.5度爲起點,45度的間隔進行第二次歸併,然後利用獲取最長線的原則將兩次的結果合併.具體步驟是:1)先獲取每個LSR的長度.2)如果某個像素包含在兩個不同的LSR中,它就投票給長度長的那個LSR.3)每個LSR都會得到內部像素的投票.4)取投票數在50%以上的作爲LSR.大部分的LSR要麼獲得非常多的票,要麼非常少.
二.Approximate the intensity surface by a planar surface
獲得LSR以後需要確定準確的直線的位置。第一個先驗知識是直線的方向垂直於LSR中各點的梯度方向。第二就是確定沿着這個直線方向的直線的具體位置。利用兩平面橫切的方法獲取具體位置。第一個平面是梯度大小利用最小二乘擬合得到的平面,第二個平面是圖像原始灰度值的平均得到的水平平面,這兩個平面相交就獲得了最終的直線位置。
三.Extract attributes from the line-support region and planar fit.

提取到直線以後可以計算一些線特徵,文中提供了一些線特徵的計算公式。
四.Filter lines on the attributes to isolate various image eventssuch as long straight lines of any contrast.

根據線特徵的一些先驗知識剔除不想要的線,留下目標直線。

源代碼

具體代碼由三個文件組成,首先是bitmap.h和bitmap.c,看代碼說明好像是發現了windows.h中的bitmap類有問題,所以就自己重寫的bmp的數據結構和讀寫函數

//
// bitmap.h
//
// header file for MS bitmap format
//
//
#ifndef BITMAP_H
#define BITMAP_H

unsigned char* read_bmp(const char* fname, int* width, int* height, int* step);
void write_bmp(const char *iname, int width, int height, unsigned char *data);

#endif

//
// bitmap.c
//
// handle MS bitmap I/O. For portability, we don't use the data structure defined in Windows.h
// However, there is some strange thing, the side of our structure is different from what it
// should though we define it in the same way as MS did. So, there is a hack, we use the hardcoded
// constanr, 14, instead of the sizeof to calculate the size of the structure.
// You are not supposed to worry about this part. However, I will appreciate if you find out the
// reason and let me know. Thanks.
//
#include "bitmap.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BMP_BI_RGB        0L

typedef unsigned short BMP_WORD;
typedef unsigned int   BMP_DWORD;
typedef int            BMP_LONG;

typedef struct {
    BMP_WORD    bfType;
    BMP_DWORD   bfSize;
    BMP_WORD    bfReserved1;
    BMP_WORD    bfReserved2;
    BMP_DWORD   bfOffBits;
} BMP_BITMAPFILEHEADER;

typedef struct {
    BMP_DWORD   biSize;
    BMP_LONG    biWidth;
    BMP_LONG    biHeight;
    BMP_WORD    biPlanes;
    BMP_WORD    biBitCount;
    BMP_DWORD   biCompression;
    BMP_DWORD   biSizeImage;
    BMP_LONG    biXPelsPerMeter;
    BMP_LONG    biYPelsPerMeter;
    BMP_DWORD   biClrUsed;
    BMP_DWORD   biClrImportant;
} BMP_BITMAPINFOHEADER;

BMP_BITMAPFILEHEADER bmfh;
BMP_BITMAPINFOHEADER bmih;

unsigned char* read_bmp(const char *fname, int* width, int* height, int* step) {
    FILE* file=fopen(fname, "rb");
    if (!file) return NULL;

    // I am doing fread( &bmfh, sizeof(BMP_BITMAPFILEHEADER), 1, file ) in a safe way.
    fread( &(bmfh.bfType), 21, file);
    fread( &(bmfh.bfSize), 41, file);
    fread( &(bmfh.bfReserved1), 21, file);
    fread( &(bmfh.bfReserved2), 21, file);
    fread( &(bmfh.bfOffBits), 41, file);

    BMP_DWORD pos = bmfh.bfOffBits;

    fread( &bmih, sizeof(BMP_BITMAPINFOHEADER), 1, file );

    // error checking
    // "BM" actually
    if ( bmfh.bfType!= 0x4d42) return NULL;
    if ( bmih.biBitCount != 24) return NULL;
    fseek( file, pos, SEEK_SET );

    *width = bmih.biWidth;
    *height = bmih.biHeight;

    int padWidth = *width * 3;
    int pad = 0;
    if ( padWidth % 4!= 0) {
        pad = 4- (padWidth % 4);
        padWidth += pad;
    }
    *step = padWidth;
    int bytes = *height * padWidth;

    unsigned char* data = malloc(bytes);

    int foo = fread( data, bytes, 1, file );

    if (!foo) {
        free(data);
        return NULL;
    }

    fclose( file );
    
    // shuffle bitmap data such that it is (R,G,B) tuples in row-major order
    int i, j;
    j = 0;
    unsigned char temp;
    unsigned char* in;
    unsigned char* out;

    in = data;
    out = data;

    for ( j = 0; j < *height; ++) {
        for ( i = 0; i < *width; ++) {
            out[1= in[1];
            temp = in[2];
            out[2= in[0];
            out[0= temp;

            in += 3;
            out += 3;
        }
        in += pad;
    }

    return data;
}

void write_bmp(const char *iname, int width, int height, unsigned char *data) {
    int bytes, pad;
    bytes = width * 3;
    pad = (bytes%4) ? 4-(bytes%4: 0;
    bytes += pad;
    bytes *= height;

    bmfh.bfType = 0x4d42;    // "BM"
    bmfh.bfSize = sizeof(BMP_BITMAPFILEHEADER) + sizeof(BMP_BITMAPINFOHEADER) + bytes;
    bmfh.bfReserved1 = 0;
    bmfh.bfReserved2 = 0;
    bmfh.bfOffBits = /*hack sizeof(BMP_BITMAPFILEHEADER)=14, sizeof doesn't work?*/
                     14 + sizeof(BMP_BITMAPINFOHEADER);

    bmih.biSize = sizeof(BMP_BITMAPINFOHEADER);
    bmih.biWidth = width;
    bmih.biHeight = height;
    bmih.biPlanes = 1;
    bmih.biBitCount = 24;
    bmih.biCompression = BMP_BI_RGB;
    bmih.biSizeImage = 0;
    bmih.biXPelsPerMeter = (int)(100/ 2.54* 72);
    bmih.biYPelsPerMeter = (int)(100/ 2.54* 72);
    bmih.biClrUsed = 0;
    bmih.biClrImportant = 0;

    FILE *foo=fopen(iname, "wb");

    // fwrite(&bmfh, sizeof(BMP_BITMAPFILEHEADER), 1, foo);
    fwrite( &(bmfh.bfType), 21, foo);
    fwrite( &(bmfh.bfSize), 41, foo);
    fwrite( &(bmfh.bfReserved1), 21, foo);
    fwrite( &(bmfh.bfReserved2), 21, foo);
    fwrite( &(bmfh.bfOffBits), 41, foo);

    fwrite(&bmih, sizeof(BMP_BITMAPINFOHEADER), 1, foo);

    bytes /= height;
    unsigned char* scanline = malloc(bytes);
    int i, j;
    for (= 0; j < height; ++) {
        memcpy( scanline, data + j*3*width, bytes );
        for (= 0; i < width; ++) {
            unsigned char temp = scanline[i*3];
            scanline[i*3= scanline[i*3+2];
            scanline[i*3+2= temp;
        }
        fwrite( scanline, bytes, 1, foo);
    }

    free(scanline);
    fclose(foo);
}
接下來是算法的實現函數,burns.h和burns.c,其中使用了 Intel 的IPP圖像庫(Integrated Performance Primitives),該庫的下載地址在後面列出

//
// burns.h
//
#ifndef _BURNS_H
#define _BURNS_H

#include <ipp.h>

/**
 * Straight line segment.
 */

typedef struct Line {
    /** Coordinates of endpoints. */
    double x1, y1, x2, y2;
    /** Length of line. */
    double length;
    /** Number of pixels in the line's support. */
    unsigned int num_pixels;
    /** Number of pixels who voted for line. */
    unsigned int num_votes;
    /** Slope in the x-direction. */
    double tangent_x;
    /** Slope in the y-direction. */
    double tangent_y;
    /** Midpoint in the x-direction. */
    double midpoint_x;
    /** Midpoint in the y-direction. */
    double midpoint_y;
} Line;

void print_lines(Line* line, int num_lines);

void free_lines(Line* line);

/**
 * Find straight line segments in a grayscale image.
 * @Article{ Burns1,
 *    title = "Extracting Straight Lines",
 *    author = "J. B. Burns and A. R. Hanson and E. M. Riseman",
 *    journal = "IEEE Transactions on Pattern Analysis and Machine Intelligence",
 *    pages = "425--455",
 *    month = jul,
 *    year = "1986"
 * }
 *
 * @param pixels Array of bytes, the image data.
 * @param step Interval in bytes between consecutive rows in the image.
 * @param width Width of region of interest.
 * @param height Height of region of interest.
 * @param roi Region of interest, the size of the image.
 * @param num_buckets Increasing the number of buckets decreases the tolerance
 *        for variations in the gradient orientation along a line. Recommended
 *        value: 8.
 * @param min_gradient Treat gradients below this magnitude (taxi cab distance [0..255])
 *        as zero. Increasing this value eliminates lines with fuzzy boundaries.
 * @param min_length Do not return lines below this length.
 */

void burns_line_extraction(Ipp8u *image, int step, int width, int height,
        int num_buckets, int min_gradient, double min_length,
        Line** lines, int* num_lines);

void burns_line_extraction_demo(Line** lines, int* num_lines);

#endif /* _BURNS_H */
#ifndef _BURNS_H
#define _BURNS_H

#include <ipp.h>

/**
 * Straight line segment.
 */

typedef struct Line {
    /** Coordinates of endpoints. */
    double x1, y1, x2, y2;
    /** Length of line. */
    double length;
    /** Number of pixels in the line's support. */
    unsigned int num_pixels;
    /** Number of pixels who voted for line. */
    unsigned int num_votes;
    /** Slope in the x-direction. */
    double tangent_x;
    /** Slope in the y-direction. */
    double tangent_y;
    /** Midpoint in the x-direction. */
    double midpoint_x;
    /** Midpoint in the y-direction. */
    double midpoint_y;
} Line;

void print_lines(Line* line, int num_lines);

void free_lines(Line* line);

/**
 * Find straight line segments in a grayscale image.
 * @Article{ Burns1,
 *    title = "Extracting Straight Lines",
 *    author = "J. B. Burns and A. R. Hanson and E. M. Riseman",
 *    journal = "IEEE Transactions on Pattern Analysis and Machine Intelligence",
 *    pages = "425--455",
 *    month = jul,
 *    year = "1986"
 * }
 *
 * @param pixels Array of bytes, the image data.
 * @param step Interval in bytes between consecutive rows in the image.
 * @param width Width of region of interest.
 * @param height Height of region of interest.
 * @param roi Region of interest, the size of the image.
 * @param num_buckets Increasing the number of buckets decreases the tolerance
 *        for variations in the gradient orientation along a line. Recommended
 *        value: 8.
 * @param min_gradient Treat gradients below this magnitude (taxi cab distance [0..255])
 *        as zero. Increasing this value eliminates lines with fuzzy boundaries.
 * @param min_length Do not return lines below this length.
 */

void burns_line_extraction(Ipp8u *image, int step, int width, int height,
        int num_buckets, int min_gradient, double min_length,
        Line** lines, int* num_lines);

void burns_line_extraction_demo(Line** lines, int* num_lines);

#endif /* _BURNS_H */
burns.c

//
// burns.c
//
#include "burns.h"
#include "bitmap.h"
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <GL/gl.h>

/**
 * Convenient way to group image parameters which tend to go together.
 */

typedef struct Image8u {
    /** The actual pixel data of the image. */
    Ipp8u* pixels;
    /**
     * Interval in bytes between consecutive rows. This may be different
     * than the image width because ippiMalloc adds padding for better
     * alignment.
     */

    int step;
    /**
     * Region of interest (image size).
     */

    IppiSize roi;
} Image8u;

/**
 * Allocate a new image.
 */

static Image8u new_Image8u(int width, int height) {
    int step;
    Ipp8u* pixels = ippiMalloc_8u_C1(width, height, &step);
    IppiSize roi = {width, height};
    Image8u out = {pixels, step, roi};
    return out;
}

/**
 * Free memory associated with an image.
 */

static void free_Image8u(Image8u image) {
    ippiFree(image.pixels);
}

/**
 * Convenient way to group image parameters which tend to go together.
 */

typedef struct Image16s {
    /** The actual pixel data of the image. */
    Ipp16s* pixels;
    /**
     * Interval in pixels between consecutive rows. Multiply this by
     * sizeof(Ipp16s) before using in ippi functions.
     */

    int step;
    IppiSize roi;
} Image16s;

/**
 * Allocate a new image.
 */

static Image16s new_Image16s(int width, int height) {
    int step;
    Ipp16s* pixels = ippiMalloc_16s_C1(width, height, &step);
    IppiSize roi = {width, height};
    Image16s out = {pixels, step/sizeof(Ipp16s), roi};
    return out;
}

/**
 * Free memory associated with an image.
 */

static void free_Image16s(Image16s image) {
    ippiFree(image.pixels);
}

/**
 * Allocate a new Line object.
 */

static Line* new_Line() {
    Line* out = malloc(sizeof(Line));
    out->num_pixels = 0;
    out->num_votes = 0;
}

void print_lines(Line* lines, int num_lines) {
    int i;
    printf("%d Lines:\n", num_lines);
    for (= 0; i < num_lines; ++i) {
        Line* line = &lines[i];
        printf("Line from (%f %f) to (%f %f)\n",
            line->x1, line->y1,
            line->x2, line->y2);
    }
}

void free_lines(Line* lines) {
    free(lines);
}

/**
 * Part of a connected component of an image. Connected components are
 * represented by tree loops (i.e. n-ary trees where the root points to itself)
 * where each node is a Region.
 */

typedef struct Region {
    /** Pointer to the parent region. */
    struct Region* parent;
    /**
     * Rank is a measure of the complexity of a connected component.
     * It is used when merging two connected components to decide which
     * will be the root node.
     */

    int rank;
    /** Line associated with the region. */
    Line* line;
    /**
     * Regions are allocated in a linked list for easy iteration, this
     * points to the next region in the list.
     */

    struct Region* next;
} Region;

/**
 * Allocate a new region / connected component.
 */

static Region* new_Region() {
    Region* out = malloc(sizeof(Region));
    out->parent = out;
    out->rank = 0;
    out->line = NULL;
    out->next = NULL;
    return out;
}

/**
 * Find the root Region of a connected component. As a side-effect this updates
 * the tree so that all the Regions on the path to the root point directly to
 * the root, speeding up future lookups.
 */

static Region* region_root(Region* c) {
    if (c->parent == c) return c;
    return c->parent = region_root(c->parent);
}

/**
 * Merge two connected components by linking one's root to the other. The
 * smaller component is always linked to the larger one, to avoid
 * badly unbalanced trees.
 */

static void union_regions(Region* a, Region* b) {
    if (== b) return;
    Region* aroot = region_root(a);
    Region* broot = region_root(b);
    if (aroot->rank > broot->rank) {
        broot->parent = aroot;
    } else if (aroot->rank < broot->rank) {
        aroot->parent = broot;
    } else if (aroot != broot) {
        broot->parent = aroot;
        ++aroot->rank;
    }
}

/**
 * Map of pixels to regions. This mainly exists for compatibility with the AT
 * macro.
 */

typedef struct ImageR {
    /** Array of pointers to regions. */
    Region** pixels;
    /** Interval (in units of sizeof(void*)) between consecutive rows. */
    int step;
    /** Region of interest; size of the image. */
    IppiSize roi;
} ImageR;

/**
 * Allocate a new region map.
 */

static ImageR new_ImageR(int width, int height) {
    IppiSize roi = {width, height};
    ImageR out = {calloc(width*height, sizeof(Region*)), width, roi};
    return out;
}

/**
 * Free data associated with a region map.
 */

static void free_ImageR(ImageR image) {
    free(image.pixels);
}

/**
 * Return the element at coordinates (x,y) as measured from the top left in an
 * Image8u-like struct.
 */

#define AT(image, x, y) (image).pixels[(y)*(image).step + (x)]

/**
 * Print the contents of a region map for debugging purposes.
 */

static void print_ImageR(ImageR image) {
    int x, y;
    for (= 0; y < image.roi.height; ++y) {
        for (= 0; x < image.roi.width; ++x) {
            printf("%p ", AT(image, x, y));
        }
        printf("\n");
    }
}

/**
 * Print the contents of a grayscale image for debugging purposes.
 */

static void print_Image8u(Image8u image) {
    int x, y;
    for (= 0; y < image.roi.height; ++y) {
        for (= 0; x < image.roi.width; ++x) {
            printf("%d", AT(image, x, y)/26);
        }
        printf("\n");
    }
}

/**
 * Calculate 4-connected components of the image based on gradient orientation.
 * See http://people.csail.mit.edu/rahimi/connected/ for more details.
 */

static ImageR line_support(Image8u gradient_m, Image8u gradient_q, Region** rtail) {
    int x, y;
    ImageR out = new_ImageR(gradient_m.roi.width, gradient_m.roi.height);

    Region* rhead = *rtail;

    // first pixel
    if (AT(gradient_m, 00)) {
        AT(out, 00= (*rtail) = (*rtail)->next = new_Region();
    }
    // first row
    for (= 1; x < gradient_m.roi.width; ++x) {
        if (AT(gradient_m, x, 0)) {
            if (AT(out, x-10&& AT(gradient_q,x,0== AT(gradient_q,x-1,0)) {
                // matches left pixel
                AT(out, x, 0= AT(out, x-10);
            } else {
                AT(out, x, 0= (*rtail) = (*rtail)->next = new_Region();
            }
        }
    }
    // remaining rows
    for (= 1; y < gradient_m.roi.height; ++y) {
        // first pixel of row
        if (AT(gradient_m, 0, y)) {
            if (AT(out, 0, y-1&& AT(gradient_q,0,y) == AT(gradient_q,0,y-1)) {
                // matches top pixel
                AT(out, 0, y) = AT(out, 0, y-1);
            } else {
                AT(out, 0, y) = (*rtail) = (*rtail)->next = new_Region();
            }
        }
        // remaining pixels of row
        for (= 1; x < gradient_m.roi.width; ++x) {
            if (AT(gradient_m, x, y)) {
                if (AT(out, x-1, y) && AT(gradient_q,x,y) == AT(gradient_q,x-1,y)) {
                    // matches left pixel
                    AT(out, x, y) = AT(out, x-1, y);
                    if (AT(out, x, y-1&& AT(gradient_q,x,y) == AT(gradient_q,x,y-1)) {
                        // matches top and left pixels
                        union_regions(AT(out, x, y), AT(out, x, y-1));
                        AT(out, x, y) = AT(out, x, y-1);
                    }
                } else if (AT(out, x, y-1&& AT(gradient_q,x,y-1== AT(gradient_q,x,y)) {
                    // matches top pixel
                    AT(out, x, y) = AT(out, x, y-1);
                } else {
                    // matches neither top nor left pixels
                    AT(out, x, y) = (*rtail) = (*rtail)->next = new_Region();
                }
            }
        }
    }

    // Merge regions and compute dimensions of each region
    for (= 0; y < gradient_m.roi.height; ++y) {
        for (= 0; x < gradient_m.roi.width; ++x) {
            if (!AT(out, x, y)) continue;
            // Find the root for this connected component.
            AT(out, x, y) = region_root(AT(out, x, y));
            Region* region = AT(out, x, y);
            if (!region->line) {
                region->line = new_Line();
                region->line->x1 = region->line->x2 = x+1;
                region->line->y1 = region->line->y2 = y+1;
            } else {
                if (x+1> region->line->x2) region->line->x2 = x+1;
                if (x+1< region->line->x1) region->line->x1 = x+1;
                region->line->y2 = y+1;
            }
            ++region->line->num_pixels;
        }
    }

    // remove temporary regions from the linked list
    Region* rtmp;
    while (rhead->next != NULL) {
        if (!rhead->next->line) {
            rtmp = rhead->next->next;
            free(rhead->next);
            rhead->next = rtmp;
        } else {
            rhead = rhead->next;
        }
    }
    *rtail = rhead;
    return out;
}

static void estimate_line_properties(ImageR regions, Image8u gradient_m, Image16s gradient_x, Image16s gradient_y) {
    int x, y;
    // calculate correct line parameters for good lines
    for (= 0; y < regions.roi.height; ++y) {
        for (= 0; x < regions.roi.width; ++x) {
            Region* region = AT(regions, x, y);
            if (!region) continue;
            Line* line = region->line;
            if (!line) continue;
            int new_num_pixels = line->num_pixels + 1;
            double weight = hypot(line->tangent_x, line->tangent_y) * line->num_pixels;
            double sx = AT(gradient_x, x, y);
            double sy = AT(gradient_y, x, y);
            line->tangent_x = line->tangent_x * line->num_pixels / (double)new_num_pixels + sx / new_num_pixels;
            line->tangent_y = line->tangent_y * line->num_pixels / (double)new_num_pixels + sy / new_num_pixels;
            double new_weight = hypot(line->tangent_x, line->tangent_y) * new_num_pixels;
            double current_weight = hypot(sx, sy);
            line->midpoint_x = (line->midpoint_x * weight + (x+1* current_weight) / (weight + current_weight);
            line->midpoint_y = (line->midpoint_y * weight + (y+1* current_weight) / (weight + current_weight);
            line->num_pixels = new_num_pixels;
        }
    }
}

static void draw_Image8u(Image8u image) {
    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f(1,0,0);
    glLineWidth(2);
    glRasterPos2f(0,0);
    glDrawPixels(image.roi.width, image.roi.height, GL_LUMINANCE, GL_UNSIGNED_BYTE, image.pixels);
}

static int min(int a, int b) {
    if (<= b) return a;
    else return b;
}
static int max(int a, int b) {
    if (>= b) return a;
    else return b;
}

void burns_line_extraction(Ipp8u* pixels, int step, int width, int height,
    int num_buckets, int min_gradient, double min_length,
    Line** lines, int* num_lines)
{
    IppiSize roi = {width, height};
    Image8u image = {pixels, step, roi};
    int x, y;

    // The edges of the image do not have a well-defined gradient (using
    // the Sobel filter), so we will ignore them for the purposes of this
    // algorithm.
    roi.height -= 2;
    roi.width -= 2;
    Image16s gradient_x = new_Image16s(roi.width, roi.height);
    ippiFilterSobelHoriz_8u16s_C1R(&AT(image, 11), image.step, gradient_x.pixels, gradient_x.step*sizeof(Ipp16s), gradient_x.roi, ippMskSize3x3);
    Image16s gradient_y = new_Image16s(roi.width, roi.height);
    ippiFilterSobelVert_8u16s_C1R(&AT(image, 11), image.step, gradient_y.pixels, gradient_y.step*sizeof(Ipp16s), gradient_y.roi, ippMskSize3x3);

    // Gradient magnitude and direction (in integer multiples of pi/8).
    // We could do this with ippiFilter but since we have to iterate
    // through the image anyways, doing the filtering at the same time
    // avoids unnecessary memory allocation.
    Image8u gradient_m = new_Image8u(roi.width, roi.height);
    // Gradient direction quantized into buckets starting at 0 degrees.
    Image8u gradient_q1 = new_Image8u(roi.width, roi.height);
    // Gradient direction quantized into buckets starting at 180/num_buckets
    // degrees (i.e. offset by half a bucket from gradient_q1).
    Image8u gradient_q2 = new_Image8u(roi.width, roi.height);
    for (= 0; y < gradient_m.roi.height; ++y) {
        for (= 0; x < gradient_m.roi.width; ++x) {
            int hgrad = AT(gradient_x, x, y);
            int vgrad = AT(gradient_y, x, y);
            int gmag = abs(hgrad)/4+ abs(vgrad)/4;
            if (gmag > min_gradient) {
                double theta = atan2((double)vgrad, (double)hgrad);
                int bucket = (int)floor((theta+M_PI) * (double)num_buckets/M_PI);
                AT(gradient_m, x, y) = gmag;
                AT(gradient_q1, x, y) = bucket/2;
                AT(gradient_q2, x, y) = ((bucket+1)%(2*num_buckets))/2;
            } else {
                AT(gradient_m, x, y) = 0;
                AT(gradient_q1, x, y) = 0;
                AT(gradient_q2, x, y) = 0;
            }
        }
    }

    // Find the line-support regions of the image; regions will be
    // collected in a linked list so we can iterate through them later.
    Region* rhead = new_Region();
    Region* rtail = rhead;
    ImageR regions1 = line_support(gradient_m, gradient_q1, &rtail);
    ImageR regions2 = line_support(gradient_m, gradient_q2, &rtail);
    // free temporary list head
    rtail = rhead;
    rhead = rhead->next;
    free(rtail);
    // gradient orientations no longer needed
    free_Image8u(gradient_q1);
    free_Image8u(gradient_q2);

    // approximate length of each region's line
    for (rtail = rhead; rtail != NULL; rtail = rtail->next) {
        Line* line = rtail->line;
        line->length = abs(line->x1 - line->x2)/2+ abs(line->y1 - line->y2)/2;
    }

    // vote on regions
    for (= 0; y < regions1.roi.height; ++y) {
        for (= 0; x < regions1.roi.width; ++x) {
            if (!AT(regions1, x, y)) continue;
            Region* c1 = AT(regions1, x, y);
            Region* c2 = AT(regions2, x, y);
            if (c1->line->length >= c2->line->length) {
                ++c1->line->num_votes;
            } else {
                ++c2->line->num_votes;
            }
        }
    }

    // discard bad (undervoted) lines
    for (rtail = rhead; rtail != NULL; rtail = rtail->next) {
        Line* line = rtail->line;
        if (line) {
            if (line->length < min_length || line->num_votes < line->num_pixels/2{
                free(line);
                rtail->line = NULL;
            } else {
                // reset pixel counter for averages
                line->num_pixels = 0;
            }
        }
    }

    estimate_line_properties(regions1, gradient_m, gradient_x, gradient_y);
    estimate_line_properties(regions2, gradient_m, gradient_x, gradient_y);

    // calculate more accurate endpoints for lines
    for (rtail = rhead; rtail != NULL; rtail = rtail->next) {
        Line* line = rtail->line;
        if (line) {
            double xmin = line->x1; double xmax = line->x2;
            double ymin = line->y1; double ymax = line->y2;

            line->x1 = line->midpoint_x - (line->midpoint_y - ymin)*line->tangent_x/line->tangent_y;
            if (line->x1 < xmin) {
                line->x1 = xmin;
                line->y1 = line->midpoint_y - (line->midpoint_x - xmin)*line->tangent_y/line->tangent_x;
            } else if (line->x1 > xmax) {
                line->x1 = xmax;
                line->y1 = line->midpoint_y + (xmax - line->midpoint_x)*line->tangent_y/line->tangent_x;
            }
            line->x2 = line->midpoint_x + (ymax - line->midpoint_y)*line->tangent_x/line->tangent_y;
            if (line->x2 < xmin) {
                line->x2 = xmin;
                line->y2 = line->midpoint_y - (line->midpoint_x - xmin)*line->tangent_y/line->tangent_x;
            } else if (line->x2 > xmax) {
                line->x2 = xmax;
                line->y2 = line->midpoint_y + (xmax - line->midpoint_x)*line->tangent_y/line->tangent_x;
            }

            // re-calculate length
            line->length = hypot(line->x1 - line->x2, line->y1 - line->y2);
            // discard too-short lines
            if (line->length < min_length) {
                free(line);
                rtail->line = NULL;
            }
        }
    }

    // count lines
    *num_lines = 0;
    for (rtail = rhead; rtail != NULL; rtail = rtail->next) {
        Line* line = rtail->line;
        if (line) ++(*num_lines);
    }

    // gradient information no longer needed
    free_Image8u(gradient_m);
    free_Image16s(gradient_y);
    free_Image16s(gradient_x);
    // region maps no longer needed
    free_ImageR(regions1);
    free_ImageR(regions2);

    // Lines will be collected in an array
    *lines = malloc(*num_lines * sizeof(Line));
    int i = 0;

    // build output list and free regions
    while (rhead != NULL) {
        Line* line = rhead->line;
        if (line) {
            // found the root of a connected component with a good line
            (*lines)[i++] = *line;
            free(line);
        }
        // free the region and move to the next one
        rtail = rhead;
        rhead = rhead->next;
        free(rtail);
    }
}

void burns_line_extraction_demo(Line** lines, int* num_lines) {
    IppiSize roi;
    int rgb_step, gray_step;
    Ipp8u* rgb_pixels = read_bmp("demo.bmp",  &roi.width, &roi.height, &rgb_step);
    Ipp8u* gray_pixels = ippiMalloc_8u_C1(roi.width, roi.height, &gray_step);
    ippiRGBToGray_8u_C3C1R(rgb_pixels, rgb_step, gray_pixels, gray_step, roi);
    burns_line_extraction(gray_pixels, gray_step, roi.width, roi.height, 85010, lines, num_lines);
}
最後是調用和利用OpenGL進行顯示的主函數main.c

#include "burns.h"
#include "bitmap.h"
#include <stdio.h>
#include <math.h>
#include <ippcc.h>
#include <GL/gl.h>
#include <GL/glut.h>

Ipp8u* rgb_pixels;
int rgb_step;
IppiSize rgb_roi;

Ipp8u* gray_pixels;
int gray_step;
IppiSize gray_roi;

void display() {
    glClear(GL_COLOR_BUFFER_BIT);
    glLineWidth(2);
    glRasterPos2f(0,0);
    glDrawPixels(gray_roi.width, gray_roi.height, GL_LUMINANCE, GL_UNSIGNED_BYTE, gray_pixels);

    Line* lines;
    int num_lines;
    int i;
    burns_line_extraction(gray_pixels, gray_step, gray_roi.width, gray_roi.height,
        8105,
        &lines, &num_lines);

    glColor3f(1,0,0);
    glBegin(GL_LINES);
    for (= 0; i < num_lines; ++i) {
        Line* line = &lines[i];
        glVertex3d(line->x1, line->y1, 0);
        glVertex3d(line->x2, line->y2, 0);
    }
    glEnd();

    glPointSize(3);
    glColor3f(0,0,1);
    glBegin(GL_POINTS);
    for (= 0; i < num_lines; ++i) {
        Line* line = &lines[i];
        glVertex3d(line->midpoint_x, line->midpoint_y, 0);
    }
    glEnd();
    glFlush();
    free_lines(lines);
}

int main(int argc, char **argv) {
    IppiSize rgb_roi;
    rgb_pixels = read_bmp("demo.bmp",  &rgb_roi.width, &rgb_roi.height, &rgb_step);
    gray_pixels = ippiMalloc_8u_C1(rgb_roi.width, rgb_roi.height, &gray_step);
    gray_roi = rgb_roi;

    ippiRGBToGray_8u_C3C1R(rgb_pixels, rgb_step, gray_pixels, gray_step, rgb_roi);

    glutInit(&argc, argv);
    glutInitWindowSize(gray_roi.width, gray_roi.height);
    glutInitWindowPosition(00);
    glutInitDisplayMode(GLUT_RGB);
    glutCreateWindow("window");
    glClearColor(0.00.00.00.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, gray_roi.width, 0, gray_roi.height, -11);
    glutDisplayFunc(display);
    glutMainLoop();
    return 0;
}

編譯過程中可能遇到的問題

理論上這幾個c文件利用gcc是可以直接編譯通過的,但無奈本人的gcc功力不夠,最後只能把它移植到VC6中進行編譯和調試。下面列 一下用VC6編譯過程中可能遇到的問題和解決方案:

1、在自定義的min和max函數報錯:錯誤:expected identifier or ‘(’ before ‘int’,找了半天感覺應該是max或者min這個函數名稱已經被用過了?改成Mymax或者Mymin以後就行了

2、結構體初始化報錯,比如Image8u out = {pixels, step, roi};改成各個成員分別初始化就行了:

Image8u out;
out.pixels = pixels;
out.step = step;
out.roi = roi;

3、OpenGL環境的配置可以參考我之前寫過的博客Windows下配置OpenGL的開發環境,以VC6爲例

需要的資源下載

最後列一下編譯需要的資源,其實主要是ipp庫和OpenGL

OpenGL上面說過了,參考我之前發過的博客,裏面有資源下載地址,找個簡單例子測試下沒問題就行了。

ipp庫就比較噁心了,我是在intel的網站下載了個試用版http://software.intel.com/en-us/intel-ipp

反正有頭文件和lib就行了。

最後貼幾張效果圖吧,費這麼大勁總得搞點東西出來啊。

需要注意的是不知爲啥最後只能顯示寬度爲64倍數的bmp圖片(其他寬度的圖片提取結果應該是沒問題,只是顯示一團糟,懷疑是bitmap的數據結構導致的,高手可以改進下,爭取去掉它的ipp數據結構,換成opencv或者gdal的數據結構,這樣就啥圖片都可以處理了)

首先是經典的lena中的直線


再來看一張高分辨率遙感數據的直線提取結果


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章