某島

… : "…アッカリ~ン . .. . " .. .
December 4, 2010

Lab-7,OpenCV …

Nov 29th,去了一下樓上寢室膜拜了一下沙漠君,得到了它們目前的工作進展,是一份高級程序語言的作業 … 啊,當時我就驚了…

xiaodai : “我的這個還不是很好的,Xueyr 的作業在縮放的時侯用了多線程。。。”
xiaodao :”。#啊啊啊啊。。。要怎麼在 Xcode 里實現多幀刷屏呢?。。”

//by SHA Mo
//Visual Studio 2010 + OpenCV 2.1


#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

//聲明滑鼠回調函數
void my_mouse_callback(int event, int x, int y, int flags, void* param);

//圖的解析度
const int IMAGE_SIZE_WID = 640;
const int IMAGE_SIZE_HGT = 480;

//自動縮放圖像分割數
const int split_wid = 4;
const int split_hgt = 4;

//迭代次數上界
const int ITERATION_TIME = 200;

//過度動畫幀數
const int TRANSITION_IMAGE = 20;

//過度動畫幀速
const int TRANSITION_TIME = 1;

//茱莉亞集合參數
double j_re = -0.835;
double j_im = -0.2350;

//圖的縮放比
double zoomRatio_WID = 4;
double zoomRatio_HGT = 3;

//函數圖象的平移量
double mov_re = -2.5;
double mov_im = -1.5;

//上色控量
const double PI = acos(-1.0);
double ratio_r = 30;		
double ratio_g = 25;
double ratio_b = 20;
double phase_r = 0;			
double phase_g = 0;
double phase_b = 0.25;

//滑鼠事件所需量
CvRect box;
bool drawing_box = false;
bool moving = false;
int mov_x;
int mov_y;

//滑鼠實時位置
CvPoint mousePoint;

//判斷一個複數是否屬於Mandelbrot_Set
int mSetTest(double c_re, double c_im) {
	int current_time = 1;
	double z_re = 0;
	double z_im = 0;
	while ( z_re*z_re + z_im*z_im < 4 && current_time < ITERATION_TIME) {
		++current_time;
		double t_re = z_re*z_re - z_im*z_im + c_re;
		z_im = 2*z_re*z_im + c_im;
		z_re = t_re;
	}
	if (current_time == ITERATION_TIME) return 0;
	else return current_time;
}

//判斷一個複數是否屬於Julia_Set
int jSetTest(double c_re, double c_im) {
	int current_time = 1;
	while ( c_re*c_re + c_im*c_im < 4 && current_time < ITERATION_TIME) {
		++current_time;
		double t_re = c_re*c_re - c_im*c_im + j_re;
		c_im = 2*c_re*c_im + j_im;
		c_re = t_re;
	}
	if (current_time == ITERATION_TIME) return 0;
	else return current_time;
}

//計算像素點的顏色
CvScalar whatColor(int x, int y) {
	//七彩顏色庫:紅橙黃綠藍靛紫
	CvScalar colorLib[7] = {{0, 0, 255},{15, 123, 255},{15, 255, 255},{15, 255, 33},{255, 141, 16},{255, 15, 69},{201, 15, 255}};
	CvScalar black = {0, 0, 0};
	CvScalar color;
	int step = jSetTest(mov_re + x/(double)IMAGE_SIZE_WID*zoomRatio_WID, mov_im + y/(double)IMAGE_SIZE_HGT*zoomRatio_HGT);
	if (step) {
		//不屬於分型集合
		//color = colorLib[(step + 5) % 7];
		color.val[0] = 127.5 - 127.5 * cos(step / ratio_b + phase_b * PI);
		color.val[1] = 127.5 - 127.5 * cos(step / ratio_g + phase_g * PI);
		color.val[2] = 127.5 - 127.5 * cos(step / ratio_r + phase_r * PI);
	}
	else {
		//屬於分型集合
		color = black;
	}
	return color;
}

//繪製選定框
void draw_box( IplImage* img, CvRect rect ) {
	cvRectangle (
		img,
		cvPoint(box.x, box.y),
		cvPoint(box.x+box.width, box.y+box.height),
		cvScalar(0x00, 0xff, 0x00)
		);
}

//繪製圖像
void draw( IplImage* image ) {
	for (int i = 0; i < IMAGE_SIZE_WID; ++i)
		for (int j = 0; j < IMAGE_SIZE_HGT; ++j) {
			cvSet2D(image, j, i, whatColor(i, j));
		}
}

//自定義可以越界的取色函數
CvScalar my_cvGet2D(IplImage* image, int x, int y)
{
	//越界返回黑色
	if (x<0 || y<0 || x>=IMAGE_SIZE_WID || y>=IMAGE_SIZE_HGT) return cvScalar(0x00, 0x00, 0x00);
	return cvGet2D(image, y, x);
}

//拷貝圖像矩形區域(允許越界,越界部分填黑,ROI實現)
IplImage* my_cvCloneImageRect(IplImage* src, CvRect rect) {
	IplImage *dst = cvCreateImage(cvSize(rect.width, rect.height), src->depth, src->nChannels);
	cvSetZero(dst);

	//計算src的ROI矩形
	CvRect src_rect;
	src_rect.x = max(0, rect.x);
	src_rect.y = max(0, rect.y);
	src_rect.width = min(src->width, rect.x + rect.width) - src_rect.x;
	src_rect.height = min(src->height, rect.y + rect.height) - src_rect.y;
	if (src_rect.width <= 0 || src_rect.height <= 0) {
		src_rect.width = 0;
		src_rect.height = 0;
	}

	//計算dst的ROI矩形
	CvRect dst_rect;
	dst_rect.x = max(0, -rect.x);
	dst_rect.y = max(0, -rect.y);
	dst_rect.width = min(rect.width, -rect.x + src->width) - dst_rect.x;
	dst_rect.height = min(rect.height, -rect.y + src->height) - dst_rect.y;	

	if (dst_rect.width <= 0 || dst_rect.height <= 0) {
		dst_rect.width = 0;
		dst_rect.height = 0;
	}
	if (src_rect.width!=0 && src_rect.height!=0 && dst_rect.width!=0 && dst_rect.height!=0) {
		cvSetImageROI(src, src_rect);
		cvSetImageROI(dst, dst_rect);
		cvResize(src, dst);
		cvResetImageROI(src);
		cvResetImageROI(dst);
	}
	return dst;
}

//顯示過度效果
IplImage* show_transition(IplImage* image, CvRect &box) {
	
	IplImage* tmp = cvCloneImage( image );
	for (int k = 1; k <= TRANSITION_IMAGE; ++k) {

		//計算分步縮放的臨時選定框
		double tmp_x = box.x * (double)k/TRANSITION_IMAGE;
		double tmp_y = box.y * (double)k/TRANSITION_IMAGE;
		double tmp_width = IMAGE_SIZE_WID - (IMAGE_SIZE_WID - box.x - box.width) * (double)k/TRANSITION_IMAGE - tmp_x;
		double tmp_height = IMAGE_SIZE_HGT - (IMAGE_SIZE_HGT - box.y - box.height) * (double)k/TRANSITION_IMAGE - tmp_y;

		CvRect tmp_box = cvRect(tmp_x, tmp_y, tmp_width, tmp_height);
		cvResize(my_cvCloneImageRect(image, tmp_box), tmp);
		cvShowImage("HelloOpenCV", tmp);
		cvWaitKey( TRANSITION_TIME );
	}
	return tmp;
}

//移動縮放圖像(增加動態動畫效果)
void move_zoom_image(IplImage* image, CvRect &box, bool transition = 1) {
	IplImage* current_image = image;

	//顯示過度效果
	if (transition) {
		current_image = show_transition( image, box );
	}

	//改變函數圖象的平移量
	mov_re += (double)box.x/IMAGE_SIZE_WID*zoomRatio_WID;
	mov_im += (double)box.y/IMAGE_SIZE_HGT*zoomRatio_HGT;

	//改變圖的縮放比
	zoomRatio_WID *= (double)box.width/IMAGE_SIZE_WID;
	zoomRatio_HGT *= (double)box.height/IMAGE_SIZE_HGT;

	//刷新圖像
	draw( image );
}

//灰度圖,開在函數內部會棧溢出
int grey_level[IMAGE_SIZE_WID][IMAGE_SIZE_HGT];
//評估圖像複雜度(多運算元加權:信息熵、反差[灰度矩陣求法]、邊緣比率)
double image_rect_complexity( IplImage* image, CvRect rect ) {
	//各灰度像素數
	int grey_level_count[256] = {};

	//灰度總量
	int grey_level_num = rect.width * rect.height;

	//灰度共生矩陣
	int GLCM[256][256] = {};

	//抽取圖像灰度信息
	for (int i = rect.x; i < rect.x + rect.width; ++i)
		for (int j = rect.y; j < rect.y + rect.height; ++j) {
			grey_level[i][j]  = ((uchar*)(image->imageData + j*image->widthStep))[i*image->nChannels + 0];
			grey_level[i][j] += ((uchar*)(image->imageData + j*image->widthStep))[i*image->nChannels + 1];
			grey_level[i][j] += ((uchar*)(image->imageData + j*image->widthStep))[i*image->nChannels + 2];
			grey_level[i][j] /= 3;
			++grey_level_count[ grey_level[i][j] ];
		}

	//計算灰度共生矩陣(△x=1,△y=1)和 邊緣像素數
	int edge_pixel = 0;
	for (int i = rect.x; i < rect.x + rect.width - 1; ++i)
		for (int j = rect.y; j < rect.y + rect.height - 1; ++j) {
			++GLCM[grey_level[i][j]][grey_level[i+1][j+1]];
			if (grey_level[i][j] != grey_level[i+1][j+1]) ++edge_pixel;
		}


	//計算信息熵
	double inf_entropy = 0;
	for (int i = 0; i < 256; ++i) if (grey_level_count[i] != 0) {
		inf_entropy -= (double)grey_level_count[i]/grey_level_num * log((double)grey_level_count[i]/grey_level_num);
	}

	//計算反差
	double contrast = 0;
	for (int i = 0; i < 256; ++i)
		for (int j = 0; j < 256; ++j) if (GLCM[i][j] != 0)
			contrast = (double)GLCM[i][j]/grey_level_num * (i - j) * (i - j);

	//邊緣比率
	double edge_ratio = edge_pixel / grey_level_num;

	//返回加權圖像複雜度
	return contrast/2 + inf_entropy + edge_ratio;
}

//自動縮放
void auto_zoom( IplImage* image ) {
	double max_complexity = 0;
	CvRect max_complex_rect;
	for (int i = 0; i < split_wid; ++i)
		for (int j = 0; j < split_hgt; ++j) {
			double tmp_complexity = image_rect_complexity( image, 
				cvRect(	i*(IMAGE_SIZE_WID/split_wid), 
						j*(IMAGE_SIZE_HGT/split_hgt),
						IMAGE_SIZE_WID/split_wid,
						IMAGE_SIZE_HGT/split_hgt ) );
			if (tmp_complexity > max_complexity) {
				max_complex_rect = cvRect(	
					i*(IMAGE_SIZE_WID/split_wid), 
					j*(IMAGE_SIZE_HGT/split_hgt),
					IMAGE_SIZE_WID/split_wid,
					IMAGE_SIZE_HGT/split_hgt );
				max_complexity = tmp_complexity;
			}
		}
	move_zoom_image( image, max_complex_rect );
}

int main( int argc, char* argv[] ) {
	//初始化選定框變數
	box = cvRect(-1, -1, 0, 0);

	//創建圖像
	IplImage* pImg = cvCreateImage (/*size*/cvSize (IMAGE_SIZE_WID, IMAGE_SIZE_HGT), /*depth*/IPL_DEPTH_8U, /*nChannels*/3);
	cvSetZero(pImg);
	IplImage* temp = cvCloneImage( pImg );

	//創建窗口
	cvNamedWindow (/*name of the window*/"HelloOpenCV", 1);

	//標誌滑鼠事件
	cvSetMouseCallback("HelloOpenCV", my_mouse_callback, (void*) pImg);

	//初始化圖像
	draw( pImg );

	//反覆獲取用戶交互信息並處理
	while ( 1 ) {
		CvFont font;
		double hScale = 1.0;
		double vScale = 1.0;
		int lineWidth = 1;
		cvCopyImage( pImg, temp );

		//實時繪製選定框
		if ( drawing_box ) draw_box( temp, box );

		//按住右鍵時拖動畫面
		if ( moving ) {
			CvRect tmp_box = cvRect(mov_x - mousePoint.x , mov_y - mousePoint.y, pImg->width, pImg->height);
			cvResize(my_cvCloneImageRect(pImg, tmp_box), temp);
		}

		//實時顯示滑鼠位置對應的複數坐標系的坐標
		cvInitFont(&font, CV_FONT_HERSHEY_DUPLEX, hScale, vScale, 0, lineWidth);

		char x_coord[10];
		itoa((int)mousePoint.x, x_coord, 10);
		char y_coord[10];
		itoa((int)mousePoint.y, y_coord, 10);
		cvPutText(temp, strcat(strcat(x_coord, ","), y_coord), mousePoint, &font, cvScalar(0x00, 0x00, 0xff));
		cvShowImage( "HelloOpenCV", temp);

		//輸入ESC後退出
		int key_num = cvWaitKey( 20 );
		if ( key_num == 27 ) break;
		else if ( key_num == 'r') {
			CvRect normal =  cvRect(0, 0, pImg->width, (int)(pImg->height / ((zoomRatio_HGT/zoomRatio_WID)/((double)IMAGE_SIZE_HGT/IMAGE_SIZE_WID))));
			move_zoom_image( pImg, normal );
			//zoomRatio_HGT = zoomRatio_WID * IMAGE_SIZE_HGT / IMAGE_SIZE_WID;
			//draw( pImg );
		}
		else if ( key_num == 'c') auto_zoom( pImg );
	}
	
	//關閉窗口、釋放圖像
	cvReleaseImage( &pImg );
	cvReleaseImage( &temp );
	cvDestroyWindow( "HelloOpenCV" );
	return 0;
}

//定義滑鼠事件回調函數
void my_mouse_callback(int event, int x, int y, int flags, void* param) {
	
	IplImage* image = (IplImage*) param;
	switch ( event ) {

	//滑鼠拖動
	case CV_EVENT_MOUSEMOVE :

		mousePoint = cvPoint(x, y);
		//左鍵拖動時實時維護選定框
		if (flags & CV_EVENT_FLAG_LBUTTON) {
			drawing_box = true;
			if ( drawing_box ) {
				box.width = x - box.x;
				box.height = y - box.y;
			}
		}
		else if (flags & CV_EVENT_FLAG_RBUTTON) {
			moving = true;
		}
	break;

	//左鍵按下
	case CV_EVENT_LBUTTONDOWN: 
		drawing_box = false;
		box = cvRect(x,y,0,0);
		break;

	//左鍵抬起
	case CV_EVENT_LBUTTONUP:
		if (drawing_box) {
			drawing_box = false;
			//確保選定框長寬正值
			if (box.width < 0) {
				box.x += box.width;
				box.width *= -1;
			}
			if (box.height < 0) {
				box.y += box.height;
				box.height *= -1;
			}
			move_zoom_image(image, box);
		}
		else {
			box = cvRect(x-IMAGE_SIZE_WID/2, y-IMAGE_SIZE_HGT/2, IMAGE_SIZE_WID, IMAGE_SIZE_HGT);
			move_zoom_image(image, box);
		}
		break;

	//右鍵按下
	case CV_EVENT_RBUTTONDOWN:
		moving = false;
		mov_x = x;
		mov_y = y;
		break;

	//右鍵放開
	case CV_EVENT_RBUTTONUP: 
		if (moving) {
			moving = false;
			box = cvRect(mov_x - x, mov_y - y, IMAGE_SIZE_WID, IMAGE_SIZE_HGT);
			move_zoom_image(image, box, 0);
		}
		else {
			box = cvRect(-IMAGE_SIZE_WID/2, -IMAGE_SIZE_HGT/2, IMAGE_SIZE_WID*2, IMAGE_SIZE_HGT*2);
			move_zoom_image(image, box);
		}
		break;
	}
}
/*

1100300627 薛怡然 製作

環境  Visual Studio 2010 + OpenCV 2.1
      Code::Blocks 10.05 + MinGW 4.4.1 + OpenCV 2.1

本機編譯、運行通過

實現功能:Task 2 ~ Task 6

附加功能:右鍵拖動平移、多重採樣抗鋸齒、動畫縮放、朱利亞集、多線程繪圖、多線程搜索複雜區域、用戶配置文件等

*/

#include 		//C++
#include 
#include 
#include 		//STL
#include 
#include 
#include 		//C
#include 
#include 
#include 	//WIN32
#include 			//OpenCV
#include 

#define LIMIT_SQUARE 4		//迭代閾值(2)的平方

//以下全局變數可通過設置函數讀取 config.ini 進行更改

char name_m[] = "Mandelbrot";	//窗口名
char name_j[] = "Julia";
char* window_name = name_m;

double julia_set = false;		//分形種類:false:曼得波羅集 true:朱利亞集

//為統一介面,定義:
//曼得波羅集:		z[n+1] = z[n]^2 + c
//朱利亞集:		c[n+1] = c[n]^2 + j_c

double j_c_re = -0.726895347709114071439;	//朱利亞集中 j_c 的實部
double j_c_im = 0.188887129043845954792;	//朱利亞集中 j_c 的虛部

bool ssaa = true;			//4x多重採樣抗鋸齒開關
int max_iteration = 300;	//最大迭代次數
int img_width = 800;		//圖像寬度
int img_height = 600;		//圖像高度
int animation = 20;			//動畫縮放效果步數
int max_zoom = 10;			//自動縮放最大次數
int zoom_partition = 4;		//自動縮放畫面每軸分區數
int zoom_delay = 3000;		//自動縮放延遲(毫秒)
double ctr_re = -0.5, ctr_im = 0;	//圖像中心複平面坐標
double img_left = -2;				//圖像左邊緣複平面實軸坐標

int draw_partition = 8;		//繪圖線程數(一般會再多出一個)
int draw_thread = 0;		//繪圖線程返回計數
int beauty_thread = 0;      //區域複雜度線程返回計數

IplImage *img, *tmp;		//img:主體圖像 tmp:用於動態顯示的臨時圖像
CvRect box;					//臨時線框
bool drawing_box = false;	//繪製線框狀態
CvPoint move_from, move_to;	//右鍵平移範圍
bool moving = false;		//右鍵平移狀態

double pi = acos(-1.0);		//上色三角函數用 π
double ratio_r = 30;		//上色三角函數自變數係數(倒數)
double ratio_g = 25;
double ratio_b = 20;
double phase_r = 0;			//上色三角函數初相 / π
double phase_g = 0;
double phase_b = 0.25;

using namespace std;
using namespace cv;

//測試C是否屬於M_Set
int xMSetTest(double c_re, double c_im) {
	double z_re = 0, z_im = 0, temp;
    int step = 0;
    while (z_re*z_re + z_im*z_im < LIMIT_SQUARE && step != max_iteration) {
		temp = z_re * z_re - z_im * z_im;
		z_im = 2 * z_re * z_im + c_im;
		z_re = temp + c_re;
        step++;
    }
    if (z_re*z_re + z_im*z_im < LIMIT_SQUARE && step == max_iteration)
		return 0;
    else return step;
}

//測試C是否屬於J_SET
int xJSetTest(double c_re, double c_im) {
	double temp;
	int step = 0;
	while (c_re*c_re + c_im*c_im < LIMIT_SQUARE && step != max_iteration) {
		temp = c_re * c_re - c_im * c_im;
		c_im = 2 * c_re * c_im + j_c_im;
		c_re = temp + j_c_re;
        step++;
    }
	if (c_re*c_re + c_im*c_im < LIMIT_SQUARE && step == max_iteration)
		return 0;
	else return step;
}

//測試函數指針,用於給 M_SET 與 J_SET 測試提供同樣介面
int (*xSetTest)(double c_re, double c_im) = xMSetTest;

//整型絕對值
inline int xAbs(int x) {
	return (x > 0) ? x : -x;
}

//像素坐標轉換至複平面坐標(實軸)
inline double xRe(double x) {
	return ctr_re + (2 * x - img_width)
		* (ctr_re - img_left) / img_width;
}

//像素坐標轉換至複平面坐標(虛軸)
inline double xIm(double y) {
	return ctr_im - (2 * y - img_height)
		* (ctr_re - img_left) / img_width;
}

//拷貝圖像矩形區域(允許越界,越界部分填黑)
IplImage* xCloneImageOver(IplImage* src, CvRect rect) {
	IplImage *dst = cvCreateImage(cvSize(rect.width, rect.height), src->depth, src->nChannels);
	cvSetZero(dst);
	CvRect src_range = rect;
	CvRect dst_range = cvRect(0, 0, rect.width, rect.height);
	//檢查四側越界,適當縮小複製範圍
	if (src_range.x < 0) {
		src_range.width += src_range.x;
		dst_range.width += src_range.x;
		dst_range.x -= src_range.x;
		src_range.x = 0;
	}
	if (src_range.y < 0) {
		src_range.height += src_range.y;
		dst_range.height += src_range.y;
		dst_range.y -= src_range.y;
		src_range.y = 0;
	}
	if (src_range.x + src_range.width > src->width) {
		int diff = src_range.x + src_range.width - src->width;
		src_range.width -= diff;
		dst_range.width -= diff;
	}
	if (src_range.y + src_range.height > src->height) {
		int diff = src_range.y + src_range.height - src->height;
		src_range.height -= diff;
		dst_range.height -= diff;
	}
	//檢查完全越界狀態
	if (src_range.height <= 0 || src_range.width <= 0) return dst;
	//複製圖像內容
	for (int i = 0; i < src_range.height; i++) {
		char* src_ptr = src->imageData + (src_range.y + i) * src->widthStep +
			src_range.x * (src->depth / 8) * src->nChannels;
		char* dst_ptr = dst->imageData + (dst_range.y + i) * dst->widthStep +
			dst_range.x * (src->depth / 8) * src->nChannels;
		memcpy(dst_ptr, src_ptr, src_range.width * (src->depth / 8) * src->nChannels);
	}
	return dst;
}

//定義像素顏色 & 4xAA
CvScalar xColorAA(double x, double y) {
	double result[4];
	CvScalar color = cvScalar(0);
	result[0] = xSetTest(xRe(x - 0.125), xIm(y - 0.25));
	result[1] = xSetTest(xRe(x + 0.25), xIm(y - 0.125));
	result[2] = xSetTest(xRe(x + 0.125), xIm(y + 0.25));
	result[3] = xSetTest(xRe(x - 0.25), xIm(y + 0.125));
	for (int i=0; i<4; i++) {
		if (result[i]) {
			color.val[0] += 127.5 - 127.5 * cos(result[i] / ratio_b + phase_b * pi);
			color.val[1] += 127.5 - 127.5 * cos(result[i] / ratio_g + phase_g * pi);
			color.val[2] += 127.5 - 127.5 * cos(result[i] / ratio_r + phase_r * pi);
		} else {
			color.val[0] += 0;
			color.val[1] += 0;
			color.val[2] += 0;
		}
	}
	color.val[0] /= 4;
	color.val[1] /= 4;
	color.val[2] /= 4;
	return color;
}

//定義像素顏色 & noAA
CvScalar xColorNoAA(double x, double y) {
	double result = xSetTest(xRe(x),xIm(y));
	CvScalar color;
	if (result) {
		color.val[0] = 127.5 - 127.5 * cos(result / ratio_b + phase_b * pi);
		color.val[1] = 127.5 - 127.5 * cos(result / ratio_g + phase_g * pi);
		color.val[2] = 127.5 - 127.5 * cos(result / ratio_r + phase_r * pi);
	} else {
		color.val[0] = 0;
		color.val[1] = 0;
		color.val[2] = 0;
	}
	return color;
}

//定義像素顏色函數指針,用於給 xColorAA 和 xColorNoAA 提供同樣介面
CvScalar (*xColor)(double x, double y) = xColorNoAA;

//單個繪製圖像線程
DWORD WINAPI xDrawThread(LPVOID param) {
	pair *range = (pair*)param;
	for (int i=range->first; isecond; i++) {
		for (int j=0; j > param(draw_partition + 1);
	draw_thread = 0;
	//多線程繪製
	for (int i=0; i 10000) x = 0;
		if (y > 10000) y = 0;
		if (flags & CV_EVENT_FLAG_LBUTTON) { //左鍵拖動
			drawing_box = true;
			box.width = x - box.x;
			box.height = y - box.y;
			if (xAbs(box.width) * img_height > xAbs(box.height) * img_width) {
				box.height = ((box.height > 0) ? 1 : -1) *
					xAbs(box.width) * img_height / img_width;
			} else {
				box.width = ((box.width > 0) ? 1 : -1) *
					xAbs(box.height) * img_width / img_height;
			}
		} else if (flags & CV_EVENT_FLAG_RBUTTON) { //右鍵拖動
			moving = true;
			move_to.x = 2 * move_from.x - x;
			move_to.y = 2 * move_from.y - y;
		}
		break;
	case CV_EVENT_LBUTTONDOWN: //左鍵按下
		drawing_box = false;
		box = cvRect(x,y,0,0);
		break;
	case CV_EVENT_LBUTTONUP: //左鍵放開
		if (drawing_box && xAbs(box.width) > 2) {
			drawing_box = false;
			if (box.width < 0) {
				box.x += box.width;
				box.width *= -1;
			}
			if (box.height < 0) {
				box.y += box.height;
				box.height *= -1;
			}
			xZoom(box);
		} else {
			drawing_box = false;
			xZoom(cvRect(x - img_width / 2, y - img_height / 2,
				img_width, img_height));
		}
		break;
	case CV_EVENT_RBUTTONDOWN: //右鍵按下
		moving = false;
		move_from.x = x;
		move_from.y = y;
		break;
	case CV_EVENT_RBUTTONUP: //右鍵放開
		if (moving) {
			moving = false;
			double t_ctr_re = xRe(move_to.x - move_from.x + img_width / 2.0);
			double t_ctr_im = xIm(move_to.y - move_from.y + img_height / 2.0);
			img_left = xRe(move_to.x - move_from.x);
			ctr_re = t_ctr_re;
			ctr_im = t_ctr_im;
			xReDraw();
		} else {
			xZoom(cvRect(x, y, 0, 0), 0.5);
		}
		break;
	}
}

//計算區域複雜度線程參數
struct BeautyParam {
	double result;
	CvPoint begin, end;
};

//計算區域複雜度線程
DWORD WINAPI xBeautyThread(LPVOID thread_param) {
	BeautyParam *param = (BeautyParam*)thread_param;
	int real_num = (param->end.x - param->begin.x) * (param->end.y - param->begin.y);
	int color_num = real_num;
	CvScalar average = cvScalar(0);
	param->result = 0;
	uchar *ptr;
	//計算平均值,黑色不計
	for (int i=param->begin.y; iend.y; i++) {
		ptr = (uchar*)(img->imageData + i * img->widthStep);
		for (int j=param->begin.x; jend.x; j++) {
			if (ptr[3 * j] || ptr[3 * j + 1] || ptr[3 * j + 2]) {
				average.val[0] += ptr[3 * j];
				average.val[1] += ptr[3 * j + 1];
				average.val[2] += ptr[3 * j + 2];
			} else {
				color_num--;
			}
		}
	}
	if (color_num) {
		average.val[0] /= color_num;
		average.val[1] /= color_num;
		average.val[2] /= color_num;
	}
	//計算方差,黑色不計入平方和,但計入分母數
	for (int i=param->begin.y; iend.y; i++) {
		ptr = (uchar*)(img->imageData + i * img->widthStep);
		for (int j=param->begin.x; jend.x; j++) {
			if (ptr[3 * j] || ptr[3 * j + 1] || ptr[3 * j + 2]) {
				param->result += (ptr[3 * j] - average.val[0]) *
					(ptr[3 * j] - average.val[0]);
				param->result += (ptr[3 * j + 1] - average.val[1]) *
					(ptr[3 * j + 1] - average.val[1]);
				param->result += (ptr[3 * j + 2] - average.val[2]) *
					(ptr[3 * j + 2] - average.val[2]);
			}
		}
	}
	param->result /= real_num;
	beauty_thread--;
	return 0;
}

//查找複雜區域
int xFindBeauty(){
	vector param(zoom_partition * zoom_partition);
	int unit_x = img_width / zoom_partition;
	int unit_y = img_height / zoom_partition;
	CvRNG seed = cvRNG(time(0));
	//圖像分區,多線程計算方差
	beauty_thread = 0;
	for (int i=0; i param[max].result) max = i;
	}
	return max;
};

//自動查找複雜區域並縮放
void xAutoZoom() {
	int zoom_to;
	int unit_x = img_width / zoom_partition;
	int unit_y = img_height / zoom_partition;
	CvRect auto_box;
	cout << "Auto Zoom" << endl;
	for (int i=0; i= 40 && img_height >= 40){
		cvCopyImage(img, tmp);
		cvRectangle(tmp, cvPoint(20, 20),
			cvPoint(img_width - 20, img_height - 20),
			cvScalar(0xff,0xff,0xff), 2);
		cvRectangle(tmp, cvPoint(20, 20),
			cvPoint(img_width - 20, img_height - 20),
			cvScalar(0xff,0x00,0x00));
		cvShowImage(window_name, tmp);
		cvWaitKey(1000);
		cvShowImage(window_name, img);
	}
	cout << "Manual Operate" << endl;
}

//循環繪製圖像並接收用戶輸入
void xLoop() {
	box = cvRect(-1,-1,0,0);
	int key = 0;
	xReDraw();
	for (;;) {
		if (drawing_box) { //左鍵選定矩形區域時
			cvCopyImage(img,tmp);
			cvRectangle(tmp,
				cvPoint(box.x, box.y),
				cvPoint(box.x + box.width, box.y + box.height),
				cvScalar(0xff,0xff,0xff), 2);
			cvRectangle(tmp,
				cvPoint(box.x, box.y),
				cvPoint(box.x + box.width, box.y + box.height),
				cvScalar(0xff,0x00,0x00));
			cvShowImage(window_name,tmp);
		} else if (moving) { //右鍵拖動平移圖像時
			IplImage *move = xCloneImageOver(img, cvRect(
				move_to.x - move_from.x,
				move_to.y - move_from.y,
				img_width,
				img_height));
			cvShowImage(window_name, move);
			cvReleaseImage(&move);
		} else { //空閑時
			cvShowImage(window_name, img);
		}
		cvSetMouseCallback(window_name, xOnMouse);
		if (drawing_box || moving) key = cvWaitKey(15);
		else key = cvWaitKey(100);
		if (key == 27) return;
		if (key == 'a') xAutoZoom();
	}
}

//讀取設置文件
void xSetting() {
	ifstream fi("config.ini");
	string cmd;
	double val;
	//隨機生成圖像顏色
	CvRNG seed = cvRNG(time(0));
	ratio_r = cvRandReal(&seed) * 50 + 10;
	ratio_g = cvRandReal(&seed) * 50 + 10;
	ratio_b = cvRandReal(&seed) * 50 + 10;
	phase_r = cvRandReal(&seed) * 2 * pi;
	phase_g = cvRandReal(&seed) * 2 * pi;
	phase_b = cvRandReal(&seed) * 2 * pi;
	//讀取用戶設置
	while (fi >> cmd >> val) {
		if (cmd == "julia_set" && val == 1) {
			julia_set = true;
			window_name = name_j;
			xSetTest = xJSetTest;
			ctr_re = 0;
			ctr_im = 0;
			img_left = -1.5;
		}
		else if (cmd == "ssaa" || cmd == "4xAA") ssaa = (val != 0);
		else if (cmd == "max_iteration") max_iteration = (int)val;
		else if (cmd == "img_width" && val > 0) img_width = (int)val;
		else if (cmd == "img_height" && val > 0) img_height = (int)val;
		else if (cmd == "animation" && val >= 1) animation = (int)val;
		else if (cmd == "max_zoom" && val >= 1) max_zoom = (int)val;
		else if (cmd == "zoom_partition" && val >= 1) zoom_partition = (int)val;
		else if (cmd == "zoom_delay" && val >= 1) zoom_delay = (int)val;
		else if (cmd == "draw_partition" && val >= 1) draw_partition = (int)val;
		else if (cmd == "ctr_re") ctr_re = val;
		else if (cmd == "ctr_im") ctr_im = val;
		else if (cmd == "img_left") img_left = val;
		else if (cmd == "ratio_r" && val != 0) ratio_r = val;
		else if (cmd == "ratio_g" && val != 0) ratio_g = val;
		else if (cmd == "ratio_b" && val != 0) ratio_b = val;
		else if (cmd == "phase_r") phase_r = val;
		else if (cmd == "phase_r") phase_r = val;
		else if (cmd == "phase_r") phase_r = val;
		else if (cmd == "j_c_re") j_c_re = val;
		else if (cmd == "j_c_im") j_c_im = val;
	}
	//檢查複平面設置
	if (img_left > ctr_re) img_left = 2 * ctr_re - img_left;
	//設置計算像素顏色函數指針
	if (ssaa) xColor = xColorAA;
}

//創建和刪除圖像
int main() {
	xSetting();
	cout << "Use mouse to zoom in, zoom out, or move the image." << endl;
	cout << "Press A in the image window to enter Auto Zoom mode." << endl << endl;
	img = cvCreateImage(cvSize(img_width, img_height),/*depth*/8,/*nChannels*/3);
	tmp = cvCloneImage(img);
	xLoop();
	cvReleaseImage(&img);
	cvReleaseImage(&tmp);
	return 0;
}

參考資料

OpenCV 中文站
M67 Pascal 的繪圖程序..