/************************************************************************/
/* 下面這一堆代碼真正的分水嶺代碼在cvWatershed( img0, markers )函數裏面,其它
概括起來爲:實現鼠標標記圖像,製做markers標記圖像,實現顏色填充效果。 */
/************************************************************************/算法
//
// ch9_watershed image
// This is an exact copy of the watershed.cpp demo in the OpenCV ../samples/c directory
//
// Think about using a morphologically eroded foreground and background segmented image as the template
// for the watershed algorithm to segment objects by color and edges for collecting
//函數
#include "stdio.h"
#include "cv.h"
#include "highgui.h"
#include <stdio.h>
#include <stdlib.h>ui
IplImage* marker_mask = 0;
IplImage* markers = 0;
IplImage* img0 = 0, *img = 0, *img_gray = 0, *wshed = 0;
CvPoint prev_pt = {-1,-1};spa
void on_mouse(int event, int x, int y, int flags, void* param)
{
if(!img)
return;rest
if(event == CV_EVENT_LBUTTONUP || !(flags & CV_EVENT_FLAG_LBUTTON))
// 若是鼠標左鍵彈起或鼠標左鍵沒有按下
prev_pt = cvPoint(-1,-1);
else if(event == CV_EVENT_LBUTTONDOWN)
// 若是鼠標左鍵按下
prev_pt = cvPoint(x,y);
else if(event == CV_EVENT_MOUSEMOVE && (flags & CV_EVENT_FLAG_LBUTTON))
// 若是鼠標移動且左鍵按下
{
CvPoint pt = cvPoint(x,y);
if( prev_pt.x < 0 )
prev_pt = pt;
cvLine(marker_mask, prev_pt, pt, cvScalarAll(255), 5, 8, 0);
// 實際標記 marker_mask 纔會被算法用到
cvLine(img, prev_pt, pt, cvScalarAll(255), 5, 8, 0);
// img標記只便於用戶觀察
prev_pt = pt;
cvShowImage("image", img);
}
}orm
int main()
{
printf( "Hot keys: \n"
"\tESC - quit the program\n"
"\tr - restore the original image\n"
"\tw or ENTER - run watershed algorithm\n"
"\t\t(before running it, roughly mark the areas on the image)\n"
"\t (before that, roughly outline several markers on the image)\n");blog
char* filename = "lena.BMP";
CvRNG rng = cvRNG(-1);
// 定義一個隨機化生成器並初始化爲-1,配合下面cvRandInt(&rng)生成隨機數,如今知道爲何會變顏色了吧圖片
if((img0 = cvLoadImage(filename,1)) == 0)
return 0;
cvNamedWindow("image", 1);
cvNamedWindow("watershed transform", 1);
img = cvCloneImage(img0);
// 用於顯示的原圖像
img_gray = cvCloneImage(img0);
// 用於和分割出的顏色塊進行混合
wshed = cvCreateImage(cvGetSize(img), 8, 3);
// 用於存儲分割出的顏色塊和最後的效果圖
marker_mask = cvCreateImage(cvGetSize(img), 8, 1);
// 用於記錄用戶標記區域的畫布,並在此基礎上製做用於分水嶺算法使用的markers
markers = cvCreateImage(cvGetSize(img), IPL_DEPTH_32S, 1);
cvCvtColor(img, marker_mask, CV_BGR2GRAY);
cvCvtColor(marker_mask, img_gray, CV_GRAY2BGR);
cvZero(marker_mask);
cvZero( wshed );
cvShowImage( "image", img );
cvShowImage( "watershed transform", wshed );get
cvSetMouseCallback("image", on_mouse, 0);
// 從這兒開始實現鼠標標記功能,具體可查ICVL
for(;;)
{
char c = cvWaitKey();
if( c == 27 )
break;原型
if( c == 'r' )
{
cvZero(marker_mask);
cvCopy(img0, img);
cvShowImage("image", img);
}
if(c == 'w')
{
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* contours = 0;
int comp_count = 1;
// 粗看覺得這是記錄輪廓數目呢,其實否則,他將把每一個輪廓設爲同一像素值
cvFindContours( marker_mask, storage, &contours, sizeof(CvContour),
CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
cvZero( markers );
for( ; contours != 0; contours = contours->h_next, comp_count++)
{
cvDrawContours( markers, contours, cvScalarAll(comp_count+1),
cvScalarAll(comp_count+1), -1, -1, 8, cvPoint(0,0) );
}
// 上面這些最後獲得markers 將會是一些數值塊,每一個輪廓區域內都有同一像素值
// 到此時Watershed 終於獲得了它如飢似渴的 markers 這個markers 中記錄了剛剛
// 用戶用鼠標勾勒的感興趣區域
CvMat* color_tab;
color_tab = cvCreateMat(1, comp_count, CV_8UC3);
// 構造一個一維8bit無符號3通道元素類型的矩陣,用來記錄一些隨機的顏色
for(int i = 0; i < comp_count; i++)
{
uchar* ptr = color_tab->data.ptr + i*3;
ptr[0] = (uchar)(cvRandInt(&rng)%180 + 50);
ptr[1] = (uchar)(cvRandInt(&rng)%180 + 50);
ptr[2] = (uchar)(cvRandInt(&rng)%180 + 50);
}
{// 千呼萬喚始出來的cvWatershed
double t = (double)cvGetTickCount();
cvWatershed(img0, markers);
t = (double)cvGetTickCount() - t;
printf( "exec time = %gms\n", t/(cvGetTickFrequency()*1000.) );
// 上面的t用來計算此算法運行時間
/************************************************************************/
/* markers中包含了一些用戶感興趣的區域,每一個區域用一、二、3。。一些像素值標註,通過
此算法後,markers會變成什麼樣呢?要知道markers中標註的只是用戶用鼠標輕描淡寫的
一些區域,把這些區域想像成一些湖泊,若是隻有一個區域,則表明整幅圖將會被這一個
湖泊淹沒,上面color_tab 正是用來記錄每一個湖泊的顏色。若是用戶標註了兩個區域,則
湖泊會沿着這兩個區域蔓延,直到把圖片分紅兩個湖泊,這兩個湖泊不是無規律的,而是
儘量把圖像的輪廓分隔開。如標註多個區域,則將造成多種顏色的湖泊,此算法會把把
每一個湖泊的分水嶺賦爲 -1,即用來分隔這些湖泊,下面圖片展現了這些湖泊把整幅圖都分
隔開了 */
/************************************************************************/
}
// paint the watershed image
for(int i = 0; i < markers->height; i++)
for(int j = 0; j < markers->width; j++)
{
int idx = CV_IMAGE_ELEM( markers, int, i, j );
// idx獲得了markers 在(i, j)座標的的像素值,這個值對應color_tab中的一種顏色
// 由於markers 中的像素值就是用1-comp_count 的像素值標註的
uchar* dst = &CV_IMAGE_ELEM( wshed, uchar, i, j*3 );
// dst獲得了wshed圖像 (i, j)像素數據的首地址,由於乘3是由於3通道
if( idx == -1 )
// 在wshed圖像中將markers 中獲得的分水嶺標記爲白色,原先-1將顯示黑色
dst[0] = dst[1] = dst[2] = (uchar)255;
else if( idx <= 0 || idx > comp_count )
dst[0] = dst[1] = dst[2] = (uchar)0; // should not get here
else
{
uchar* ptr = color_tab->data.ptr + (idx-1)*3;
// 指向idx 所對應的顏色通道,這些顏色是上面隨機生成的
dst[0] = ptr[0]; dst[1] = ptr[1]; dst[2] = ptr[2];
// 把對應的像素值賦給wshed 圖像
}
}
cvAddWeighted( wshed, 0.5, img_gray, 0.5, 0, wshed );
// 能夠註釋掉看下效果
cvShowImage( "watershed transform", wshed );
cvReleaseMemStorage( &storage );
cvReleaseMat( &color_tab );
}
}
return 1;}