?? dlgreg.cpp
字號:
// 循環變量
int i,j;
// 如果特征選取標志位為TRUE,則進行特征點的選取和配準,否則退出
if(!m_bChoseFeature){
return;
}
// 待配準圖象的特征選取區域,在這里選擇特征點的選擇區域要比圖象的區
// 域小一圈
CRect rectChoose;
rectChoose.bottom = m_rectResltImage.bottom - 5;
rectChoose.top = m_rectResltImage.top + 5;
rectChoose.left = m_rectResltImage.left + 5;
rectChoose.right = m_rectResltImage.right - 5;
// 特征點的區域
CRect rectFeature;
// 標志位,表示此點是否是已經選擇的特征點
BOOL bFlag = FALSE;
// 判斷此點是否合法,并判斷此點是否已經選擇,如果是,則去掉此點
if(rectChoose.PtInRect(point))
{
// 如果所選擇的特征點是以前的特征點,則去掉此點
for( i = 0; i<m_nChsFeatureNum; i++){
// 選擇特征點的顯示區域,以便對特征點進行取舍
rectFeature.bottom = m_pPointSampl[i].y + m_rectResltImage.top + 5;
rectFeature.top = m_pPointSampl[i].y + m_rectResltImage.top - 5;
rectFeature.left = m_pPointSampl[i].x + m_rectResltImage.left- 5;
rectFeature.right = m_pPointSampl[i].x + m_rectResltImage.left+ 5;
// 判斷所選擇的特征點是否為原來選擇的特征點
// 如果是,則去掉此特征點
if(rectFeature.PtInRect(point)){
// 將后面的特征點向前移動一位,去掉所選擇的此特征點
for(j=i; j<m_nChsFeatureNum-1; j++){
m_pPointSampl[j] = m_pPointSampl[j+1];
m_pPointBase[j] = m_pPointBase[j+1];
}
// 將特征點的計數減一
m_nChsFeatureNum--;
// 更新顯示
Invalidate();
// 設置標志位
bFlag = TRUE;
// 退出
return;
}
}
// 在判斷特征點是否已經選取夠了
if(m_nChsFeatureNum == 3){
AfxMessageBox("你已經選取了3個特征點,如果要繼續選取,你可以去掉配準不正確的特征點在進行選取");
return;
}
// 如果此點是需要選取的,則進行相關操作
if(!bFlag){
// 將此待配準特征點選取,注意特征點的坐標是以圖象的左上角為原點確定的
m_pPointSampl[m_nChsFeatureNum].x = point.x - m_rectResltImage.left;
m_pPointSampl[m_nChsFeatureNum].y = point.y - m_rectResltImage.top;
// 配準此特征點
m_pPointBase[m_nChsFeatureNum] = FindMatchPoint(m_pDibInit, m_pDibSamp,
m_pPointSampl[m_nChsFeatureNum]);
// 將特征點計數加一
m_nChsFeatureNum++;
}
}
// 更新顯示
Invalidate();
CDialog::OnLButtonUp(nFlags, point);
}
/*************************************************************************
*
* \函數名稱:
* OnLButtonMove()
*
* \輸入參數:
* 無
*
* \返回值:
* 無
*
* \說明:
* 如果特征點選取標志位為TRUE,則該函數將待配準區域的鼠標設置為十字形狀,
*以便能更精確的定位特征點。
*
*************************************************************************
*/
void CDlgReg::OnMouseMove(UINT nFlags, CPoint point)
{
// 如果不是在特征點選取狀態,則不進行相關的操作
if(m_bChoseFeature){
// 如果鼠標在待配準圖象區域中,則更改鼠標形狀
if(m_rectResltImage.PtInRect(point))
{
::SetCursor(LoadCursor(NULL,IDC_CROSS));
}
else
{
::SetCursor(LoadCursor(NULL,IDC_ARROW));
}
}
CDialog::OnMouseMove(nFlags, point);
}
/*************************************************************************
*
* \函數名稱:
* FindMatchPoint()
*
* \輸入參數:
* CDib* pDibBase - 基準圖象指針
* CDib* pDibSamp - 待配準圖象指針
* CPoint pointSamp - 待配準特征點的位置
*
* \返回值:
* CPoint - 返回在基準圖象中配準的特征點的位置
*
* \說明:
* 該函數根據待待配準圖象中的特征點位置在基準圖象中尋找配準特征點,并將
*配準的特征點位置返回。在配準的過程中,采取的是塊配準的方法進行配準,在這里
*選取塊的大小為7*7。搜索的方法為全局搜索。
*
*************************************************************************
*/
CPoint CDlgReg::FindMatchPoint(CDib* pDibBase, CDib* pDibSamp,
CPoint pointSamp)
{
// 循環變量
int i,j,m,n;
// 臨時變量
int nX, nY;
// 配準特征點位置
CPoint pointBase;
// 配準數據塊的尺寸
int nBlockLen = 7;
int nBlockHalfLen =3;
// 基準圖象數據指針
unsigned char* pBase;
pBase = (unsigned char *)pDibBase->m_lpImage;
// 待配準圖象數據指針
unsigned char* pSamp;
pSamp = (unsigned char *)pDibSamp->m_lpImage;
// 特征點位置的數據配準塊
unsigned char* pUnchSampBlock;
pUnchSampBlock = new unsigned char[nBlockLen*nBlockLen];
// 臨時分配內存,用于存放配準數據塊
unsigned char* pUnchBaseBlock;
pUnchBaseBlock = new unsigned char[nBlockLen*nBlockLen];
// 相似度
double dbCor;
// 最大相似度
double dbMaxCor = 0;
// 基準圖象的存儲大小
CSize sizeBaseImg;
sizeBaseImg = pDibBase->GetDibSaveDim();
// 待配準圖象的存儲大小
CSize sizeSampImg;
sizeSampImg = pDibSamp->GetDibSaveDim();
// 從待配準圖象中提取以特征點為中心的nBlockLen*nBlockLen0的數據塊
for(i=-nBlockHalfLen; i<=nBlockHalfLen; i++){
for(j=-nBlockHalfLen; j<=nBlockHalfLen; j++){
// 計算此點在圖象中的位置
nX = pointSamp.x + i;
nY = sizeSampImg.cy - pointSamp.y + j +1;
// 提取圖象數據
pUnchSampBlock[(j+nBlockHalfLen)*nBlockLen + (i+nBlockHalfLen)] =
pSamp[nY*sizeSampImg.cx + nX];
}
}
// 基準圖象的高度和寬度
int nBaseImgHeight, nBaseImgWidth;
nBaseImgHeight = pDibBase->m_lpBMIH->biHeight;
nBaseImgWidth = pDibBase->m_lpBMIH->biWidth;
// 在基準圖象中尋找配準特征點,采取的搜索方法為全局搜索
for(m = nBlockHalfLen; m< nBaseImgHeight-nBlockHalfLen; m++){
for(n=nBlockHalfLen; n<nBaseImgWidth-nBlockHalfLen; n++){
// 提取以此點為中心,大小為nBlockLen*nBlockLen的數據塊
for(i=-nBlockHalfLen; i<=nBlockHalfLen; i++){
for(j=-nBlockHalfLen; j<=nBlockHalfLen; j++){
// 計算此點在圖象中存儲的位置
nX = n + i;
nY = sizeBaseImg.cy - m + j - 1;
// 提取圖象數據
pUnchBaseBlock[(j+nBlockHalfLen)*nBlockLen + (i+nBlockHalfLen)] =
pBase[nY*sizeBaseImg.cx + nX];
}
}
// 對這兩個數據塊進行配準,計算相似度
dbCor = CalCorrelation(pUnchBaseBlock, pUnchSampBlock, nBlockLen);
// 判斷是否為最大相似度,如果是,則記錄此相似度和配準特征點位置
if(dbCor > dbMaxCor){
dbMaxCor = dbCor;
pointBase.x = n;
pointBase.y = m;
}
}
}
return pointBase;
}
/*************************************************************************
*
* \函數名稱:
* CalCorrelation()
*
* \輸入參數:
* unsigned char* pBase - 基準圖象數據指針
* unsigned char* pSamp - 待配準圖象數據指針
* int nBlockLen - 配準數據塊的尺度大小
*
* \返回值:
* double - 返回兩個數據塊配準的相似度
*
* \說明:
* 該函數對給定的兩個大小為nBlockLen*nBlockLen的數據塊,計算兩者之間的
*的配準相似度。其中,去掉均值以消除亮度變換的影響。
*
*************************************************************************
*/
double CDlgReg::CalCorrelation(unsigned char* pBase, unsigned char* pSamp, int nBlockLen)
{
// 臨時變量
double dbSelfBase=0,dbSelfSamp=0;
// 相似度
double dbCor=0;
// 塊均值
double dbMeanBase=0,dbMeanSamp=0;
// 計算兩個塊的平均值
for(int i=0;i<nBlockLen;i++)
for(int j=0;j<nBlockLen;j++)
{
dbMeanBase += pBase[j*nBlockLen + i];
dbMeanSamp += pSamp[j*nBlockLen + i];
}
dbMeanBase = dbMeanBase/(nBlockLen*nBlockLen);
dbMeanSamp = dbMeanSamp/(nBlockLen*nBlockLen);
// 求取相似度
for(i=0;i<nBlockLen;i++)
for(int j=0;j<nBlockLen;j++)
{
dbSelfBase += (pBase[j*nBlockLen + i] - dbMeanBase)*
(pBase[j*nBlockLen + i] - dbMeanBase);
dbSelfSamp += (pSamp[j*nBlockLen + i] - dbMeanSamp)*
(pSamp[j*nBlockLen + i] - dbMeanSamp);
dbCor += (pBase[j*nBlockLen + i] - dbMeanBase)*
(pSamp[j*nBlockLen + i] - dbMeanSamp);
}
dbCor = dbCor / sqrt(dbSelfBase * dbSelfSamp);
// 返回相似度
return dbCor;
}
/*************************************************************************
*
* \函數名稱:
* OnRegReg()
*
* \輸入參數:
* 無
*
* \返回值:
* 無
*
* \說明:
* 該函數對兩幅圖象m_pDibInit和m_pDibSamp進行配準。其中,需要先確定特征點,
*特征點至少需要選取3個以上,這一步驟需要在“選取特征點”步驟中進行。根據選取
*的特征點,計算得到仿射參數。最后,將配準的兩幅圖象拼接起來顯示。
*
*************************************************************************
*/
void CDlgReg::OnRegReg()
{
// 進行配準之前,判斷是否已經選取特征點
if(!m_bChoseFeature){
AfxMessageBox("還沒有選取特征點,請先選取特征點");
return;
}
// 如果選取的特征點數目不夠,也不能進行處理
if(m_nChsFeatureNum < 3){
AfxMessageBox("請選擇三個特征點,再進行圖象配準");
return;
}
// 仿射變換系數,仿射變換的系數為6個。此系數為從基準圖象到待配準圖象的變換系數
double *pDbBs2SpAffPara;
pDbBs2SpAffPara = new double[2*3];
// 仿射變換系數,仿射變換的系數為6個。此系數為從待配準圖象到基準圖象的變換系數
double *pDbSp2BsAffPara;
pDbSp2BsAffPara = new double[2*3];
// 如果已經選取了特征點,并得到了配準的特征點,則可以計算仿射變換系數了
GetAffinePara(m_pPointBase, m_pPointSampl, pDbSp2BsAffPara);
// 計算從待配準圖象到基準圖象的仿射變換系數
GetAffinePara(m_pPointSampl, m_pPointBase, pDbBs2SpAffPara);
// 計算圖象經過仿射變換后的尺寸大小
CRect rectAftAff;
rectAftAff = GetAftAffDim(pDbSp2BsAffPara);
// 根據圖象的尺寸大小創建新的圖象
//CreateDIB();
int nNewImgWidth, nNewImgHeight;
nNewImgWidth = rectAftAff.right - rectAftAff.left;
nNewImgHeight = rectAftAff.bottom- rectAftAff.top;
// 將基準圖象放入新的圖象中
LPBYTE lpBaseImg;
lpBaseImg = SetBaseImgAftAff(rectAftAff);
// 將待配準圖象放入新的圖象中
LPBYTE lpSampImg;
lpSampImg = SetSampImgAftAff(pDbBs2SpAffPara, rectAftAff);
// 將此圖象用CDib類封裝
m_pDibResult = new CDib(CSize(nNewImgWidth,nNewImgHeight), 8);
// 計算結果圖象的存儲大小尺寸
CSize sizeSaveResult;
sizeSaveResult = m_pDibResult->GetDibSaveDim();
// 拷貝調色板
memcpy(m_pDibResult->m_lpvColorTable, m_pDibInit->m_lpvColorTable, m_pDibResult->m_nColorTableEntries*sizeof(RGBQUAD));
// 應用調色板
m_pDibResult->MakePalette();
// 分配內存給合并后的圖象
LPBYTE lpImgResult;
lpImgResult = (LPBYTE)new unsigned char[sizeSaveResult.cx * sizeSaveResult.cy];
// 對圖象進行賦值
for( int i=0; i<nNewImgWidth; i++)
for( int j=0; j<nNewImgHeight; j++){
int nX = i;
int nY = sizeSaveResult.cy - j - 1;
lpImgResult[nY*sizeSaveResult.cx + nX] = (lpBaseImg[j*nNewImgWidth + i] +lpSampImg[j*nNewImgWidth + i])/2;
}
// 將指針賦值給CDib類的數據
m_pDibResult->m_lpImage = lpImgResult;
// 顯示合并后的圖象
CDlgAftReg* pDlg;
pDlg = new CDlgAftReg(NULL, m_pDibResult);
pDlg->DoModal();
// 刪除對象
delete pDlg;
// 釋放已分配內存
delete[]lpBaseImg;
delete[]lpSampImg;
delete[]pDbBs2SpAffPara;
delete[]pDbSp2BsAffPara;
}
/*************************************************************************
*
* \函數名稱:
* GetAffinePara()
*
* \輸入參數:
* double *pDbBs2SpAffPara - 用于存放基準圖象到待配準圖象的仿射變換系數
* double *pDbSp2BsAffPara - 用于存放待配準圖象到基準圖象的仿射變換系數
*
* \返回值:
* 無
*
* \說明:
* 該函數根據得到的三對配準的特征點,計算仿射變換系數。得到的仿射變換系數
*存放在兩個輸入參數所指向的內存中。
*
*************************************************************************
*/
void CDlgReg::GetAffinePara(CPoint* pPointBase, CPoint* pPointSampl, double* pDbAffPara)
{
// pDbBMatrix中存放的是基準圖象中特征點的坐標,
// 大小為2*m_nChsFeatureNum,前m_nChsFeatureNum為X坐標
double *pDbBMatrix;
pDbBMatrix = new double[2*m_nChsFeatureNum];
// pDbSMatrix中存放的是待配準圖象中特征點的擴展坐標
// 大小為3*m_nChsFeatureNum,其中前m_nChsFeatureNum為X坐標
// 中間m_nChsFeatureNum個為Y坐標,后面m_nChsFeatureNum為1
double *pDbSMatrix;
pDbSMatrix = new double[3*m_nChsFeatureNum];
// pDbSMatrixT中存放的pDbSMatrix的轉置矩陣,
// 大小為m_nChsFeatureNum*3
double *pDbSMatrixT;
pDbSMatrixT = new double[m_nChsFeatureNum*3];
// pDbInvMatrix為臨時變量,存放的是pDbSMatrix*pDbSMatrixT的逆
// 大小為3*3
double *pDbInvMatrix;
pDbInvMatrix = new double[3*3];
// 臨時內存
double *pDbTemp;
pDbTemp = new double[2*3];
// 循環變量
int count;
// 給矩陣賦值
for(count = 0; count<m_nChsFeatureNum; count++)
{
pDbBMatrix[0*m_nChsFeatureNum + count] = pPointBase[count].x;
pDbBMatrix[1*m_nChsFeatureNum + count] = pPointBase[count].y;
pDbSMatrix[0*m_nChsFeatureNum + count] = pPointSampl[count].x;
pDbSMatrix[1*m_nChsFeatureNum + count] = pPointSampl[count].y;
pDbSMatrix[2*m_nChsFeatureNum + count] = 1;
pDbSMatrixT[count*m_nChsFeatureNum + 0] = pPointSampl[count].x;
pDbSMatrixT[count*m_nChsFeatureNum + 1] = pPointSampl[count].y;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -