2014-05-06 15:21:09 u012244950 阅读数 1389
  • 数据结构与算法(C/C++实现)视频教程

    C/C++实现数据结构与算法视频培训课程全面介绍计算机行业,基本的数据结构与算法,既有理论的深度也有实战的技法。课程全程讲师手敲代码,一步步代你走进数据结构与算法。 本课程涉及的数据结构与算法有,栈,队列,单向链表,双向循环链表,树,二叉树,搜索二叉树,平衡搜索二叉树,冒泡,选择,直插,希尔,,归并等,课程还涉及深度优先算法与广度优先算法等等。

    3629 人正在学习 去看看 王桂林

c++实现代码:

int otsu(IplImage* src)    
{    
    int height = src -> height;    
    int width = src -> width;   
    //------------直方图 ---------------   
    float histogram[256] = {0};   
    for(int i = 0; i < height; i++)   
    {    
        unsigned char* p = (unsigned char*)src -> imageData + src -> widthStep * i;    
        for(int j = 0; j < width; j++)   
        {    
            histogram[*p++]++;  
        }    
    }   
    //-----------直方图归一化--------    
    int size = height * width;    
    for(int i = 0; i < 256; i++)   
    {    
        histogram[i] = histogram[i] / size;    
    }    
    //-----------灰度均值 ----------   
    float avgValue = 0;    
    for(int i = 0; i < 256; i++)  
    {    
        avgValue+= i * histogram[i];    
    }    
  
    int threshold;           //阈值      
    float maxVariance = 0;   //最大类间方差    
    float w = 0;             //直方图零阶矩  
    float u = 0;             //直方图一级矩    
    //--------------求阈值---------------  
    for(int i = 0; i < 256; i++)   
    {    
        w+= histogram[i];    
        u+= i * histogram[i];    
  
        float t = avgValue * w - u;    
        float variance = t * t / (w * (1 - w));  //类间方差  
        if(variance > maxVariance)   
        {    
            maxVariance = variance;    
            threshold = i;    
        }    
    }  
    return threshold;  
}   


2018-10-28 23:02:40 Aidam_Bo 阅读数 607
  • 数据结构与算法(C/C++实现)视频教程

    C/C++实现数据结构与算法视频培训课程全面介绍计算机行业,基本的数据结构与算法,既有理论的深度也有实战的技法。课程全程讲师手敲代码,一步步代你走进数据结构与算法。 本课程涉及的数据结构与算法有,栈,队列,单向链表,双向循环链表,树,二叉树,搜索二叉树,平衡搜索二叉树,冒泡,选择,直插,希尔,,归并等,课程还涉及深度优先算法与广度优先算法等等。

    3629 人正在学习 去看看 王桂林


 

//添加椒盐噪声
void salt(Mat& src,int number)
{
    for (int i = 0; i < number; i++)
    {
        int r = static_cast<int>(rng.uniform(0, src.rows));
        int c = static_cast<int>(rng.uniform(0, src.cols));
        int k = (static_cast<int>(rng.uniform(0, 1000))&1);
        if(k==1)
            src.at<uchar>(r, c) = 255;
        else
            src.at<uchar>(r, c) = 0;
    }
    return;
}
/*
* @ drt :高斯方差
* @ Medium :高斯均值
*/
int Get_Gauss(int Medium, int drt)
{
    //产生高斯样本,以U为均值,D为均方差
    double sum = 0;
    for (int i = 0; i<12; i++) 
        sum += rand() / 32767.00;
    //计算机中rand()函数为-32767~+32767(2^15-1)
    //故sum+为0~1之间的均匀随机变量
    return int(Medium + drt*(sum - 6));
    //产生均值为U,标准差为D的高斯分布的样本,并返回
}
/*
* variance :高斯噪声的方差
*/


 

//添加高斯噪声
void ImgAddGaussNoise1( uchar * dstImgbuff, int srcwith, int srcheigh, int chanels)
{
    assert( srcwith > 0 && srcheigh > 0);
    int bytecount = srcwith * srcheigh * chanels;
 
    for (size_t i = 0; i < bytecount; i++)
    {
        int  iTemp = dstImgbuff[i] + Get_Gauss(0, 20);
        iTemp = iTemp > 255 ? 255 : iTemp;
        iTemp = iTemp < 0 ? 0 : iTemp;
        dstImgbuff[i] = iTemp;
    }
}


 

//均值求取
void Meanvalue(Mat* src, int indexrows, int indexcols, float* meanv, int ker)
{
    int lo = (ker - 1) / 2;
    float total = 0;
    for (int i = indexrows - lo; i <= indexrows + lo; i++)
    {
        for (int j = indexcols - lo; j <= indexcols + lo; j++)
        {
            total += src->at<uchar>(i, j);
        }
    }
    *meanv = total / (ker * ker);
    return;
}
//中值求取
void Media(Mat* src, int indexrows, int indexcols, int* meanv, int ker)
{
    int lo = (ker - 1) / 2;
    vector<int>moreo;
    for (int i = indexrows - lo; i <= indexrows + lo; i++)
    {
        for (int j = indexcols - lo; j <= indexcols + lo; j++)
        {
            moreo.push_back(src->at<uchar>(i, j));
        }
    }
    sort(moreo.begin(), moreo.end());
    *meanv = moreo.at(ker * ker / 2);
    return;
}


 

//局部方差求取
void Vvalue(Mat* src, int indexrows, int indexcols, int* vall, int ker, float mean)
{
    int lo = (ker - 1) / 2;
    float total = 0;
    for (int i = indexrows - lo; i <= indexrows + lo; i++)
    {
        for (int j = indexcols - lo; j <= indexcols + lo; j++)
        {
            total += pow((src->at<uchar>(i, j) - mean), 2);
        }
    }
    *vall = static_cast<int>(total);
    return;
}

 

//像素方差
void Variance(Mat& src, vector<test>& hierachy, int ker)
{
    int row = src.rows;
    int col = src.cols;
    int lo = (ker - 1) / 2;
    for (int ir = lo; ir < row - lo; ir++)
    {
        for (int jc = lo; jc < col - lo; jc++)
        {
            float means;
            int var;
            //计算均值
            Meanvalue(&src, ir, jc, &means, ker);
            Vvalue(&src, ir, jc, &var, ker, means);
            test temp;
            temp.menval = var;
            temp.x = ir;
            temp.y = jc;
            hierachy.push_back(temp);
        }
    }
    return;
}

 

//STL排序方式
bool SortByM1(const test &v1, const test &v2)//注意:本函数的参数的类型一定要与vector中元素的类型一致  
{
    return v1.menval < v2.menval;//升序排列  
}

 

//SSIM 结构相似比
Scalar getMSSIM(const Mat& i1, const Mat& i2)
{
    const double C1 = 6.5025, C2 = 58.5225;
    /***************************** INITS **********************************/
    int d = CV_32F;
 
    Mat I1, I2;
    i1.convertTo(I1, d);           // cannot calculate on one byte large values
    i2.convertTo(I2, d);
 
    int num = I1.channels();
    //cv::imshow("123", I1);
    //cv::waitKey();
 
    Mat I2_2 = I2.mul(I2);        // I2^2
    Mat I1_2 = I1.mul(I1);        // I1^2
    Mat I1_I2 = I1.mul(I2);        // I1 * I2
 
                                   /*************************** END INITS **********************************/
 
    Mat mu1, mu2;   // PRELIMINARY COMPUTING
    GaussianBlur(I1, mu1, Size(11, 11), 1.5);
    GaussianBlur(I2, mu2, Size(11, 11), 1.5);
 
    Mat mu1_2 = mu1.mul(mu1);
    Mat mu2_2 = mu2.mul(mu2);
    Mat mu1_mu2 = mu1.mul(mu2);
 
    Mat sigma1_2, sigma2_2, sigma12;
 
    GaussianBlur(I1_2, sigma1_2, Size(11, 11), 1.5);
    sigma1_2 -= mu1_2;
 
    GaussianBlur(I2_2, sigma2_2, Size(11, 11), 1.5);
    sigma2_2 -= mu2_2;
 
    GaussianBlur(I1_I2, sigma12, Size(11, 11), 1.5);
    sigma12 -= mu1_mu2;
 
    ///////////////////////////////// FORMULA ////////////////////////////////
    Mat t1, t2, t3;
 
    t1 = 2 * mu1_mu2 + C1;
    t2 = 2 * sigma12 + C2;
    t3 = t1.mul(t2);              // t3 = ((2*mu1_mu2 + C1).*(2*sigma12 + C2))
 
    t1 = mu1_2 + mu2_2 + C1;
    t2 = sigma1_2 + sigma2_2 + C2;
    t1 = t1.mul(t2);               // t1 =((mu1_2 + mu2_2 + C1).*(sigma1_2 + sigma2_2 + C2))
 
    Mat ssim_map;
    divide(t3, t1, ssim_map);      // ssim_map =  t3./t1;
 
    Scalar mssim = mean(ssim_map); // mssim = average of ssim map
    return mssim;
}
//功能:局部均值求取 局部方差求取
/* zc 2018/07/08
parameters:
Mat*         src;         //待处理的图像
float*       meanv;       //保存局部均值
float*       dev;         //保存局部方差
int          indexrows;   //要求局部所在行
int          indexcols;   //要求局部所在列
int          ker;         //窗口大小系数
*/
void Meanvalue(Mat* src, float* meanv, float* dev, int indexrows, int indexcols,  int ker)
{
    int lo = (ker - 1) / 2;
    float total = 0;
    float total2 = 0;
    for (int i = indexrows - lo; i <= indexrows + lo; i++)
    {
        for (int j = indexcols - lo; j <= indexcols + lo; j++)
        {
            float temp = static_cast<float>(src->at<uchar>(i, j));
            total += temp;
            total2 += temp*temp;
        }
    }
    int size = ker * ker;
    *meanv = total / size;                                      //均值
    *dev = (total2 - (total*total) / size) / size;              //方差
    return;
}



 
 

2011-07-05 12:29:38 maozefa 阅读数 11875
  • 数据结构与算法(C/C++实现)视频教程

    C/C++实现数据结构与算法视频培训课程全面介绍计算机行业,基本的数据结构与算法,既有理论的深度也有实战的技法。课程全程讲师手敲代码,一步步代你走进数据结构与算法。 本课程涉及的数据结构与算法有,栈,队列,单向链表,双向循环链表,树,二叉树,搜索二叉树,平衡搜索二叉树,冒泡,选择,直插,希尔,,归并等,课程还涉及深度优先算法与广度优先算法等等。

    3629 人正在学习 去看看 王桂林

阅读提示

    《C++图像处理》系列以代码清晰,可读性为主,全部使用C++代码。

    《Delphi图像处理》系列以效率为侧重点,一般代码为PASCAL,核心代码采用BASM。

    尽可能保持二者内容一致,可相互对照。

    本文代码必须包括《C++图像处理 -- 数据类型及公用函数文章中的BmpData.h头文件。

 

    Photoshop提供了丰富的图象图层混合模式,其中的颜色混合模式是用下图层图象的亮度与上图层填充颜色或者图象色彩进行混合,形成的结果既有着上图层的色彩,又保留了下层图象的灰度,基于这种混合特性,颜色混合模式常用来对灰度图象进行着色。

    如何用程序代码准确地实现Photoshop的图层颜色混合模式,一直是程序员们热衷的话题。本文采用BCB2007和GDI+等工具,较好地实现了其主要功能(不考虑不透明度和填充选项)。

    按照Photoshop的解释,颜色混合模式是用上图层图象颜色的色相、饱和度与下图层图象像素的明度进行的混合。如此,我们在程序代码中,就需要首先将上层图象颜色的色相、饱和度和下图层图象颜色的明度(亮度)提取出来,色相、饱和度的提取是按照HSV的方式进行的,然后按照下图层颜色明度按照0.3R +0.59G + 0.11B 的比例逐像素进行运算合成,可事实上,我在颜色合成过程中,无论是采用HSV还是HSL甚或其它HSB方式,均没法达到应有的效果。例如取上层颜色R=225,G=211,B=179,提取的H,S分别为42,20%,下层灰度为179,采用HSV或者SHL合成颜色的G,B均为0,而实际合成的R,G,B应分别为192,178,146。

    通过在Photoshop中反复试验,发现上层颜色中的饱和度在合成过程中似乎没起什么作用,最终合成结果只要保证上层颜色色相和下层灰度的比例不变就行了,这也颜色混合模式的2个必要条件,其中灰度比例是必须保证的,如果二者发生冲突,可不考虑色相比例(如图象某像素的灰度为0或者255)。按照这个思路,我放弃了用HSB进行合成的方法,而按照上面2个条件采用解方程的方法来实现颜色混合。为此,可列出下列等式关系:

1:Max - Min = a

2:Mid - Min = b

3:0.3R + 0.59G + 0.11B = c

其中,Max,Mid,Min分别为上层颜色R、G、B分量中的最大、中间、最小值。等式1和2代表了上层颜色色相的比例关系,等式3则代表着下层颜色的灰度比例。

如果只考虑60度以内的色相和假定R>G>B,那么用上面的3个等式可列为下面的三元一次方程组:

1)  R - B = a

2)  G - B = b

3)  0.3R + 0.59G + 0.11B = c

    可以将满足色相在0 - 60范围,R>G>B的任何颜色的常数代入上面的方程组进行验算,其结果是正确的。但是实际的颜色混合是用2个颜色不同的灰度和色相,采用上面的方程组解出的RGB值有可能会超出0 -- 255的范围,而我们又无法在方程组中加入这种范围限制,因此对于超出范围的RGB值,还必须在程序代码中进行调整。

    Photoshop图层颜色混合模式在提取下层图像明度时,采用的是黑白调整方法,它实际上是一种灰度计算,但同一般的像素灰度计算方法有区别。计算公式为:

    bwGray = (Max - Min) * ratio_Max + (Mid - Min) * ratio_Max_Mid + Min

    公式中,bwGray为黑白灰度;Max,Mid和Min分别为RGB各分量的最大值,中间值和最小值;ratio_Max为像素中Max代表的颜色(单色)比率,ratio_Max_Mid为像素中Max和Mid所形成的间色比率。

    这个公式中单色和间色是Photoshop中的概念,不熟悉Photoshop的人可能不容易弄明白。举个例子就好懂了:

    某像素的r、g、b分别为200、100、50,那么Max=r=200,Mid=g=100,Min=b=50,显然ratio_Max为r(红色)的比率,而ratio_Max_Mid则为r(红色)与g(绿色)形成的间色(黄色)的比率,按上面的公式和Photoshop黑白调整的缺省比率(红色=40%,黄色=60%)计算该像素的黑白灰度值,则为:

        bwGray = (200 - 100) * 0.4 + (100 - 50) * 0.6 + 50 = 120

    按照这个原理,在Photoshop黑白功能调整对话框中,只有调整红色和黄色的比率才会对例子中的像素灰度值起作用。

    下面是我按照上面思路和公式写的全部程序代码: 

//---------------------------------------------------------------------------

typedef FLOAT		BWParams, *PBWParams;

// 黑白调整缺省参数:红,黄,绿,洋红,蓝,青
CONST INT _BWDefault[] = {410, 614, 410, 819, 205, 614};

enum
{
	BWIndexBlue		= 0x40000,
	BWIndexGreen	= 0x20000,
	BWIndexRed		= 0x00000
};

enum
{
	IndexBlue	= 0x00000,
	IndexGreen	= 0x10000,
	IndexRed	= 0x20000
};

typedef union 				// 颜色分量交换结构
{
	INT tmp;				// 交换时用的临时变量
	struct
	{
		SHORT value;		// 颜色分量值
		SHORT index;		// 颜色分量索引
	};
}RGBIndex;
//---------------------------------------------------------------------------

// 交换像素分量
FORCEINLINE
VOID SwapRgb(RGBIndex &a, RGBIndex &b)
{
	a.tmp ^= b.tmp;
	b.tmp ^= a.tmp;
	a.tmp ^= b.tmp;
}
//---------------------------------------------------------------------------

// 获取黑白灰度
FORCEINLINE
INT	GetBWGray(CONST PARGBQuad pixel, CONST PINT bwParams)
{
	RGBIndex max, mid, min;
	min.tmp = pixel->Blue | BWIndexBlue;
	mid.tmp = pixel->Green | BWIndexGreen;
	max.tmp = pixel->Red | BWIndexRed;

	if (max.value < mid.value)
		SwapRgb(max, mid);
	if (max.value < min.value)
		SwapRgb(max, min);
	if (min.value > mid.value)
		SwapRgb(min, mid);

	return (((max.value - mid.value) * bwParams[max.index] +
		(mid.value - min.value) * bwParams[max.index + mid.index - 1] +
		512) >> 10) + min.value;
}
//---------------------------------------------------------------------------

VOID ColorMix(PARGBQuad pd, CONST PARGBQuad ps, INT gray)
{
	// 灰度计算常数:蓝,绿、红
	CONST DOUBLE ys[] = {0.11, 0.59, 0.30};

	RGBIndex max, mid, min;
	min.tmp = ps->Blue | IndexBlue;
	mid.tmp = ps->Green | IndexGreen;
	max.tmp = ps->Red | IndexRed;

	if (max.value < mid.value)
		SwapRgb(max, mid);
	if (max.value < min.value)
		SwapRgb(max, min);
	if (min.value > mid.value)
		SwapRgb(min, mid);

	INT max_min = max.value - min.value;
	// 饱和度为0,返回灰度
	if (max_min == 0)
	{
		pd->Blue = pd->Green = pd->Red = gray;
		return;
	}
	INT mid_min = mid.value - min.value;
	DOUBLE hueCoef = (DOUBLE)mid_min / (DOUBLE)max_min;

	// 假设最大值=R,中间值=G,最小值=B,设置方程组:
	//   1): -B + R = max - min
	//   2): -B + G = mid - min
	//   3): 11B + 59G + 30R = Gray * 100
	INT e1[4], e2[4], e3[4], e4[4], e5[4], e6[4];
	e1[max.index] = 1;
	e1[mid.index] = 0;
	e1[min.index] = -1;
	e1[3] = max_min;
	e2[max.index] = 0;
	e2[mid.index] = 1;
	e2[min.index] = -1;
	e2[3] = mid_min;
	e3[0] = 11;
	e3[1] = 59;
	e3[2] = 30;
	e3[3] = gray * 100;

	// 解方程组:
	//  4): (1) - 2)) * 30
	//  5): 2) * 11
	//  6): 3) - 4) + 5)
	for (INT i = 0; i < 4; i ++)
	{
		e4[i] = (e1[i] - e2[i]) * e3[max.index];
		e5[i] = e2[i] * e3[min.index];
		e6[i] = e3[i] - e4[i] + e5[i];
	}

	INT newMax;
	// 求G解:6) / 100  (因灰度公式缘故,等式右边恒等于100)
	INT newMid = (e6[3] + 50) / 100;
	// 求B解:G代入 2)
	INT newMin = newMid - e2[3];
	// 如果B < 0,B = 0,同时按灰度比例和色相比例解二元一次方程求R、G
	// 方程式:1-1): 0.3R + 0.59G = Gray
	//         1-2): HueCoef * R - G = 0
	if (newMin < 0 || newMid <= 0)
	{
		newMax = (int)(gray / (ys[max.index] + ys[mid.index] * hueCoef) + 0.5);
		newMid = (int)(newMax * hueCoef + 0.5);
		newMin = 1;
	}
	// 否则求R解:G、B代入 1)
	else
	{
		newMax = newMin + e1[3];
		// 如果R > 255,R = 255,同时按灰度比例和色相比例解二元一次方程求G、B
		// 方程式:2-1): 0.59G + 0.11B = gray - 0.3 * 255
		//         2-2): G + (hueCoef - 1)B = 255 * hueCoef
		if (newMax > 255)
		{
			newMin = (INT)((gray - (ys[max.index] + ys[mid.index] * hueCoef) * 255) /
						(ys[min.index] - ys[mid.index] * (hueCoef - 1)) + 1.0);
			newMid = (INT)(newMin + (255 - newMin) * hueCoef + 0.5);
			newMax = 255;
		}
	}

	((LPBYTE)pd)[max.index] = newMax > 255? 255 : newMax;
	((LPBYTE)pd)[mid.index] = newMid > 255? 255 : newMid;
	((LPBYTE)pd)[min.index] = newMin > 255? 255 : newMin;
}
//---------------------------------------------------------------------------

// 图像黑白调整。
// 调整参数bwParams为元素数等于6的数组指针,分别为红,黄,绿,青,蓝,洋红
VOID ImageBlackWhite(BitmapData *data, CONST PBWParams bwParams = NULL)
{
	// 拷贝像素灰度参数,并交换青色和洋红色
	INT params[6], *pparams;
	if (bwParams)
	{
		for (INT i = 0; i < 6; i ++)
			params[i] = (INT)(bwParams[i] * 1024 + 0.5);
		params[3] ^= params[5];
		params[5] ^= params[3];
		params[3] ^= params[5];
		pparams = params;
	}
	else
		pparams = (INT*)_BWDefault;

	PARGBQuad p = (PARGBQuad)data->Scan0;
	INT dataOffset = (data->Stride >> 2) - (INT)data->Width;

	for (UINT y = 0; y < data->Height; y ++, p += dataOffset)
	{
		for (UINT x = 0; x < data->Width; x ++, p ++)
		{
			INT gray = GetBWGray(p, pparams);
			p->Blue = p->Green = p->Red =
				(gray & ~0xff) == 0? gray : gray > 255? 255 : 0;
		}
	}
}
//---------------------------------------------------------------------------

// 灰度图像染色。
VOID ImageTint(BitmapData *grayData, ARGB color)
{
	PARGBQuad p = (PARGBQuad)grayData->Scan0;
	INT dataOffset = (grayData->Stride >> 2) - (INT)grayData->Width;

	for (UINT y = 0; y < grayData->Height; y ++, p += dataOffset)
	{
		for (UINT x = 0; x < grayData->Width; x ++, p ++)
		{
			ColorMix(p, (PARGBQuad)&color, p->Blue);
		}
	}
}
//---------------------------------------------------------------------------

// 图像颜色模式混合
VOID ImageColorMixer(BitmapData *dest, CONST BitmapData *source)
{
	PARGBQuad pd, ps;
	UINT width, height;
	INT dstOffset, srcOffset;
	GetDataCopyParams(dest, source, width, height, pd, ps, dstOffset, srcOffset);

	for (UINT y = 0; y < height; y ++, pd += dstOffset, ps += srcOffset)
	{
		for (UINT x = 0; x < width; x ++, pd ++, ps ++)
		{
			ColorMix(pd, ps, GetBWGray(pd, (PINT)_BWDefault));
		}
	}
}
//---------------------------------------------------------------------------

    上面代码中,ColorMix函数写出了比较详细的解方程过程代码,并作了相应的注释;解三元一次方程组时,将灰度比例值扩大了100倍,可使用定点数运算,因为灰度比例关系恒等于100的缘故,运算过程中不会产生误差;其中对值超出0 - 255范围RGB值分别使用了2组二元一次方程进行了处理;另外,由于定义了一个RGBIndex类型,使得在比较和交换最大、最小值过程中,保存了原R、G、B信息,这不仅方便了代码中的运算,也使得前面的三元一次方程组的适用范围从色相60度以内和R>G>B,扩展到了色相全范围以及任意大小的R、G、B值,同时也避免了HSB转换为RGB时通常使用的switch条件语句。

    为了检验本文思路和代码是否正确,用本文代码进行黑白调整、灰度图染色和图像颜色混合后,同Photoshop同样参数调整形成的图像逐像素进行了比较,下面是个简单的比较函数代码:

//---------------------------------------------------------------------------

void ImageCompare(Bitmap *bmp1, Bitmap *bmp2)
{
	int count, r_count = 0, g_count = 0, b_count = 0;
	int diff, r_diff = 0, g_diff = 0, b_diff = 0;
	BitmapData data1, data2;
	Gdiplus::Rect r(0, 0, bmp1->GetWidth(), bmp1->GetHeight());

	bmp1->LockBits(&r, ImageLockModeRead, PixelFormat24bppRGB, &data1);
	bmp2->LockBits(&r, ImageLockModeRead, PixelFormat24bppRGB, &data2);
	try
	{
		PRGBTRIPLE p1 = (PRGBTriple)data1.Scan0;
		PRGBTRIPLE p2 = (PRGBTriple)data2.Scan0;
		int offset = data1.Stride - data1.Width * sizeof(RGBTRIPLE);
		for (unsigned y = 0; y < data1.Height; y ++, (char*)p1 += offset, (char*)p2 += offset)
		{
			for (unsigned x = 0; x < data1.Width; x ++, p1 ++, p2 ++)
			{
				diff = p1->rgbtRed - p2->rgbtRed;
				if (diff)
				{
					r_count ++;
					if (diff < 0) diff = -diff;
					if (r_diff < diff) r_diff = diff;
				}
				diff = p1->rgbtGreen - p2->rgbtGreen;
				if (diff)
				{
					g_count ++;
					if (diff < 0) diff = -diff;
					if (g_diff < diff) g_diff = diff;
				}
				diff = p1->rgbtBlue - p2->rgbtBlue;
				if (diff)
				{
					b_count ++;
					if (diff < 0) diff = -diff;
					if (b_diff < diff) b_diff = diff;
				}
			}
		}
	}
	__finally
	{
		bmp2->UnlockBits(&data2);
		bmp1->UnlockBits(&data1);
	}
	count = data1.Width * data1.Height;
	String s;
	s.sprintf(L"像素总数:%d\n"	\
			  L"红误差数:%d,误差率:%d%%,最大误差:%d\n"	\
			  L"绿误差数:%d,误差率:%d%%,最大误差:%d\n"	\
			  L"蓝误差数:%d,误差率:%d%%,最大误差:%d",
			  count, r_count, (r_count * 100) / count, r_diff,
			  g_count, (g_count * 100) / count, g_diff,
			  b_count, (b_count * 100) / count, b_diff);
	ShowMessage(s);
}
//---------------------------------------------------------------------------

    首先对图像进行黑白调整,请看下面的例子:

//---------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)
{
	Gdiplus::Bitmap *bmp =  new Gdiplus::Bitmap(L"..\\..\\media\\Source.bmp");

	BitmapData data;
	LockBitmap(bmp, &data);
	ImageBlackWhite(&data);
	UnlockBitmap(bmp, &data);

	Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle);
	g->DrawImage(bmp, 0, 0);
	delete g;

	Gdiplus::Bitmap *bmp2 =  new Gdiplus::Bitmap(L"..\\..\\media\\GraySource.bmp");
	ImageCompare(bmp, bmp2);
	delete bmp2;
	delete bmp;
}
//---------------------------------------------------------------------------

    下面是原图像:

    黑白调整例子程序运行界面截图,比较后的误差为0:

  

    在做一个图像染色的例子:

//---------------------------------------------------------------------------

void __fastcall TForm1::Button2Click(TObject *Sender)
{
	Gdiplus::Bitmap *bmp =  new Gdiplus::Bitmap(L"..\\..\\media\\Source.bmp");

	BitmapData data;
	LockBitmap(bmp, &data);
	ImageBlackWhite(&data);
	ImageTint(&data, 0xff314ead);
	UnlockBitmap(bmp, &data);

	Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle);
	g->DrawImage(bmp, 0, 0);
	delete g;

	Gdiplus::Bitmap *bmp2 =  new Gdiplus::Bitmap(L"..\\..\\media\\Source314ead.bmp");
	ImageCompare(bmp, bmp2);
	delete bmp2;
	delete bmp;
}
//---------------------------------------------------------------------------

    运行结果如下:


 

    染色误差为1。

    最后再以上面的原图为背景(下层图象),和一副风景图象(上层图像)做颜色模式混合:

//---------------------------------------------------------------------------

void __fastcall TForm1::Button3Click(TObject *Sender)
{
	Gdiplus::Bitmap *bmp =  new Gdiplus::Bitmap(L"..\\..\\media\\Source.bmp");
	Gdiplus::Bitmap *bmp1 =  new Gdiplus::Bitmap(L"..\\..\\media\\Test1.bmp");

	BitmapData dest, source;
	LockBitmap(bmp, &dest);
	LockBitmap(bmp1, &source);
	ImageColorMixer(&dest, &source);
	UnlockBitmap(bmp1, &source);
	UnlockBitmap(bmp, &dest);
	delete bmp1;

	Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle);
	g->DrawImage(bmp, 0, 0);
	delete g;

	Gdiplus::Bitmap *bmp2 =  new Gdiplus::Bitmap(L"..\\..\\media\\TestMix.bmp");
	ImageCompare(bmp, bmp2);
	delete bmp2;
	delete bmp;
}
//---------------------------------------------------------------------------

    前景图像: 

    运行结果图:

    误差为2,而且误差率也不高。

    综合看来,本文代码的可行度是较高的。 

    因水平有限,错误在所难免,欢迎指正和指导。邮箱地址:maozefa@hotmail.com

    这里可访问《C++图像处理 -- 文章索引

2012-12-07 22:10:43 maozefa 阅读数 11687
  • 数据结构与算法(C/C++实现)视频教程

    C/C++实现数据结构与算法视频培训课程全面介绍计算机行业,基本的数据结构与算法,既有理论的深度也有实战的技法。课程全程讲师手敲代码,一步步代你走进数据结构与算法。 本课程涉及的数据结构与算法有,栈,队列,单向链表,双向循环链表,树,二叉树,搜索二叉树,平衡搜索二叉树,冒泡,选择,直插,希尔,,归并等,课程还涉及深度优先算法与广度优先算法等等。

    3629 人正在学习 去看看 王桂林

阅读提示

    《C++图像处理》系列以代码清晰,可读性为主,全部使用C++代码。

    《Delphi图像处理》系列以效率为侧重点,一般代码为PASCAL,核心代码采用BASM。

    尽可能保持二者内容一致,可相互对照。

    本文代码必须包括《C++图像处理 -- 数据类型及公用函数文章中的BmpData.h头文件。

 

    图像的表面模糊处理是Photoshop CS2以后才有的新功能,其作用是在保留图像边缘的情况下,对图像的表面进行模糊处理。在对人物皮肤处理上,比高斯模糊更有效。因为高斯模糊在使人物皮肤光洁的同时,也将一些边缘特征如脸部的眉毛、嘴唇等给模糊了,不得不用蒙版小心的抹去这些地方的模糊部分。

    在处理手法上,表面模糊也与其它卷积处理手段不同,如高斯模糊等在处理图像时都是采用统一的卷积矩阵进行,而表面模糊却是每一个像素点都有自己的卷积矩阵,而且还是3(4)套,用以对应于像素的R、G、B(A、R、G、B)分量。所以表面模糊在编程处理时,比其它卷积操作更复杂、更耗时,因为它要对每一个像素计算自己的卷积矩阵。表面模糊编程的难点也在计算卷积矩阵上,其它与一般图像卷积处理一样。

    表面模糊处理有2个参数,即模糊半径和模糊阈值,前者确定模糊的范围,后者确定模糊的程度。模糊范围就是卷积矩阵大小,如模糊半径为1,则模糊矩阵直径为1*2+1等于3,矩阵元素个数为3*3等于9,矩阵的中间元素即是当前像素点。

    矩阵元素值的计算公式为:

    mij = 1 - (|pij - p0|) / 2.5T

    其中,mij为矩阵的各元素值,pij为矩阵元素对应的像素分量值,p0为矩阵中心元素对应的像素分量值,T为阈值,|pij - p0|为矩阵元素对应像素分量值与中心元素对应像素分量值的绝对差值。如果mij < 0,则mij = 0。

    对ARGB格式图像数据来说,因有4个分量,故需要4套卷积矩阵。

    矩阵元素确定后,就可按照一般图像卷积操作进行处理了,即分别累计矩阵元素值和与之对应的像素分量值乘积,用累计像素分量值除以累计元素值,即可得到当前像素分量模糊处理后的值。

    下面是表面模糊处理代码:

//---------------------------------------------------------------------------

// ARGB图像数据表面模糊处理
// 参数: 图像数据, 模糊半径(1 - 100), 阈值(2 - 255)
VOID SurfaceBlur(BitmapData *data, UINT radius, UINT threshold)
{
	if (radius < 1 || radius > 100 || threshold < 2 || threshold > 255)
		return;

	// 设置模糊矩阵元素表
	FLOAT *matrixItems = new FLOAT[255*2+1];
	FLOAT *items = &matrixItems[255];
	FLOAT fv = threshold * 2.5;
	INT i;
	for (i = 1; i < 256; i ++)
	{
		items[-i] = items[i] = 1 - i / fv;
		if (items[i] < 0) break;
	}
	for (; i < 256; i ++)
		items[-i] = items[i] = 0;
	*items = 1;

	// 获取边框像素扩展图像数据到src
	BitmapData src;
	GetExpendData(data, radius, &src);
	// 获取数据处理参数
	PARGBQuad pd, ps;
	UINT width, height;
	INT dstOffset, srcOffset;
	GetDataCopyParams(data, &src, width, height, pd, ps, dstOffset, srcOffset);

	INT size = (radius << 1) + 1;
	INT pOffset = ((src.Stride >> 2) + 1) * radius;
	INT iOffset = (src.Stride >> 2) - size;
	FLOAT pixelA, pixelR, pixelG, pixelB;
	FLOAT nuclearA, nuclearR, nuclearG, nuclearB;
	FLOAT ivA, ivR, ivG, ivB;
	for (UINT y = 0; y < height; y ++, ps += srcOffset, pd += dstOffset)
	{
		for (UINT x = 0; x < width; x ++, ps ++, pd ++)
		{
			pixelA = pixelR = pixelG = pixelB = 0;
			nuclearA = nuclearR = nuclearG = nuclearB = 0;
			PARGBQuad p = ps, p0 = p + pOffset;
			for (INT i = 0;  i < size; i ++, p += iOffset)
			{
				for (INT j = 0; j < size; j ++, p ++)
				{
					// 以Pij - p0为下标从模糊矩阵元素表中获取像素各分量的元素值
					ivB = items[p->Blue - p0->Blue];
					ivG = items[p->Green - p0->Green];
					ivR = items[p->Red - p0->Red];
					ivA = items[p->Alpha - p0->Alpha];
					// 累计像素各分量元素值
					nuclearB += ivB;
					nuclearG += ivG;
					nuclearR += ivR;
					nuclearA += ivA;
					// 累计像素各分量元素值与分量值的乘积
					pixelB += (ivB * p->Blue);
					pixelG += (ivG * p->Green);
					pixelR += (ivR * p->Red);
					pixelA += (ivA * p->Alpha);
				}
			}
			// 计算像素表面模糊后的新分量值
			if (nuclearB > 0)
				pd->Blue = (BYTE)(pixelB / nuclearB + 0.5);
			if (nuclearG > 0)
				pd->Green = (BYTE)(pixelG / nuclearG + 0.5);
			if (nuclearR > 0)
				pd->Red = (BYTE)(pixelR / nuclearR + 0.5);
			if (nuclearA > 0)
				pd->Alpha = (BYTE)(pixelA / nuclearA + 0.5);
		}
	}

	FreeBitmapData(&src);
	delete[] matrixItems;
	// 如果图像数据含Alpha,将PARGB像素格式还原为ARGB像素格式
	if (HasAlphaFlag(data))
		ArgbConvertPArgb(data);
}
//---------------------------------------------------------------------------

    表面模糊处理函数SurfaceBlur采用浮点数进行矩阵元素的计算和像素的模糊处理,为减少循环过程中的模糊矩阵元素的计算量,函数预先定义并计算了255*2+1大小的模糊矩阵元素表matrixItems,元素指针items指向matrixItems[255]元素位置,该位置为pij - p0 = 0时的元素值,如果pij - p0 > 0,其对应元素值在items右边,反之,如果pij - p0 < 0则对应元素值在items左边,如此便免去了|pij - p0|中的绝对值处理。

    为了加快运行速度,对于不含Alpha信息的图像可不处理alpha分量。也可将处理函数改为定点数运算,但计算精度要差一些。

    下面是一个利用SurfaceBlur函数对GDI+位图进行表面模糊的例子(使用BCB2010):

//---------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)
{
	Gdiplus::Bitmap *bmp =  new Gdiplus::Bitmap(L"..\\..\\media\\Source1.jpg");
	Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle);
	g->DrawImage(bmp, 0, 0);

	BitmapData data;
	LockBitmap(bmp, &data);
	SurfaceBlur(&data, 3, 10);
	UnlockBitmap(bmp, &data);
	g->DrawImage(bmp, data.Width + 8, 0);

	delete g;
	delete bmp;
}
//---------------------------------------------------------------------------

    例子运行效果截图:

    因水平有限,错误在所难免,欢迎指正和指导。邮箱地址:maozefa@hotmail.com

    这里可访问《C++图像处理 -- 文章索引

2011-07-14 13:09:40 maozefa 阅读数 5670
  • 数据结构与算法(C/C++实现)视频教程

    C/C++实现数据结构与算法视频培训课程全面介绍计算机行业,基本的数据结构与算法,既有理论的深度也有实战的技法。课程全程讲师手敲代码,一步步代你走进数据结构与算法。 本课程涉及的数据结构与算法有,栈,队列,单向链表,双向循环链表,树,二叉树,搜索二叉树,平衡搜索二叉树,冒泡,选择,直插,希尔,,归并等,课程还涉及深度优先算法与广度优先算法等等。

    3629 人正在学习 去看看 王桂林

阅读提示

    《C++图像处理》系列以代码清晰,可读性为主,全部使用C++代码。

    《Delphi图像处理》系列以效率为侧重点,一般代码为PASCAL,核心代码采用BASM。

    尽可能保持二者内容一致,可相互对照。

    本文代码必须包括《C++图像处理 -- 数据类型及公用函数文章中的BmpData.h头文件以及《C++图像处理 -- 平面几何变换类》TransformMatrix.h文件。

 

    在《C++图像处理 -- 平面几何变换类》一文中,介绍了图像平面几何变换类TransformMatrix,并写了一个简单的临近插值法图像几何变换函数Transform,用于测试。很显然,Transform函数产生的变换图像不仅质量较差,而且也不具备通用性,只能作为一个实现图像几何变换的框架。

    本文拟采用临近插值法、双线性插值法和双立方插值法等三种插值方式,来实现较完整、通用的图形图像平面几何变换。下面是除TransformMatrix类外的全部代码(TransformMatrix类代码在《C++图像处理 -- 平面几何变换类》中)。

//---------------------------------------------------------------------------

#define GetPixel4096(data, x, y)	\
	(PARGBQuad)((LPBYTE)data->Scan0 + (y >> 12) * data->Stride + ((x >> 12) << 2))
//---------------------------------------------------------------------------

// 获取临近插值颜色
FORCEINLINE
ARGBQuad GetNearColor(CONST BitmapData *data, UINT x, UINT y)
{
	return *GetPixel4096(data, x, y);
}
//---------------------------------------------------------------------------

// 获取线性插值颜色
FORCEINLINE
ARGBQuad GetBilinearColor(CONST BitmapData *data, UINT x, UINT y)
{
	UINT u = (x & 0xfff) >> 4;	// u = (x % 0x1000) / 16
	UINT v = (y & 0xfff) >> 4;	// v = (y % 0x1000) / 16
	UINT u0 = u ^ 255;			// u0 = 255 - u
	UINT v0 = v ^ 255;			// v0 = 255 - v
	UINT m0 = v0 * u0;
	UINT m1 = v * u0;
	UINT m2 = v0 * u;
	UINT m3 = v * u;
	PARGBQuad p0 = GetPixel4096(data, x, y);
	PARGBQuad p1 = (PARGBQuad)((LPBYTE)p0 + data->Stride);
	PARGBQuad p2 = p0 + 1;
	PARGBQuad p3 = p1 + 1;
	ARGBQuad color;
	// 如果不要求很高精度,/ (255 * 255)可改为 >> 16,能提高速度
	color.Blue = (p0->Blue * m0 + p1->Blue * m1 + p2->Blue * m2 + p3->Blue * m3) / (255 * 255);
	color.Green = (p0->Green * m0 + p1->Green * m1 + p2->Green * m2 + p3->Green * m3) / (255 * 255);
	color.Red = (p0->Red * m0 + p1->Red * m1 + p2->Red * m2 + p3->Red * m3) / (255 * 255);
	color.Alpha = (p0->Alpha * m0 + p1->Alpha * m1 + p2->Alpha * m2 + p3->Alpha * m3) / (255 * 255);
	return color;
}
//---------------------------------------------------------------------------

static INT uvTable[513];

// 获取双立方插值颜色
FORCEINLINE
ARGBQuad GetBicubicColor(CONST BitmapData *data, UINT x, UINT y)
{
	INT us[4], vs[4];
	UINT u = (x & 0xfff) >> 4;	// u = (x % 0x1000) / 16
	UINT v = (y & 0xfff) >> 4;	// v = (y % 0x1000) / 16
	us[0] = uvTable[256 + u];
	us[1] = uvTable[u];
	us[2] = uvTable[256 - u];
	us[3] = uvTable[512 - u];
	vs[0] = uvTable[256 + v];
	vs[1] = uvTable[v];
	vs[2] = uvTable[256 - v];
	vs[3] = uvTable[512 - v];

	PARGBQuad p = GetPixel4096(data, x, y);
	INT pixOffset = data->Stride >> 2;
	INT sA, sR, sG, sB;
	sA = sR = sG = sB = 0;

	for (INT i = 0; i < 4; i ++, p += pixOffset)
	{
		sB += ((us[0] * p[0].Blue + us[1] * p[1].Blue +
				us[2] * p[2].Blue + us[3] * p[3].Blue) * vs[i]);
		sG += ((us[0] * p[0].Green + us[1] * p[1].Green +
				us[2] * p[2].Green + us[3] * p[3].Green) * vs[i]);
		sR += ((us[0] * p[0].Red + us[1] * p[1].Red +
				us[2] * p[2].Red + us[3] * p[3].Red) * vs[i]);
		sA += ((us[0] * p[0].Alpha + us[1] * p[1].Alpha +
				us[2] * p[2].Alpha + us[3] * p[3].Alpha) * vs[i]);
	}
	sB >>= 16;
	sG >>= 16;
	sR >>= 16;
	sA >>= 16;
	ARGBQuad color;
	sA = sA < 0? 0 : sA > 255? 255 : sA;
	// 因像素格式为PARGB,上限必须为sA(Alpha)而非255
	color.Blue = sB < 0? 0 : sB > sA? sA : sB;
	color.Green = sG < 0? 0 : sG > sA? sA : sG;
	color.Red = sR < 0? 0 : sR > sA? sA : sR;
	color.Alpha = sA;
	return color;
}
//---------------------------------------------------------------------------

VOID InitBicubicUVTable(FLOAT slope = -0.75)
{
	static FLOAT _slope = 0;
	DOUBLE x, x2, x3;
	if (!(slope < 0)) slope = -0.75;
	if (_slope != slope)
	{
		_slope = slope;
		for (INT i = 0; i <= 512; i ++)
		{
			x = i * (1.0 / 256);
			x2 = x * x;
			x3 = x * x2;
			if (x > 2)
				uvTable[i] = 0;
			else if (x > 1)
				uvTable[i] = (INT)(slope * (x3 - 5 * x2 + 8 * x - 4) * 256);
			else
				uvTable[i] = (INT)(((slope + 2) * x3 - (slope + 3) * x2 + 1) * 256);
		}
	}
}
//---------------------------------------------------------------------------

// 目标像素pd和颜色cs合成函数。
FORCEINLINE
VOID MixerColor(PARGBQuad pd, ARGBQuad cs)
{
	if (cs.Alpha == 255)		// 如果源像素不透明度为255,直接拷贝
		pd->Color = cs.Color;
	else if (cs.Alpha != 0)		// 否则,如果源像素不透明度大于0
	{
		if (pd->Alpha == 255)	// 如果目标像素不透明度为255,ARGB合成
		{
			pd->Blue += (cs.Blue - (pd->Blue * cs.Alpha + 127) / 255);
			pd->Green += (cs.Green - (pd->Green * cs.Alpha + 127) / 255);
			pd->Red += (cs.Red - (pd->Red * cs.Alpha + 127) / 255);
		}
		else  					// 否则,PARGB合成
		{
			// pd转换为PARGB,cs已经是PARGB格式
			pd->Blue = (pd->Blue * pd->Alpha + 127) / 255;
			pd->Green = (pd->Green * pd->Alpha + 127) / 255;
			pd->Red = (pd->Red * pd->Alpha + 127) / 255;
			// pd与cs合成
			pd->Blue += (cs.Blue - (pd->Blue * cs.Alpha + 127) / 255);
			pd->Green += (cs.Green - (pd->Green * cs.Alpha + 127) / 255);
			pd->Red += (cs.Red - (pd->Red * cs.Alpha + 127) / 255);
			pd->Alpha += (cs.Alpha - (pd->Alpha * cs.Alpha + 127) / 255);
			// 重新转换为ARGB
			pd->Blue = pd->Blue * 255 / pd->Alpha;
			pd->Green = pd->Green * 255 / pd->Alpha;
			pd->Red = pd->Red * 255 / pd->Alpha;
		}
	}
}
//---------------------------------------------------------------------------

typedef ARGBQuad (*InterpolateProc)(CONST BitmapData*, UINT, UINT);

// 获取插值过程和扩展半径
INT GetInterpolateProc(InterpolateMode mode, InterpolateProc &proc)
{
	INT radius[] = {2, 1, 2, 4};
	InterpolateProc procs[] = {GetBilinearColor, GetNearColor,
							   GetBilinearColor, GetBicubicColor};
	proc = procs[mode];
	return radius[mode];
}
//---------------------------------------------------------------------------

VOID CopyInterpolateData(BitmapData *dest, CONST BitmapData *source, INT alpha)
{
//	SetAlphaFlag(dest, HasAlphaFlag(source));
	PARGBQuad pd, ps;
	UINT width, height;
	INT dstOffset, srcOffset;
	GetDataCopyParams(dest, source, width, height, pd, ps, dstOffset, srcOffset);
	UINT x, y;
	// 如果alpha < 255或者源数据含Alpha,转换为PARGB像素格式
	if (alpha < 255 || HasAlphaFlag(source))
	{
		for (y = 0; y < height; y ++, pd += dstOffset, ps += srcOffset)
		{
			for (x = 0; x < width; x ++, pd ++, ps ++)
			{
				pd->Alpha = (alpha * ps->Alpha + 127) / 255;
				pd->Blue = (ps->Blue * pd->Alpha + 127) / 255;
				pd->Green = (ps->Green * pd->Alpha + 127) / 255;
				pd->Red = (ps->Red * pd->Alpha + 127) / 255;
			}
		}
	}
	// 否则, 直接像素拷贝
	else
	{
		for (y = 0; y < height; y ++, pd += dstOffset, ps += srcOffset)
		{
			for (x = 0; x < width; *pd ++ = *ps ++, x ++);
		}
	}
}
//---------------------------------------------------------------------------

VOID FillBorder(BitmapData *data, UINT radius, BOOL fillX, BOOL fillY)
{
	UINT height = data->Height - (radius << 1);
	UINT x, y;
	PARGBQuad pd, ps;
	if (fillX)
	{
		UINT width = data->Width - (radius << 1);
		pd = (PARGBQuad)data->Scan0 + radius * data->Width;
		for (y = 0; y < height; y ++)
		{
			for (x = 0, ps = pd + radius; x < radius; *pd ++ = *ps, x ++);
			for (x = 0, pd += width, ps = pd - 1; x < radius; *pd ++ = *ps, x ++);
        }
	}
	if (fillY)
	{
		pd = (PARGBQuad)data->Scan0;
		ps = pd + radius * data->Width;
		PARGBQuad pd2 = ps + height * data->Width;
		PARGBQuad ps2 = pd2 - data->Width;
		for (y = 0; y < radius; y ++)
		{
			for (x = 0; x < data->Width; *pd ++ = ps[x], *pd2 ++ = ps2[x], x ++);
		}
    }
}
//---------------------------------------------------------------------------

BOOL CanTransform(INT width, INT height, RECT &r)
{
	r.right += r.left;
	r.bottom += r.top;
	if (r.right > width) r.right = width;
	if (r.bottom > height) r.bottom = height;
	if (r.left > 0) r.right -= r.left; else r.left = 0;
	if (r.top > 0) r.bottom -= r.top; else r.top = 0;
	return r.right > 0 && r.bottom > 0;
}

BOOL GetTransformParams(INT dstWidth, INT dstHeight,
	INT srcWidth, INT srcHeight, TransformMatrix &matrix, RECT &dst, RECT &src)
{
	FLOAT fx, fy, fwidth, fheight;
	matrix.GetTransformSize(srcWidth, srcHeight, fx, fy, fwidth, fheight);
	matrix.Invert();
	dst.left = (LONG)fx;
	dst.top = (LONG)fy;
	dst.right = (LONG)(fwidth + fx + 0.999999f);
	dst.bottom = (LONG)(fheight + fy + 0.999999f);
	if (!CanTransform(dstWidth, dstHeight, dst))
		return FALSE;
	if (fx > 0 || fy > 0)
	{
		if (fx < 0) fx = 0;
		else if (fy < 0) fy = 0;
		matrix.Translate(fx, fy);
	}
	matrix.GetTransformSize(dst.right, dst.bottom, fx, fy, fwidth, fheight);
	src.left = (LONG)fx;
	src.top = (LONG)fy;
	src.right = (LONG)(fwidth + fx + 0.999999f);
	src.bottom = (LONG)(fheight + fy + 0.999999f);
	if (!CanTransform(srcWidth, srcHeight, src))
		return FALSE;
	if (fx > 0) matrix.GetElements().dx -= fx;
	if (fy > 0) matrix.GetElements().dy -= fy;
	return TRUE;
}
//---------------------------------------------------------------------------

// 执行图像数据几何变换
VOID ImageTransform(BitmapData *dest, INT x, INT y,
	CONST BitmapData *source, TransformMatrix *matrix, FLOAT alpha = 1.0f)
{
	INT alphaI = (INT)(alpha * 255);
	if (alphaI <= 0) return;
	if (alphaI > 255) alphaI = 255;

	// 复制几何变换矩阵对象
	TransformMatrix m(matrix);

	// 几何变换矩阵绝对增加平移量x, y
	m.GetElements().dx += x;
	m.GetElements().dy += y;

	// 逆转几何变换矩阵,计算并分别返回目标和源图像实际大小dstR和srcR
	RECT dstR, srcR;
	if (GetTransformParams(dest->Width, dest->Height,
		source->Width, source->Height, m, dstR, srcR) == FALSE)
		return;

	// 将浮点数扩大4096倍,采用定点数运算
	INT im11 = (INT)(m.GetElements().m11 * 4096.0f);
	INT im12 = (INT)(m.GetElements().m12 * 4096.0f);
	INT im21 = (INT)(m.GetElements().m21 * 4096.0f);
	INT im22 = (INT)(m.GetElements().m22 * 4096.0f);

	// 根据mode获取插值过程及边框扩展半径
	InterpolateMode mode = GetInterpolateMode(source);
	InterpolateProc ColorProc;
	INT radius = GetInterpolateProc(mode, ColorProc);

	BitmapData dst, src, exp, tmp;
	// 按dstR和srcR分别获取目标和源图像子图到dst和src
	GetBitmapData(dest, dstR.left, dstR.top, dstR.right, dstR.bottom, &dst);
	GetBitmapData(source, srcR.left, srcR.top, srcR.right, srcR.bottom, &src);
	// 建立扩展半径为radius新的图像数据对象exp
	GetBitmapData(src.Width + radius * 2, src.Height + radius * 2, &exp);
	// src图像数据拷贝到exp
	GetBitmapData(&exp, radius, radius, src.Width, src.Height, &tmp);
	CopyInterpolateData(&tmp, &src, alphaI);
	// 填充exp边框像素。如果im21或im12的12位尾数不为0,说明x或y向为斜边,不填充
	BOOL fillX = (im21 & 0xfff) == 0;
	BOOL fillY = (im12 & 0xfff) == 0;
	FillBorder(&exp, radius, fillX, fillY);
	if (fillX && fillY && alphaI == 255 && !HasAlphaFlag(source) &&
		dest->Width == dst.Width && dest->Height == dst.Height)
		SetAlphaFlag(dest, FALSE);
	// 确定源图像边界界限
	INT up = radius * 0x800;
	INT xDown = (exp.Width - radius) * 0x1000;
	INT yDown = (exp.Height - radius) * 0x1000;
	// 几何变换逆矩阵的平移量为与子图原点对应的源图起始坐标点
	INT xs = (INT)(m.GetElements().dx * 4096.0f) + up + 0x800;
	INT ys = (INT)(m.GetElements().dy * 4096.0f) + up + 0x800;
	INT width = (INT)dst.Width;
	INT height = (INT)dst.Height;

	PARGBQuad pd = (PARGBQuad)dst.Scan0;
	INT dstOffset = (dst.Stride >> 2) - dst.Width;
	// 如果插值方式为双立方卷积,初始化UV表
	if (mode == InterpolateModeBicubic)
		InitBicubicUVTable(-0.75);

	// 按目标子图逐点复制源子图几何变换后的数据
	for (y = 0; y < height; y ++, ys += im22, xs += im21, pd += dstOffset)
	{
		INT y0 = ys;
		INT x0 = xs;
		for (x = 0; x < width; x ++, x0 += im11, y0 += im12, pd ++)
		{
			if (y0 >= up && y0 < yDown && x0 >= up && x0 < xDown)
			{
				MixerColor(pd, ColorProc(&exp, x0, y0));
            }
		}
	}
	FreeBitmapData(&exp);
}
//---------------------------------------------------------------------------

    同《C++图像处理 -- 平面几何变换类》的临近插值图形图像平面几何变换函数相比,本文代码有以下特点:

    1、实现了邻近插值、双线性插值和双立方插值三种插值方式,具有很强的通用性和实用性。

    2、插值过程采用了定点数运算,比浮点数运算速度快。

    3、较好的实现了边界处理。边界处理是图形图像平面几何变换的一个难点,处理不好会出现难看的锯齿或者半透明的图像边缘。本文代码采用了扩展图像边框像素的方法,较好的解决了这个问题。当边界发生倾斜(变形)时,超出图像边界的像素值设置为0,通过插值后的半透明像素点能较好地解决边界锯齿;而边界不变形时,超出图像边界的像素值用临近的边界像素值替代,这样就不会出现一些不该出现的半透明像素,避免难看的半透明的图像边缘。下面是用本文代码和GDI+几何变换函数分别作1.2倍缩放处理加0.3剪切的双线性插值处理效果图:

    4、可处理含Alpha信息的图像,同时增加了图像数据不透明度的处理。对含Alpha信息的图像数据或者不透明度小于1的几何变换,对图像源作了自乘预处理,减少了原像素与变换后的像素值得差异,保证了较好的视觉效果。下面的PNG图片几何变换处理效果图中,左边是经过自乘预处理后的,而右边是未处理的:

    5、限于文章篇幅,本文代码以通用性和清晰度为主,没作过多的优化处理,有兴趣的朋友可根据自己需要进行改进。例如,可将缩放处理过程独立,以提高缩放处理处理速度。

    下面是用BCB2010和GDI+运用本文图形图像平面几何变换代码处理Alpha像素格式图像的例子代码:

void __fastcall TForm1::Button1Click(TObject *Sender)
{
	// 获取源图像扫描线数据
	Gdiplus::Bitmap *bmp =  new Gdiplus::Bitmap(L"..\\..\\media\\IMG_9440_mf.jpg");
	BitmapData source, dest;
	LockBitmap(bmp, &source);

	// 设置几何变换
	TransformMatrix matrix;
	matrix.Scale(1.2, 1.2);
	matrix.Shear(0.3, 0.3);

	// 建立目标位图并获取其扫描线数据
	RECT r;
	matrix.GetTransformRect(source.Width, source.Height, r);
	Gdiplus::Bitmap *newBmp = new Gdiplus::Bitmap(
		r.right - r.left, r.bottom - r.top, PixelFormat32bppARGB);
	LockBitmap(newBmp, &dest);

	// 设置双立方插值方式
	SetInterpolateMode(&source, InterpolateModeBicubic);
	// 执行图像几何变换。
	// 注意这里使用-r.left, -r.top为坐标,使得变换后的图像完全可见
	ImageTransform(&dest, -r.left, -r.top, &source, &matrix, 1);

	// 释放图像扫描线数据(位图解锁)
	UnlockBitmap(newBmp, &dest);
	UnlockBitmap(bmp, &source);

	// 画几何变换后的图像
	Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle);
	g->DrawImage(newBmp, 0, 0);

	delete g;
	delete newBmp;
	delete bmp;

}
//---------------------------------------------------------------------------

    因水平有限,错误在所难免,欢迎指正和指导。邮箱地址:maozefa@hotmail.com

    这里可访问《C++图像处理 -- 文章索引

C++实现FFT代码

阅读数 10870

没有更多推荐了,返回首页