分割圖像並指定多個灰度閥值,對同一幅圖像進行分區域的二值化

 大致原理介紹:

        圖像處理過程中經常需要用到二值化圖像並提取關鍵點的處理方式,但是如果只指定一個灰度閥值,得到的處理結果往往不能讓人滿意,亮度相對較高,閥值相對較低的地方會出現大片的“白斑”,。因此就需要將圖像分割爲多個小塊,然後指定一個最小亮度閥值 lignmin 和一個最大亮度閥值 lightmax,之後通過計算爲每個圖像小塊線性匹配出一個合適的閥值,從而得到較爲理想的效果。

        如下圖,Y軸 lightmin 和 lightmax 分別是指定的灰度閥值下界和上界,X軸是分割後小依據平均灰度從小到大排列的圖像小塊

數據結構:

        加載位圖使用OPENGL 的GLAUX 中的 auxDIBImageLoad(fielpath) 函數,然後初始圖像數據保存在一個 AUX_RGBImageRec (GLAUX提供的數據結構,注意:圖像從左下角開始掃描的)類型數據結構中。需要用到的自定義結構有 struct imgMap,保存圖像分割數據和一個存儲 imgArea * 的指針鏈表,用來存儲分割後的圖像小塊 。struct imgArea  是存儲每個分割小塊的數據結構,包含了小塊 平均亮度,大小, 原圖像中的位置和指向下一個小塊的的指針 等數據。

typedef struct _imgArea
{
	unsigned char avevalue; // 平均亮度
	unsigned char selvalue; // 選擇的亮度閥值
	// 寬度和高度以像素爲單位
	int width;
	int height;
	// 如果 imgArea 所屬的 imgMap->islefetbottom=TRUE,則存儲
	// 的是imgArea左下角第一個像素在 imgMap->pimg->data 中對
	// 應像素的指針;否則就是左上角第一個像素在 imgMap->pimg
	// ->data 中對應的指針
	unsigned char *begin; 
	// 每行像素佔用的字節數(虛擬),可以方便遍歷 imgArea 中的
	// 像素,( begin + n*step )可以方便的指向第 N 行的首個像
	// 素的地址。
	// 注意:虛擬 的含義是說 step != imgArea->width * sizeof(unsigned char) * 3
	//			實際上 step = imgMap->pimg->sizeX * sizeof(unsigned char) * 3
	int step;
	struct _imgArea *next;
}imgArea;

typedef struct _imgMap
{
	BOOL isleftbottom;
	AUX_RGBImageRec *pimg;
	unsigned char *lights;
	int mapsizeX;
	int mapsizeY;
	imgArea *data;
	imgArea **ascorder_assist;
}imgMap;

二值化圖像:

        首先是根據 AUX_DIBImageRec 創建 imgMap。

imgMap *createimgMap(
	AUX_RGBImageRec *pimg,unsigned char *lights,
	int mapsizeX,int mapsizeY,BOOL isleftbottom=TRUE
	)
{
	imgMap *newMap = new imgMap();
	newMap->pimg = pimg;
	// 初始化 imgMap 頭部
	newMap->isleftbottom = isleftbottom;
	//setorderLights(lightsize, lights);
	newMap->lights		= lights;
	newMap->mapsizeX	= mapsizeX;
	newMap->mapsizeY	= mapsizeY;
	newMap->data = nullptr;		// 未初始化的數據塊
	newMap->ascorder_assist = nullptr; 
	
	// 初始化數據塊
	CREATE_IMGMAP(newMap);
	// 設置 ascorder_assist 各成員的 selvalue
	int x=0;							// 遍歷 newMap->ascorder_assist[]
	int areanums = newMap->mapsizeX*newMap->mapsizeY;
	float factor = ( float(lights[1])-float(lights[0]) )/( float(newMap->ascorder_assist[areanums-1]->avevalue)-float(newMap->ascorder_assist[0]->avevalue) );
	newMap->ascorder_assist[0]->selvalue = lights[0];
	for(x=1;x < areanums;++x)
	{
		newMap->ascorder_assist[x]->selvalue = lights[0] + (int)( factor*(newMap->ascorder_assist[x]->avevalue-newMap->ascorder_assist[0]->avevalue));
	}
	
	return newMap;

}

        然後對imgMap進行分割,並將分割後得到的小塊根據 imgArea.avevalue 進行遞增排序。

void initimgMapData_leftbottom(imgMap *pmap)
{
    int x=0,y=0;                                                    // 遍歷 pmap->data 之用
    void *ptemp = nullptr;
    imgArea *parea = pmap->data;
    int areawidth_basic    = ( pmap->pimg->sizeX )/( pmap->mapsizeX );
    int areaheight_basic = ( pmap->pimg->sizeY) /( pmap->mapsizeY );
    int areawidth_extra    = ( pmap->pimg->sizeX )%( pmap->mapsizeX );
    int areaheight_extra = ( pmap->pimg->sizeY )%( pmap->mapsizeY );
    unsigned char *pixelperLine = nullptr;                    // 遍歷每行 imgArea 之用
    int imgStep        = pmap->pimg->sizeX*UCHAR_SIZE*3;    // 圖片的每行像素寬度
    int realStep    = areawidth_basic*UCHAR_SIZE*3;     // 每行前 mspzieX-1 個imgArea 的像素寬度

    for(y=0;y < pmap->mapsizeY;++y)
    {
        pixelperLine = pmap->pimg->data + y*( areaheight_basic*imgStep );
        for(x=0;x < pmap->mapsizeX;++x)
        {
            // 新 imgArea 插入鏈表
            ptemp = (void *)new imgArea();
            ( (imgArea *)ptemp )->next = nullptr;
            if( nullptr == pmap->data )
            {
                pmap->data = (imgArea *)ptemp;
                parea = (imgArea *)ptemp;
            }
            parea->next = (imgArea *)ptemp;
            parea = parea->next;
            // 對行 imgArea 初始化
            if( (pmap->mapsizeX - 1)  == x ) parea->width =  areawidth_basic + areawidth_extra;
            else parea->width = areawidth_basic;
            if( (pmap->mapsizeY - 1) == y ) parea->height = areaheight_basic + areaheight_extra;
            else parea->height = areaheight_basic;
            parea->step = imgStep;
            parea->begin = pixelperLine + x*realStep;
            
          // 估算平均灰度 value(採用 米字 估算)
            unsigned char *perline = nullptr;
            unsigned char *pixel = nullptr;
            float count = 0.0f;                        // 米字像素的總亮度
            float factor = (float)(parea->height) / (float)(parea->width); // 乘算因子
            int j=0, k=0;                                // 遍歷之用
            for(k=0;k < parea->width;++k)
            {
                j = (int)(k * factor);
                pixel = parea->begin + k*3;
                count += PIXEL_LIGHT( (pixel + parea->height/2*(parea->width*3)) );
                count += PIXEL_LIGHT( (pixel + j*parea->step) );
                count += PIXEL_LIGHT( (pixel + (parea->height-j-1)*parea->step) );
            }
            perline = parea->begin + parea->width*3;
            for(j=0;j < parea->height;++j)
            {
                pixel = perline + parea->step;
                count += PIXEL_LIGHT(pixel);
            }
            parea->avevalue = (int)( count / (parea->width*3 + parea->height) );
        }
    }
    // 現在將 data 鏈表的內容複製到 ascorder_assist 數組
    pmap->ascorder_assist = new imgArea*[pmap->mapsizeX * pmap->mapsizeY];
    for( parea=pmap->data,x=0;parea != nullptr;parea = parea->next,++x )
    {
        pmap->ascorder_assist[x] = parea;
    }
    // 將 ascorder_assist 的內容按升序排列
    int mapxy = pmap->mapsizeX * pmap->mapsizeY;
    for(x=0;x < mapxy;++x)
    {
        for(y=0;y < (mapxy-1-x);++y)
        {
            if( pmap->ascorder_assist[y]->avevalue > pmap->ascorder_assist[y+1]->avevalue )
            {
                parea = pmap->ascorder_assist[y+1];
                pmap->ascorder_assist[y+1] = pmap->ascorder_assist[y];
                pmap->ascorder_assist[y] = parea;
            }
        }
    }

}

        最後調用 multiValeThreshold(...)  對圖像進行二值化。

// 改進後的二值化函數
void multiValeThreshold(
    AUX_RGBImageRec *psrc,AUX_RGBImageRec *pdst,
    unsigned char *lights,int mapsizeX,int mapsizeY
    )
{
    // 將 prc 複製到 pdst
    if( pdst )
    {
        if( pdst->data )
        {
            free(pdst->data); pdst->data = nullptr;
        }
        free(pdst); pdst=nullptr;
    }
    pdst = new AUX_RGBImageRec( );
    pdst->sizeX = psrc->sizeX;
    pdst->sizeY = psrc->sizeY;
    int datasize = psrc->sizeX*psrc->sizeY*UCHAR_SIZE*3;
    pdst->data = new unsigned char[datasize];
    memset(pdst->data,0,datasize);
    memcpy_s(pdst->data,datasize,psrc->data,datasize);
    // 創建一個局部變量對pdst進行二值化處理
    imgMap *pmap = createimgMap(pdst, lights,mapsizeX,mapsizeY,TRUE);

    imgArea *parea = nullptr;                    // 遍歷 imgMap 指針
    unsigned char *pixelperLine = nullptr; // 遍歷 imgArea 指針
    unsigned char *pixel = nullptr;
    int x=0,y=0;
    for( parea=pmap->data;parea != nullptr;parea=parea->next )
    {
        for(y=0;y < parea->height;++y)
        {
            // pixelperLing 始終指向每行的第一個像素的地址
            pixelperLine = parea->begin + y*(parea->step);
            for(x=0;x < parea->width;++x)
            {
                pixel = pixelperLine + x*UCHAR_SIZE*3;
                ( (PIXEL_LIGHT(pixel)) > (float)(parea->selvalue) )? SETPIXEL_MAX(pixel):SETPIXEL_ZERO(pixel);
            }
        }
    }
    return;
}

本篇結束。

如有任何錯誤或者更好的方法或建議,歡迎指正委屈

[email protected]

發佈了31 篇原創文章 · 獲贊 18 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章