摘要: 1. 如何獲取系統(tǒng)日期
CTime tm = CTime :: GetCurrentTime();
CString strTime = tm.Format(_TEXT(“%Y-%M-%d %H:%M:%S));
MessageBox(strTime);
2.  ...
閱讀全文
Visual C++以其可視化的編程風(fēng)格成為目前Windows程序設(shè)計(jì)與開(kāi)發(fā)的主流開(kāi)發(fā)工具。而對(duì)話(huà)框在Visual C++編程中使用的尤其多。諸如模式對(duì)話(huà)框、無(wú)模式對(duì)話(huà)框、基于對(duì)話(huà)框的應(yīng)用程序等。絕大部分的VC++的書(shū)籍中都花費(fèi)大量的篇幅與筆墨來(lái)講解對(duì)話(huà)框,這充分證明了對(duì)話(huà)框在Windows應(yīng)用程序中的作用。
很多人可能都用過(guò)Bitware軟件,不知大家還記不記得其界面對(duì)話(huà)框就可以伸展自如。按下一個(gè)按鈕,對(duì)話(huà)框就向水平方向或垂直方向擴(kuò)展。再按一下按鈕,對(duì)話(huà)框又回復(fù)到原來(lái)的大小。其實(shí)這并不是一個(gè)很復(fù)雜的問(wèn)題,下面我們就來(lái)講解如何制作伸展自如的對(duì)話(huà)框。
1 打開(kāi)VisualC++工作臺(tái),新建工程設(shè)為aaa。
2 創(chuàng)建基于對(duì)話(huà)框的應(yīng)用程序如下所示:
其余選擇皆為缺省即可。
3 在對(duì)話(huà)框資源中增加控件資源,如下圖所示:
其中,最靠右邊的一排控件和最靠近下面的兩排控件將在對(duì)話(huà)框伸展或收縮時(shí)顯示出來(lái)或被遮蓋。并且為了示例方便,我們有意將他們的值對(duì)應(yīng)起來(lái)。并且我們需要通過(guò)ClassWizard給每個(gè)控件分別關(guān)聯(lián)成員變量,如下所示:
參考DoDataExchange()函數(shù)我們就可以知道每個(gè)控件所關(guān)聯(lián)的變量了,如下所示:
DDX_Text(pDX, IDC_HEIGHT, m_wHeight);
DDX_Text(pDX, IDC_STREAM_ID, m_wStreamID);
DDX_Text(pDX, IDC_WIDTH, m_wWidth);
DDX_Text(pDX, IDC_SEQUENCE_ORDER, m_wSequenceOrder);
DDX_Text(pDX, IDC_MAX_RATE, m_dwMaxRate);
DDX_Text(pDX, IDC_MIN_RATE, m_dwMinRate);
DDX_Text(pDX, IDC_HEIGHT2, m_wHeight2);
DDX_Text(pDX, IDC_MAX_RATE2, m_dwMaxRate2);
DDX_Text(pDX, IDC_MIN_RATE2, m_dwMinRate2);
DDX_Text(pDX, IDC_SEQUENCE_ORDER2, m_wSequenceOrder2);
DDX_Text(pDX, IDC_STREAM_ID2, m_wStreamID2);
DDX_Text(pDX, IDC_WIDTH2, m_wWidth2);
DDX_Check(pDX, IDC_HORIZONTAL, m_bHorizontal);
DDX_Check(pDX, IDC_VERTICAL, m_bVertical);
實(shí)際上,我們也可以不用ClassWizard而直接將上面的一段代碼copy到DoDataExchange()函數(shù)的
//{{AFX_DATA_MAP(CAaaDlg)
......
//}}AFX_DATA_MAP
之間,(注意一定要在“//{{AFX_DATA_MAP(CAaaDlg)”與“//}}AFX_DATA_MAP”之間)。
同時(shí)在aaaDlg.h文件中,在
//{{AFX_DATA(CAaaDlg)
enum { IDD = IDD_AAA_DIALOG };
......
//}}AFX_DATA
之間增加如下變量定義即可:
(注意一定要在“//{{AFX_DATA(CAaaDlg)”與“//}}AFX_DATA”之間)
UINT m_wHeight;
UINT m_wStreamID;
UINT m_wWidth;
UINT m_wSequenceOrder;
DWORD m_dwMaxRate;
DWORD m_dwMinRate;
UINT m_wHeight2;
DWORD m_dwMaxRate2;
DWORD m_dwMinRate2;
UINT m_wSequenceOrder2;
UINT m_wStreamID2;
UINT m_wWidth2;
BOOL m_bHorizontal;
BOOL m_bVertical;
5 在完成上面的步驟后,我們就可以定義幾個(gè)新的變量用來(lái)保存窗口伸展?fàn)顟B(tài)時(shí)的信息以及收縮狀態(tài)時(shí)的信息。如下:
WORD m_wOrigrinWidth; //原始狀態(tài)下的窗口寬度
WORD m_wReducedWidth; //收縮狀態(tài)下的窗口寬度
WORD m_wOrigrinHeight; //原始狀態(tài)下的窗口高度
WORD m_wReducedHeight; //收縮狀態(tài)下的窗口高度
WORD m_screenWidth; //屏幕寬度
WORD m_screenHeight; //屏幕高度
在完成以上所有的步驟后,就可以對(duì)窗口的伸展與收縮進(jìn)行隨心所欲的控制了,首先我們來(lái)侃侃具體的代碼,下面再進(jìn)行具體的解釋。代碼為:
CenterWindow(NULL);
m_screenWidth = GetSystemMetrics(SM_CXSCREEN);
m_screenHeight = GetSystemMetrics(SM_CYSCREEN);
WINDOWPLACEMENT* lpwndpl=new WINDOWPLACEMENT;
GetWindowPlacement(lpwndpl);
m_wOrigrinWidth = lpwndpl->rcNormalPosition.right;
m_wOrigrinWidth -= lpwndpl->rcNormalPosition.left;
m_wOrigrinHeight = lpwndpl->rcNormalPosition.bottom;
m_wOrigrinHeight -= lpwndpl->rcNormalPosition.top;
LPRECT lpRect1,lpRect2;
lpRect1=new RECT;
lpRect2=new RECT;
GetDlgItem(IDC_PROGRESS_BAR)->GetWindowRect(lpRect1);
GetDlgItem(IDC_STREAM_ID)->GetWindowRect(lpRect2);
lpwndpl->rcNormalPosition.right=(lpRect1->right+lpRect2->left)/2;
m_wReducedWidth = lpwndpl->rcNormalPosition.right;
m_wReducedWidth -= lpwndpl->rcNormalPosition.left;
GetDlgItem(IDC_PROGRESS_BAR)->GetWindowRect(lpRect1);
GetDlgItem(IDC_SEQUENCE_ORDER2)->GetWindowRect(lpRect2);
lpwndpl->rcNormalPosition.bottom=(lpRect1->bottom+lpRect2->top)/2;
m_wReducedHeight = lpwndpl->rcNormalPosition.bottom;
m_wReducedHeight -= lpwndpl->rcNormalPosition.top;
delete lpRect1;
delete lpRect2;
if(m_bHorizontal == TRUE)
{
lpwndpl->rcNormalPosition.right = lpwndpl->rcNormalPosition.left;
lpwndpl->rcNormalPosition.right += m_wOrigrinWidth;
lpwndpl->rcNormalPosition.bottom = lpwndpl->rcNormalPosition.top;
lpwndpl->rcNormalPosition.bottom += m_wReducedHeight;
}
else
{
lpwndpl->rcNormalPosition.right = lpwndpl->rcNormalPosition.left;
lpwndpl->rcNormalPosition.right += m_wReducedWidth;
lpwndpl->rcNormalPosition.bottom = lpwndpl->rcNormalPosition.top;
lpwndpl->rcNormalPosition.bottom += m_wReducedHeight;
}
if(m_bVertical == TRUE)
{
lpwndpl->rcNormalPosition.right = lpwndpl->rcNormalPosition.left;
lpwndpl->rcNormalPosition.right += m_wReducedWidth;
lpwndpl->rcNormalPosition.bottom = lpwndpl->rcNormalPosition.top;
lpwndpl->rcNormalPosition.bottom += m_wOrigrinHeight;
}
else
{
lpwndpl->rcNormalPosition.right = lpwndpl->rcNormalPosition.left;
lpwndpl->rcNormalPosition.right += m_wReducedWidth;
lpwndpl->rcNormalPosition.bottom = lpwndpl->rcNormalPosition.top;
lpwndpl->rcNormalPosition.bottom += m_wReducedHeight;
}
SetWindowPlacement(lpwndpl);
上面這段代碼首先將窗口置于屏幕中間,這可以通過(guò)函數(shù)CenterWindow(GetDesktopWindow()) 來(lái)實(shí)現(xiàn),函數(shù) CenterWindow()的用法為:
void CenterWindow( CWnd* pAlternateOwner = NULL );
其中參數(shù)pAlternateOwner指向所想居中的窗口的指針。
然后利用函數(shù)GetSystemMetrics( int nIndex )得到系統(tǒng)當(dāng)前設(shè)置如屏幕分辨率等。
nIndexs= SM_CXSCREEN 時(shí)函數(shù)返回屏幕的寬度;返回值單位為像素點(diǎn)。
nIndexs= SM_CYSCREEN 時(shí)函數(shù)返回屏幕的高度;返回值單位為像素點(diǎn)。
函數(shù)BOOL GetWindowPlacement( WINDOWPLACEMENT* lpwndpl ) 是最重要的。他的參數(shù)為一個(gè)指向結(jié)構(gòu)變量WINDOWPLACEMENT的指針(lpwndpl);其中WINDOWPLACEMENT結(jié)構(gòu)變量數(shù)據(jù)結(jié)構(gòu)具體為:
typedef struct tagWINDOWPLACEMENT { /* wndpl */
UINT length;
UINT flags;
UINT showCmd;
POINT ptMinPosition;
POINT ptMaxPosition;
RECT rcNormalPosition;
} WINDOWPLACEMENT;
他包含了窗口在屏幕上的定位信息。其中成員變量的含義為:
length:指結(jié)構(gòu)變量的長(zhǎng)度,單位字節(jié)。
flags: 標(biāo)志值,控制窗口最小化或窗口還原的方法,可以取如下值:
WPF_SETMINPOSITION:指定窗口最小化時(shí)的x位置和y位置。
WPF_RESTORETOMAXIMIZED:指定窗口以最大化方式還原,盡管可能窗口并不是在最大化時(shí)最小化的。不改變窗口的缺省還原方式。
showCmd:指定窗口的當(dāng)前顯示狀態(tài)。可以取值:
SW_HIDE:隱藏窗口并激活另一窗口。
SW_MINIMIZE:最小化指定窗口并激活系統(tǒng)窗口列表中最頂層窗口。
SW_RESTORE:激活并顯示窗口,如果窗口處于最小化或最大化狀態(tài),則窗口還原到原始大小和位置。
SW_SHOW:以窗口的當(dāng)前大小和位置激活并顯示窗口。
SW_SHOWMAXIMIZED:以最大化方式激活并顯示窗口。
SW_SHOWMINIMIZED:以圖標(biāo)方式激活并顯示窗口。
SW_SHOWMINNOACTIVE:以圖標(biāo)方式窗口。 但不改變窗口的活動(dòng)狀態(tài)。
SW_SHOWNA:以窗口的當(dāng)前狀態(tài)顯示窗口。
SW_SHOWNOACTIVATE:以窗口最近一次的大小和位置顯示窗口。 但不改變窗口的活 動(dòng)狀態(tài)。
SW_SHOWNORMAL:激活并顯示窗口。如果窗口被最大化或最小化,則窗口還原到原始大小和位置。
ptMinPosition:指定窗口最小化時(shí)的左傷角坐標(biāo)。
ptMaxPosition:指定窗口最大化時(shí)的左傷角坐標(biāo)。
rcNormalPosition:指定窗口在還原時(shí)的坐標(biāo)。
通過(guò)靈活使用函數(shù)GetWindowPlacement()就可以得到窗口的配置信息。
看到這,可能有些讀者已經(jīng)想到了GetWindowPlacement()函數(shù)的姐妹函數(shù)SetWindowPlacement(),不用多說(shuō),其用法如下:
BOOL SetWindowPlacement( WINDOWPLACEMENT* lpwndpl );
顯然,通過(guò)函數(shù)SetWindowPlacement(),再加以簡(jiǎn)單的計(jì)算,我們就可以來(lái)設(shè)置窗口的位置、大小以及狀態(tài)等,從而可以自如地控制窗口顯示與否以及窗口的大小、位置等。這里我們就不再多解釋了。
6 利用ClassWizard對(duì)控件IDC_HORIZONTAL和IDC_VERTICAL增加消息映射BB_CLICKED,
并分別在消息映射函數(shù)中增加如下代碼如下:
void CAaaDlg::OnHorizontal()
{
// TODO: Add your control notification handler code here
m_bHorizontal = !m_bHorizontal;
UpdateData(FALSE);
WINDOWPLACEMENT* lpwndpl=new WINDOWPLACEMENT;
GetWindowPlacement(lpwndpl);
if(m_bHorizontal == TRUE)
{
lpwndpl->rcNormalPosition.right = lpwndpl->rcNormalPosition.left;
lpwndpl->rcNormalPosition.right += m_wOrigrinWidth;
/*
lpwndpl->rcNormalPosition.bottom = lpwndpl->rcNormalPosition.top;
lpwndpl->rcNormalPosition.bottom += m_wReducedHeight;
*/
}
else
{
lpwndpl->rcNormalPosition.right = lpwndpl->rcNormalPosition.left;
lpwndpl->rcNormalPosition.right += m_wReducedWidth;
/*
lpwndpl->rcNormalPosition.bottom = lpwndpl->rcNormalPosition.top;
lpwndpl->rcNormalPosition.bottom += m_wReducedHeight;
*/
}
SetWindowPlacement(lpwndpl);
delete lpwndpl;
}
void CAaaDlg::OnVertical()
{
// TODO: Add your control notification handler code here
m_bVertical = !m_bVertical;
UpdateData(FALSE);
WINDOWPLACEMENT* lpwndpl=new WINDOWPLACEMENT;
GetWindowPlacement(lpwndpl);
if(m_bVertical == TRUE)
{
lpwndpl->rcNormalPosition.bottom = lpwndpl->rcNormalPosition.top;
lpwndpl->rcNormalPosition.bottom += m_wOrigrinHeight;
}
else
{
lpwndpl->rcNormalPosition.bottom = lpwndpl->rcNormalPosition.top;
lpwndpl->rcNormalPosition.bottom += m_wReducedHeight;
}
SetWindowPlacement(lpwndpl);
delete lpwndpl;
}
7 最后利用ClassWizard對(duì)控件IDC_BEGIN_SIMULATE增加消息映射BB_CLICKED。在這里我們模擬了一個(gè)100次循環(huán)的隨機(jī)數(shù)顯示程序。具體大媽如下:
void CAaaDlg::OnBeginSimulate()
{
// TODO: Add your control notification handler code here
srand((unsigned)time(NULL));
char temp[10];
SetDlgItemText(IDC_STATIC11,"Now Beginning ...");
for(int i=0;i<m_maxRange;i++)
{
m_pProgressCtrl->SetPos(i);
m_wSequenceOrder = m_wSequenceOrder2 = i;
m_wStreamID = m_wStreamID2 = rand();
m_wHeight = m_wHeight2 = rand();
m_wWidth = m_wWidth2 = rand();
m_dwMaxRate = m_dwMaxRate2 = rand();
m_dwMinRate = m_dwMinRate2 = rand();
switch(i%4)
{
case 0:
sprintf(temp,"歡 迎 使 用");
break;
case 1:
sprintf(temp,"迎 使 用 歡");
break;
case 2:
sprintf(temp,"使 用 歡 迎");
break;
case 3:
sprintf(temp,"用 歡 迎 使");
break;
}
SetDlgItemText(IDC_WELCOME,temp);
UpdateData(FALSE);
UpdateWindow();
Sleep(50);
}
SetDlgItemText(IDC_WELCOME,"歡 迎 使 用");
SetDlgItemText(IDC_STATIC11,"Now Finnished ...");
}
8 完成以上所有的步驟之后,我們就可以編譯程序并運(yùn)行。運(yùn)行結(jié)果如下:
(a) (b)
(a): 程序啟動(dòng)時(shí)對(duì)話(huà)框狀態(tài)
(b): 點(diǎn)擊Horizontal框后對(duì)話(huà)框狀態(tài)。
(c) (d)
(c): 點(diǎn)擊Vertical框后對(duì)話(huà)框狀態(tài)。
(d): 點(diǎn)擊BeginSimulating按鈕后系統(tǒng)模擬運(yùn)行對(duì)話(huà)框狀態(tài)。
在本程序中,我們還用到了一些其它的技巧如修改窗口標(biāo)題,進(jìn)程狀態(tài)條的顯示、動(dòng)態(tài)字符串顯示以及不通過(guò)ClassWizard而直接通過(guò)在.cpp和.h文件中增加代碼的方法來(lái)關(guān)聯(lián)控件與成員變量和消息映射等,這些都是一些很實(shí)用的技巧,讀者可以參考上面的代碼以及源程序細(xì)細(xì)體會(huì),這里我們就不多說(shuō)了。
程序源工程文件見(jiàn)aaa.zip。在VisualC++6.0下編譯通過(guò)。
在編程過(guò)程中,當(dāng)程序出現(xiàn)錯(cuò)誤,卻又不知道錯(cuò)誤的原因時(shí),可以使用
GetLastError函數(shù),它可以幫助你快速找到出錯(cuò)的原因和語(yǔ)句。
可以直接使用GetLastError函數(shù)得到錯(cuò)誤代碼,然后查找MSDN找到代碼對(duì)應(yīng)的錯(cuò)誤原因,也可使用下面函數(shù)直接把錯(cuò)誤原因顯示出來(lái):
void ShowErrMsg()

...{
TCHAR szBuf[80];
LPVOID lpMsgBuf;
DWORD dw = GetLastError();

FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );

MessageBox(NULL, lpMsgBuf, "系統(tǒng)錯(cuò)誤", MB_OK|MB_ICONSTOP);

LocalFree(lpMsgBuf);
}


然后根據(jù)錯(cuò)誤的原因查找是哪條語(yǔ)句執(zhí)行了相關(guān)操作,傳入的參數(shù)是否正確等,就可以修正錯(cuò)誤了。
CreateFile函數(shù)祥解2006-9-18 18:48:00
CreateFile
The CreateFile function creates or opens the following objects and returns a handle that can be used to access
the object:
files
pipes
mailslots
communications resources
disk devices(Windows NT only)
consoles
directories(open only)
CreateFile函數(shù)創(chuàng)建或打開(kāi)下列對(duì)象,并返回一個(gè)可以用來(lái)訪(fǎng)問(wèn)這些對(duì)象的句柄。
文件
pipes
郵槽
通信資源
磁盤(pán)驅(qū)動(dòng)器(僅適用于windowsNT)
控制臺(tái)
文件夾(僅用于打開(kāi))
HANDLE CreateFile(
LPCTSTR lpFileName, //指向文件名的指針
DWORD dwDesiredAccess, //訪(fǎng)問(wèn)模式(寫(xiě)/讀)
DWORD dwShareMode, //共享模式
LPSECURITY_ATTRIBUTES lpSecurityAttributes, //指向安全屬性的指針
DWORD dwCreationDisposition, //如何創(chuàng)建
DWORD dwFlagsAndAttributes, //文件屬性
HANDLE hTemplateFile //用于復(fù)制文件句柄
);
Parametes
參數(shù)列表
lpFileName
Pointer to a null-terminated string that specifies the name of the object(file, pipe, mailslot,
communications resource, disk device, console, or directory) to create or open.
指向一個(gè)空結(jié)尾字符串。該參數(shù)指定了用于創(chuàng)建或打開(kāi)句柄的對(duì)象。
if *lpFileName is a path, there is a default string size limit of MAX_PATH characters, This limit is
related to how the CreateFile function parses paths.
如果lpFileName的對(duì)象是一個(gè)路徑,則有一個(gè)最大字符數(shù)的限制。不能超過(guò)常量(MAX_PATH).這個(gè)限制指示了
CreateFile函數(shù)如何解析路徑.
dwDesiredAccess
Specifies the type of access to the object. An application can obtain read access, write access,
read-write access, or device query access, This parameter can be any combination of the following
values
指定對(duì)象的訪(fǎng)問(wèn)方式,程序可以獲得讀訪(fǎng)問(wèn)權(quán),寫(xiě)訪(fǎng)問(wèn)權(quán),讀寫(xiě)訪(fǎng)問(wèn)權(quán)或者是詢(xún)問(wèn)設(shè)備("device query") 訪(fǎng)問(wèn)權(quán).
這個(gè)參數(shù)可以是下列值的任意組合
Value(值) Meaning(含義)
0 Specifies device query access to the object. An application can query device
attributes without accessing the device.
指定詢(xún)問(wèn)訪(fǎng)問(wèn)權(quán).程序可以在不直接訪(fǎng)問(wèn)設(shè)備的情況下查詢(xún)?cè)O(shè)備的屬性.
GENERIC_READ Specifies read access to the object, Data can be read from the file and the
file pointer can be moved. Combine with GENERIC_WRITE for read-write access.
指定讀訪(fǎng)問(wèn)權(quán).可以從文件中讀取數(shù)據(jù),并且移動(dòng)文件指針.可以和GENERIC_WRITE組合
成為"讀寫(xiě)訪(fǎng)問(wèn)權(quán)".
GENERIC_WRITE specifies write access to the object. Data can be written to the file and the
file pointer can be moved. Combine with GENERIC_READ fro read-write access
指定寫(xiě)訪(fǎng)問(wèn)權(quán).可以從文件中寫(xiě)入數(shù)據(jù),并且移動(dòng)文件指針.可以和GENERIC_READ組合
成為"讀寫(xiě)訪(fǎng)問(wèn)權(quán)".
dwShareMode
Set of bit flags that specifies how the object can be shared, If dwShareMode is 0, the object cannot
be shared. Subsequent open operations on the object will fail, until the handle is closed.
設(shè)置位標(biāo)志指明對(duì)象如休共享.如果參數(shù)是0, 對(duì)象不能夠共享. 后續(xù)的打開(kāi)對(duì)象的操作將會(huì)失敗,直到該對(duì)象的句
柄關(guān)閉.
To share the object, use a combination of one or more of the following values:
使用一個(gè)或多個(gè)下列值的組合來(lái)共享一個(gè)對(duì)象.
Value(值) Meaning(含義)
FILE_SHARE_DELETE WindowsNT: Subsequent open operations on the object will succeed only if
delete access is requested.
WINDOWS NT:后續(xù)的僅僅請(qǐng)求刪除訪(fǎng)問(wèn)權(quán)的打開(kāi)操作將會(huì)成功.
FILE_SHARE_READ Subsequent open operations on the object will successd only if read access
is requested.
后續(xù)的僅僅請(qǐng)求讀訪(fǎng)問(wèn)權(quán)的打開(kāi)操作將會(huì)成功.
FILE_SHARE_WRITE Subsequent open operations on the object will succeed only if write access
is requested.
后續(xù)的僅僅請(qǐng)求寫(xiě)訪(fǎng)問(wèn)權(quán)的打開(kāi)操作將會(huì)成功.
lpSecurityAttributes
pointer to a SECURITY_ATTRIBUTES structure that determines whether the returned handle can be
inherited by child processes, if lpSecurityAttributes is NULL, the handle cannot be inherited.
指向一個(gè) SECURITY_ATTRIBUTES 結(jié)構(gòu)的指針用于確定如何在子進(jìn)程中繼承這個(gè)句柄.如果這個(gè)參數(shù)是NULL,
則該句柄不可繼承.
dwCreationDisposition
Specifies which action to take on files that exist, and which action to take when files do not exist.
For more information about this parameter, see the remarks section. This parameter must be one of the
following values
指定當(dāng)文件存在或者不存在時(shí)如何動(dòng)作。關(guān)于這個(gè)參數(shù)更多的信息,參考批注部分。這個(gè)參數(shù)必須是一個(gè)或多個(gè)
下列值。
VALUE(值) Neaning(含義)
CREATE_NEW Creates a new file. The function fails if the specified file already exists
創(chuàng)建一個(gè)新文件. 如果該文件已經(jīng)存在函數(shù)則會(huì)失敗.
CREATE_ALWAYS Creates a new file. If the file exsts, the function overwrites the file and
clears the existing attributes.
創(chuàng)建一個(gè)新文件.如果該文件已經(jīng)存在,函數(shù)將覆蓋已存在的文件并清除已存在的文件屬性
OPEN_EXISTING Opens the file. The function fails if the file does not exist.
See the Remarks section for a discussion of why you should use the
OPEN_EXISTING flag if you are using the CreateFile function for devices,
including the console.
打開(kāi)一個(gè)文件,如果文件不存在函數(shù)將會(huì)失敗.
如查你使用CreateFile函數(shù)為設(shè)備裝載控制臺(tái).請(qǐng)查看批注中的"為什么使用
OPEN_EXISTING標(biāo)志"的部分.
OPEN_ALWAYS Opens the file, if it exsts. If the file does not exist, the function creates
the file as if dwCreationDisposition were CREATE_NEW.
如果文件存在,打開(kāi)文件. 如果文件不存在,并且參數(shù)中有CREATE_NEW標(biāo)志,則創(chuàng)建文件.
TRUNCATE_EXISTING Opens the file. Once opened, the file is truncated so that its size is zero
bytes The calling process must open the file with at least GENERIC_WRITE access.
The function fails if the file does not exist.
打開(kāi)一個(gè)文件,每次打開(kāi),文件將被截至0字節(jié).調(diào)用進(jìn)程必須用GENERIC_WRITE訪(fǎng)問(wèn)模式打
開(kāi)文件.如果文件不存在則函數(shù)就會(huì)失敗.
dwFlagsAndatributes
Specifies the file attributes and flags for the file.
為文件指定屬性和標(biāo)志位
Any combination of the following attributes is acceptable for the dwFlagsAndAttributes parameter,
except all other file attributes override FILE_ATTRIBUTE_NORMAL.
該參數(shù)可以接收下列屬性的任意組合.除非其它所有的文件屬性忽略FILE_ATTRIBUTE_NORMAL.
Attribute(屬性) Meaning(標(biāo)志)
FILE_ATTRIBUTE_ARCHIVE The ifle should be archived. Application use this attribute to mark
files for backup or removal.
文件將被存檔,程序使用此屬性來(lái)標(biāo)志文件去備份或移除
FILE_ATTRIBUTE_HIDDEN The file is hidden. It is not to be included in an ordinary directory
listing.
文件被隱藏,它不會(huì)在一般文件夾列表中被裝載.
FILE_ATTRIBUTE_NORMAL The file has no other attributes set. This attribute is valid only if
used alone
文件沒(méi)有被設(shè)置任何屬性.
FILE_ATTRIBUTE_OFFLINE The data of the file is not immediately available. Indicates that the
file data has been physically moved to offline storage.
文件的數(shù)據(jù)沒(méi)有被立即用到。指出正在脫機(jī)使用該文件。
FILE_ATTRIBUTE_READONLY The file is read only.Applications can read the file but cannot write
to it or delete it
這個(gè)文件只可讀取.程序可以讀文件,但不可以在上面寫(xiě)入內(nèi)容,也不可刪除.
FILE_ATTRIBUTE_SYSTEM The file is part of or is used exclusively by the operation system.
文件是系統(tǒng)的一部分,或是系統(tǒng)專(zhuān)用的.
FILE_ATTRIBUTE_TEMPORARY The file is being used for temporary storage. File systems attempt
to keep all of the data in memory for quicker access rather than
flushing the data back to mass storage. A temporary file should be
deleted by the application as soon as it is no longer needed.
文件被使用后,文件系統(tǒng)將努力為(文件的)所有數(shù)據(jù)的迅迅訪(fǎng)問(wèn)保持一塊
內(nèi)存。臨時(shí)文件應(yīng)當(dāng)在程序不用時(shí)及時(shí)刪除。
Any combination of the following flags is acceptable for the dwFlagsAndAttributes parameter.
dwFlagAndAttributes可以接受下列標(biāo)志的任意組合。
FLAG(標(biāo)志) Meaning(含義)
FILE_FLAG_WRITE_THROUGH Instructs the system to write through any intermediate cache and go
directly to disk. The system can still cache write operations, but
cannot lazily flush them.
指示系統(tǒng)通過(guò)快速緩存直接寫(xiě)入磁盤(pán),
FILE_FLAG_OVERLAPPED Instructs the system to initialize the object, so that operations that
take a significant amount of time to process return ERROR_IO_PENDING.
When the operation is finished, the specified event is set to the
signaled state.
指示系統(tǒng)初始化對(duì)象, 此操作將對(duì)進(jìn)程設(shè)置一個(gè)引用計(jì)數(shù)并返回ERROR_IO_PENDING.
處理完成后, 指定對(duì)象將被設(shè)置為信號(hào)狀態(tài).
When you specify FILE_FLAG_OVERLAPPED, the file read and write functions
must specify an OVERLAPPED structure. That is, when FILE_FLAG_OVERLAPPED
is specified, an application must perform overlapped parameter(pointing
to an OVERLAPPED structure)to the file read and write functions.
This flag also enables more than one operation to be performed
simultaneously with the handle(a simultaneous read and write operation,
for example).
當(dāng)你指定FILE_FLAG_OVERLAPPED時(shí),讀寫(xiě)文件的函數(shù)必須指定一個(gè)OVERLAPPED結(jié)構(gòu).
并且. 當(dāng)FILE_FLAG_OVERLAPPED被指定, 程序必須執(zhí)行重疊參數(shù)(指向OVERLAPPED
結(jié)構(gòu))去進(jìn)行文件的讀寫(xiě).
這個(gè)標(biāo)志也可以有超過(guò)一個(gè)操作去執(zhí)行.
FILE_FLAG_NO_BUFFERING Instructs the system to open the file with no intermediate buffering or
caching.When combined with FILE_FLAG_OVERLAPPED, the flag gives maximum
asynchronous performance, because the I/O does not rely on the synchronous
operations of the memory manager. However, some I/O operations will take
longer, because data is not being held in the cache.
指示系統(tǒng)不使用快速緩沖區(qū)或緩存,當(dāng)和FILE_FLAG_OVERLAPPED組合,該標(biāo)志給出最
大的異步操作量, 因?yàn)镮/O不依賴(lài)內(nèi)存管理器的異步操作.然而,一些I/O操作將會(huì)運(yùn)行
得長(zhǎng)一些,因?yàn)閿?shù)據(jù)沒(méi)有控制在緩存中.
An application must meet certain requirements when working with files
opened with FILE_FLAG_NO_BUFFERING:
當(dāng)使用FILE_FLAG_NO_BUFFERING打開(kāi)文件進(jìn)行工作時(shí),程序必須達(dá)到下列要求:
File access must begin at byte offsets within the file that are
integer multiples of the volume's sector size.
文件的存取開(kāi)頭的字節(jié)偏移量必須是扇區(qū)尺寸的整倍數(shù).
File access must be for numbers of bytes that are integer
multiples of the volume's sector size. For example, if the sector
size is 512 bytes, an application can request reads and writes of
512, 1024, or 2048 bytes, but not of 335, 981, or 7171bytes.
文件存取的字節(jié)數(shù)必須是扇區(qū)尺寸的整倍數(shù).例如,如果扇區(qū)尺寸是512字節(jié)
程序就可以讀或者寫(xiě)512,1024或者2048字節(jié),但不能夠是335,981或者7171
字節(jié).
buffer addresses for read and write operations must be sector
aligned(aligned on addresses in memory that are integer multiples
of the volume's sector size).
進(jìn)行讀和寫(xiě)操作的地址必須在扇區(qū)的對(duì)齊位置,在內(nèi)存中對(duì)齊的地址是扇區(qū)
尺寸的整倍數(shù).
One way to align buffers on integer multiples of the volume sector size is
to use VirtualAlloc to allocate the buffers, It allocates memory that is
aligned on addresses that are integer multiples of the operating system's
memory page size. Because both memory page and volume sector sizes are
powers of 2, this memory is also aligned on addresses that are integer
multiples of a volume's sector size.
一個(gè)將緩沖區(qū)與扇區(qū)尺寸對(duì)齊的途徑是使用VirtualAlloc函數(shù). 它分配與操作系統(tǒng)
內(nèi)存頁(yè)大小的整倍數(shù)對(duì)齊的內(nèi)存地址.因?yàn)閮?nèi)存頁(yè)尺寸和扇區(qū)尺寸--2都是它們的冪.
這塊內(nèi)存在地址中同樣與扇區(qū)尺寸大小的整倍數(shù)對(duì)齊.
An application can determine a volume's sector size by calling the
GetDiskFreeSpace function
程序可以通過(guò)調(diào)用GetDiskFreeSpace來(lái)確定扇區(qū)的尺寸.
FILE_FLAG_RANDOM_ACCESS
Indicates that the file is accessed randomly. The system can use this as
a hint to optimize file caching.
指定文件是隨機(jī)訪(fǎng)問(wèn),這個(gè)標(biāo)志可以使系統(tǒng)優(yōu)化文件的緩沖.
FILE_FLAG_SEQUENTIAL_SCAN
Indicates that the file is to be accessed sequentially from beginning to
end. The system can use this as a hint to optimize file caching. If an
application moves the file pointer for random access, optimum caching may
not occur; however, correct operation is still guaranteed.
指定文件將從頭到尾連續(xù)地訪(fǎng)問(wèn).這個(gè)標(biāo)志可以提示系統(tǒng)優(yōu)化文件緩沖. 如果程序在
隨機(jī)訪(fǎng)問(wèn)文件中移動(dòng)文件指針,優(yōu)化可能不會(huì)發(fā)生;然而,正確的操作仍然可以得到保
證
Specifying this flag can increase performance for applications that read
large files using sequential access, performance gains can be even more
noticeable for applications that read large files mostly sequentially,
but occasionally skip over small ranges of bytes.
指定這個(gè)標(biāo)志可以提高程序以順序訪(fǎng)問(wèn)模式讀取大文件的性能, 性能的提高在許多
程序讀取一些大的順序文件時(shí)是異常明顯的.但是可能會(huì)有小范圍的字節(jié)遺漏.
FILE_FLAG_DELETE_ON_CLOSE Indicates that the operating system is to delete the file immediately
after all of its handles have been closed, not just the handle for which
you specified FILE_FLAG_DELETE_ON_CLOSE.
指示系統(tǒng)在文件所有打開(kāi)的句柄關(guān)閉后立即刪除文件.不只有你可以指定FILE_FLAG_DELETE_ON_CLOSE
Subsequent open requests for the file will fail, unless FILE_SHARE_DELETE
is used.
如果沒(méi)有使用FILE_SHARE_DELETE,后續(xù)的打開(kāi)文件的請(qǐng)求將會(huì)失敗.
FILE_FLAG_BACKUP_SEMANTICS WINDOWS NT:Indicates that the file is being opened or created for a backup
or restore operation.The system ensures that the calling process overrides
file security checks, provided it has the necessary privileges. The
relevant privileges are SE_BACKUP_NAME and SE_RESTORE_NAME.
WINDOWS NT:指示系統(tǒng)為文件的打開(kāi)或創(chuàng)建執(zhí)行一個(gè)備份或恢復(fù)操作. 系統(tǒng)保證調(diào)
用進(jìn)程忽略文件的安全選項(xiàng),倘若它必須有一個(gè)特權(quán).則相關(guān)的特權(quán)則是SE_BACKUP_NAME
和SE_RESTORE_NAME.
You can also set this flag to obtain a handle to a directory. A directory
handle can be passed to some Win32 functions in place of a file handle.
你也可以使用這個(gè)標(biāo)志獲得一個(gè)文件夾的句柄,一個(gè)文件夾句柄能夠象一個(gè)文件句柄
一樣傳給某些Win32函數(shù)。
FILE_FLAG_POSIX_SEMANTICS Indicates that the file is to be accessed according to POSIX rules. This
includes allowing multiple files with names, differing only in case, for file
systems that support such naming. Use care when using this option because
files created with this flag may not be accessible by applications written
for MS-DOS or 16-bit Windows.
指明文件符合POSIX標(biāo)準(zhǔn).這是在MS-DOS與16位Windows下的標(biāo)準(zhǔn).
FILE_FLAG_OPEN_REPARSE_POINT Specifying this flag inhibits the reparse behavior of NTFS reparse points.
When the file is opened, a file handle is returned, whether the filter that
controls the reparse point is operational or not. This flag cannot be used
with the CREATE_ALWAYS flag.
指定這個(gè)標(biāo)志制約NTFS分區(qū)指針.該標(biāo)志不能夠和CREAT_ALWAYS一起使用.
FILE_FLAG_OPEN_NO_RECALL Indicates that the file data is requested,but it should continue to reside in
remote storage. It should not be transported back to local storage. This flag
is intended for use by remote storage systems or the Hierarchical Storage
Management system.
指明需要文件數(shù)據(jù),但是將繼續(xù)從遠(yuǎn)程存儲(chǔ)器中接收.它不會(huì)將數(shù)據(jù)存放在本地存儲(chǔ)器中.
這個(gè)標(biāo)志由遠(yuǎn)程存儲(chǔ)系統(tǒng)或等級(jí)存儲(chǔ)管理器系統(tǒng)使用.
hTemplateFile
Specifies a handle with GENERIC_READ access to a template file. The template file supplies file attributes and
extended attributes for the file being created.
為GENERIC_READ訪(fǎng)問(wèn)的模式指定一個(gè)句柄到模板文件.模板文件在文件開(kāi)始創(chuàng)建后提供文件屬性和擴(kuò)展屬性.
Return Values
返回值
If the function succeeds, the return value is an open handle to the specified file. If the specified file exists before
the function call and dwCreation is CREATE_ALWAYS or OPEN_ALWAYS, a call to GetLastError returns ERROR_ALREADY_EXISTS
(even though the function has succeeded). If the file does not exist before the call, GetLastError returns zero.
如果函數(shù)成功,返回一個(gè)打開(kāi)的指定文件的句柄.如果指定文件在函數(shù)調(diào)用前已經(jīng)存在并且dwCreation參數(shù)是CREATE_ALWAYS 或者
OPEN_ALWAYS,調(diào)用GetLastError就會(huì)返回ERROR_ALREADY_EXISTS(表示函數(shù)成功). 如果函數(shù)文件在調(diào)用前不存在則會(huì)返回0.
If the function fails, the return value is INVALID_HANDLE_VALUE.To get extended error information, call GetLastError.
如果函數(shù)失敗,返會(huì)值會(huì)是INVALID_HANDLE_VALUE. 更多的錯(cuò)誤信息可以調(diào)用GetLastError來(lái)獲得.
內(nèi)存映射API函數(shù)CreateFileMapping創(chuàng)建一個(gè)有名的共享內(nèi)存:
HANDLE CreateFileMapping(
HANDLE hFile, // 映射文件的句柄,
//設(shè)為0xFFFFFFFF以創(chuàng)建一個(gè)進(jìn)程間共享的對(duì)象
LPSECURITY_ATTRIBUTES lpFileMappingAttributes, // 安全屬性
DWORD flProtect, // 保護(hù)方式
DWORD dwMaximumSizeHigh, //對(duì)象的大小
DWORD dwMaximumSizeLow,
LPCTSTR lpName // 必須為映射文件命名
);
與虛擬內(nèi)存類(lèi)似,保護(hù)方式可以是PAGE_READONLY或是PAGE_READWRITE。如果多進(jìn)程都對(duì)同一共享內(nèi)存進(jìn)行寫(xiě)訪(fǎng)問(wèn),則必須保持相互間同步。映射文件還可以指定PAGE_WRITECOPY標(biāo)志,可以保證其原始數(shù)據(jù)不會(huì)遭到破壞,同時(shí)允許其他進(jìn)程在必要時(shí)自由的操作數(shù)據(jù)的拷貝。
在創(chuàng)建文件映射對(duì)象后使用可以調(diào)用MapViewOfFile函數(shù)映射到本進(jìn)程的地址空間內(nèi)。
下面說(shuō)明創(chuàng)建一個(gè)名為MySharedMem的長(zhǎng)度為4096字節(jié)的有名映射文件:
HANDLE hMySharedMapFile=CreateFileMapping((HANDLE)0xFFFFFFFF),
NULL,PAGE_READWRITE,0,0x1000,"MySharedMem");
并映射緩存區(qū)視圖:
LPSTR pszMySharedMapView=(LPSTR)MapViewOfFile(hMySharedMapFile,
FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);
其他進(jìn)程訪(fǎng)問(wèn)共享對(duì)象,需要獲得對(duì)象名并調(diào)用OpenFileMapping函數(shù)。
HANDLE hMySharedMapFile=OpenFileMapping(FILE_MAP_WRITE,
FALSE,"MySharedMem");
一旦其他進(jìn)程獲得映射對(duì)象的句柄,可以象創(chuàng)建進(jìn)程那樣調(diào)用MapViewOfFile函數(shù)來(lái)映射對(duì)象視圖。用戶(hù)可以使用該對(duì)象視圖來(lái)進(jìn)行數(shù)據(jù)讀寫(xiě)操作,以達(dá)到數(shù)據(jù)通訊的目的。
當(dāng)用戶(hù)進(jìn)程結(jié)束使用共享內(nèi)存后,調(diào)用UnmapViewOfFile函數(shù)以取消其地址空間內(nèi)的視圖:
if (!UnmapViewOfFile(pszMySharedMapView))
{
AfxMessageBox("could not unmap view of file");
}
//=================================================================================
//
CreateFileMapping的MSDN翻譯和使用心得//=================================================================================
測(cè)試創(chuàng)建和打開(kāi)文件映射的時(shí)候老是得到"句柄無(wú)效"的錯(cuò)誤, 仔細(xì)看了MSDN以后才發(fā)覺(jué)是函數(shù)認(rèn)識(shí)不透, 這里把相關(guān)的解釋翻譯出來(lái)
HANDLE CreateFileMapping(
HANDLE hFile, //物理文件句柄
LPSECURITY_ATTRIBUTES lpAttributes, //安全設(shè)置
DWORD flProtect, //保護(hù)設(shè)置
DWORD dwMaximumSizeHigh, //高位文件大小
DWORD dwMaximumSizeLow, //低位文件大小
LPCTSTR lpName //共享內(nèi)存名稱(chēng)
);
1) 物理文件句柄
任何可以獲得的物理文件句柄, 如果你需要?jiǎng)?chuàng)建一個(gè)物理文件無(wú)關(guān)的內(nèi)存映射也無(wú)妨, 將它設(shè)置成為 0xFFFFFFFF(INVALID_HANDLE_VALUE)就可以了.
如果需要和物理文件關(guān)聯(lián), 要確保你的物理文件創(chuàng)建的時(shí)候的訪(fǎng)問(wèn)模式和"保護(hù)設(shè)置"匹配, 比如: 物理文件只讀, 內(nèi)存映射需要讀寫(xiě)就會(huì)發(fā)生錯(cuò)誤. 推薦你的物理文件使用獨(dú)占方式創(chuàng)建.
如果使用 INVALID_HANDLE_VALUE, 也需要設(shè)置需要申請(qǐng)的內(nèi)存空間的大小, 無(wú)論物理文件句柄參數(shù)是否有效, 這樣 CreateFileMapping 就可以創(chuàng)建一個(gè)和物理文件大小無(wú)關(guān)的內(nèi)存空間給你, 甚至超過(guò)實(shí)際文件大小, 如果你的物理文件有效, 而大小參數(shù)為0, 則返回給你的是一個(gè)和物理文件大小一樣的內(nèi)存空間地址范圍. 返回給你的文件映射地址空間是可以通過(guò)復(fù)制, 集成或者命名得到, 初始內(nèi)容為0.
2) 保護(hù)設(shè)置
就是安全設(shè)置, 不過(guò)一般設(shè)置NULL就可以了, 使用默認(rèn)的安全配置. 在win2k下如果需要進(jìn)行限制, 這是針對(duì)那些將內(nèi)存文件映射共享給整個(gè)網(wǎng)絡(luò)上面的應(yīng)用進(jìn)程使用是, 可以考慮進(jìn)行限制.
3) 高位文件大小
弟兄們, 我想目前我們的機(jī)器都是32位的東東, 不可能得到超過(guò)32位進(jìn)程所能尋址的私有32位地址空間, 一般還是設(shè)置0吧, 我沒(méi)有也不想嘗試將它設(shè)置超過(guò)0的情況.
4) 低位文件大小
這個(gè)還是可以進(jìn)行設(shè)置的, 不過(guò)為了讓其他共享用戶(hù)知道你申請(qǐng)的文件映射的相關(guān)信息, 我使用的時(shí)候是在獲得的地址空間頭部添加一個(gè)結(jié)構(gòu)化描述信息, 記錄內(nèi)存映射的大小, 名稱(chēng)等, 這樣實(shí)際申請(qǐng)的空間就比輸入的增加了一個(gè)頭信息結(jié)構(gòu)大小了, 我認(rèn)為這樣類(lèi)似BSTR的方式應(yīng)該是比較合理的.
5) 共享內(nèi)存名稱(chēng)
這個(gè)就是我今天測(cè)試的時(shí)候碰壁的禍根, 因?yàn)闉榱藢?duì)于內(nèi)存進(jìn)行互斥訪(fǎng)問(wèn), 我設(shè)置了一個(gè)互斥句柄, 而名稱(chēng)我選擇和命名共享內(nèi)存同名, 之下就是因?yàn)樗麄兪褂霉餐膎amespace導(dǎo)致了錯(cuò)誤, 呵呵.
7) 調(diào)用CreateFileMapping的時(shí)候GetLastError的對(duì)應(yīng)錯(cuò)誤
ERROR_FILE_INVALID 如果企圖創(chuàng)建一個(gè)零長(zhǎng)度的文件映射, 應(yīng)有此報(bào)
ERROR_INVALID_HANDLE 如果發(fā)現(xiàn)你的命名內(nèi)存空間和現(xiàn)有的內(nèi)存映射, 互斥量, 信號(hào)量, 臨界區(qū)同名就麻煩了
ERROR_ALREADY_EXISTS 表示內(nèi)存空間命名已經(jīng)存在
8) 相關(guān)服務(wù)或者平臺(tái)的命名保留
Terminal Services:
命名可以包含 "Global" 或者 "Local" 前綴在全局或者會(huì)話(huà)名空間初級(jí)文件映射. 其他部分可以包含任何除了()以外的字符, 可以參考 Kernel Object Name Spaces.
Windows 2000 or later:
如果 Terminal Services 沒(méi)有運(yùn)行 "Global" 和 "Local" 前綴的特殊含義就被忽略了
摘 要 該文介紹了用直方圖均衡化對(duì)灰度圖像進(jìn)行灰度映射,從而達(dá)到使圖像增強(qiáng)的目的。利用VC++6.0執(zhí)行效率高,可繼承、封裝、移植等成熟的軟件技術(shù),對(duì)直方圖均衡化算法進(jìn)行實(shí)現(xiàn)。
實(shí)驗(yàn)表明,該程序可以快速、準(zhǔn)確地對(duì)灰度圖像進(jìn)行灰度變換,達(dá)到了使圖像對(duì)比度增強(qiáng),改善圖像質(zhì)量的預(yù)期目的。
關(guān)鍵字 Visual C++;圖像增強(qiáng);直方圖均衡;DIB 文件
由于噪聲、光照等外界環(huán)境或設(shè)備本身的原因,通常我們所獲取的原始數(shù)字圖像質(zhì)量不是很高,因此在對(duì)圖像進(jìn)行邊緣檢測(cè)、圖像分割等操作之前,一般都需要對(duì)原始數(shù)字圖像進(jìn)行增強(qiáng)處理。圖像增強(qiáng)主要有兩方面應(yīng)用,一方面是改善圖像的視覺(jué)效果,另一方面也能提高邊緣檢測(cè)或圖像分割的質(zhì)量,突出圖像的特征,便于計(jì)算機(jī)更有效地對(duì)圖像進(jìn)行識(shí)別和分析。 圖像增強(qiáng)是
圖像處理最關(guān)鍵的研究問(wèn)題之一,圖像增強(qiáng)按作用域可分為兩類(lèi),即空域處理和頻域處理。空域處理是直接對(duì)圖像進(jìn)行處理,而頻域處理則是在圖像的某個(gè)變化域內(nèi),對(duì)圖像的變換系數(shù)進(jìn)行運(yùn)算,然后通過(guò)逆變換獲得圖像增強(qiáng)效果。本文主要對(duì)空域增強(qiáng)法中的直方圖均衡進(jìn)行分析并用VC ++ 6.0進(jìn)行算法實(shí)現(xiàn)。當(dāng)前圖像處理在算法實(shí)現(xiàn)中主要應(yīng)用Matlab 仿真工具,但Matlab運(yùn)行效率較低,且可移植性和實(shí)用性均不太理想。與
Java和C#等其他高級(jí)語(yǔ)言相比,VC++在程序運(yùn)行效率、內(nèi)存使用的可控性和編程的靈活性上均具有較大的優(yōu)勢(shì),因此本文采用VC ++ 6.0 集成開(kāi)發(fā)環(huán)境,以達(dá)到算法快速有效地執(zhí)行,同時(shí)增強(qiáng)了算法的可移植性。
灰度圖像直方圖均衡化的描述 1、灰度圖像直方圖處理方法
圖像的直方圖是圖像處理中一種十分重要且實(shí)用的工具,它概括了一副圖像的灰度級(jí)內(nèi)容。從數(shù)學(xué)上來(lái)說(shuō)圖像直方圖是圖像各灰度值統(tǒng)計(jì)特性與圖像灰度值的函數(shù),它統(tǒng)計(jì)一幅圖像中各個(gè)灰度級(jí)出現(xiàn)的次數(shù)或概率。實(shí)際上,灰度圖像直方圖是一個(gè)離散函數(shù):
pf(fk)=nk/n k=0,1,…,L-1
其中fk為圖像f(x,y)的第k級(jí)灰度,nk是圖像f(x,y)中具有灰度值fk的象素個(gè)數(shù),n是圖像象素總數(shù),L是圖像的灰度級(jí)數(shù)。因?yàn)閜f(fk)給出了對(duì)各個(gè)fk出現(xiàn)概率的一個(gè)統(tǒng)計(jì),所以直方圖提供了圖像的灰度值分布情況。在灰度直方圖坐標(biāo)系中,橫坐標(biāo)表示圖像中各個(gè)像素點(diǎn)的灰度級(jí),縱坐標(biāo)為各個(gè)灰度級(jí)上圖像各個(gè)像素點(diǎn)出現(xiàn)的次數(shù)或概率。在對(duì)灰度數(shù)字圖像的增強(qiáng)處理方法中,灰度均衡化和灰度規(guī)定化應(yīng)用較為廣泛,但后者需要根據(jù)具體的圖像人為規(guī)定好適當(dāng)?shù)钠谕狈綀D才能得到滿(mǎn)意的效果,如果期望直方圖規(guī)定不當(dāng)則處理效果會(huì)很差,因此后者的通用性不好。而前者在處理時(shí)只需要將當(dāng)前的灰度分布重新均衡地分布于整個(gè)灰度區(qū)間即可,雖然對(duì)于某一幅特定的圖象處理效果可能不及灰度規(guī)定化,但通用性卻要好的多,對(duì)任意圖象均可獲得相當(dāng)不錯(cuò)的處理效果。
2、灰度圖像直方圖均衡化算法分析
直方圖均衡化的基本思想是把原始圖的直方圖變換為均勻分布的形式,這樣就增加了象素灰度值的動(dòng)態(tài)范圍從而可達(dá)到增強(qiáng)圖像整體對(duì)比度的效果。設(shè)原始圖像在(x,y)處的灰度為f,而改變后的圖像為g,則對(duì)圖像增強(qiáng)的方法可表述為將在(x,y)處的灰度f(wàn)映射為g。在灰度直方圖均衡化處理中對(duì)圖像的映射函數(shù)可定義為:g = EQ (f),這個(gè)映射函數(shù)EQ(f)必須滿(mǎn)足兩個(gè)條件(其中L為圖像的灰度級(jí)數(shù)):
(1)EQ(f)在0≤f≤L-1范圍內(nèi)是一個(gè)單值單增函數(shù)。這是為了保證增強(qiáng)處理沒(méi)有打亂原始圖像的灰度排列次序,原圖各灰度級(jí)在變換后仍保持從黑到白(或從白到黑)的排列。
(2)對(duì)于0≤f≤L-1有0≤g≤L-1,這個(gè)條件保證了變換前后灰度值動(dòng)態(tài)范圍的一致性。
累計(jì)分布函數(shù)(cumulative distribution function,CDF)即可以滿(mǎn)足上述兩個(gè)條件,并且通過(guò)該函數(shù)可以完成將原圖像f的分布轉(zhuǎn)換成g的均勻分布。此時(shí)的直方圖均衡化映射函數(shù)為:
gk = EQ(fk) = (ni/n) = pf(fi) ,
(k=0,1,2,……,L-1)
上述求和區(qū)間為0到k,根據(jù)該方程可以由源圖像的各像素灰度值直接得到直方圖均衡化后各像素的灰度值。在實(shí)際處理變換時(shí),一般先對(duì)原始圖像的灰度情況進(jìn)行統(tǒng)計(jì)分析,并計(jì)算出原始直方圖分布,然后根據(jù)計(jì)算出的累計(jì)直方圖分布求出fk到gk的灰度映射關(guān)系。在重復(fù)上述步驟得到源圖像所有灰度級(jí)到目標(biāo)圖像灰度級(jí)的映射關(guān)系后,按照這個(gè)映射關(guān)系對(duì)源圖像各點(diǎn)像素進(jìn)行灰度轉(zhuǎn)換,即可完成對(duì)源圖的直方圖均衡化。
VC++ 6.0下自制
媒體播放器
可視動(dòng)畫(huà)控件ActiveMovie是
Microsoft公司開(kāi)發(fā)的ActiveX控件,從開(kāi)始的1.0版、1.2版到現(xiàn)在的2.0版,功能上已經(jīng)有了很大的改進(jìn)。由于該控件內(nèi)嵌了Microsoft MPEG音頻解碼器和Microsoft MPEG視頻解碼器,所以能夠很好地支持音頻文件和視頻文件,用其播放的VCD效果就很好。 另外,播放時(shí)若用鼠標(biāo)右鍵單擊畫(huà)面,可以直接對(duì)畫(huà)面的播放、暫停、停止等進(jìn)行控制,讀者還可以自行在“屬性”欄中對(duì)影片播放進(jìn)行控制設(shè)置,用起來(lái)非常方便。
在Microsoft公司去年推出的VC++6.0中已經(jīng)包含了ActiveMovie控件的2.0版,筆者 在VC++6.0下利用這個(gè)控件自制了一個(gè)簡(jiǎn)易的媒體播放器,除了滿(mǎn)屏功能外,還可以對(duì)音量進(jìn)行控制。下面把具體做法介紹給讀者。
一 建立工程
利用 VC++6.0的AppWizard生成一個(gè)基于對(duì)話(huà)框的工程Player,去掉對(duì)話(huà)框上的確定和取消按鈕,并加入ActiveMovie控件(通常情況下ActiveMovie控件并不出現(xiàn)在控件面板中,可在菜單中依次選擇“project—Add To Project— >Components And Controls”,在出現(xiàn)的“Components And Controls Gallery”對(duì)話(huà)框中打開(kāi)“Registered Active Controls”文件夾,選中“ActiveMovie Control Object”選項(xiàng),按“Insert”后關(guān)閉該對(duì)話(huà)框,ActiveMovie控件便出現(xiàn)在控件面板中),調(diào)整好控件在對(duì)話(huà)框中的位置。為了能夠控制控件的操作,應(yīng)為對(duì)話(huà)框設(shè)計(jì)一個(gè)菜單,菜單的項(xiàng)目可以定為文件、屏幕控制和音量控制。
二 添加代碼
首先利用ClassWizard為ActiveMovie控件聲明一個(gè)變量m_ActiveMovie。然后為菜單文件添加兩個(gè)菜單項(xiàng)打開(kāi)文件和退出,并分別添加函數(shù)OnOpen()和OnExit(),代碼如下:
void Cplayer::OnOpen()
{ // TODO: Add your command handler code here char szFilter[] =
" Video File (*.dat)∣ *.dat∣Wave File (*.wav)∣*.wav∣AVI File (*.avi)∣ (*.avi)∣Movie File
(*.mov)∣(*.mov)∣ Media File (*.mmm)∣(*.mmm)∣Mid File(*.mid;*.rmi)∣ (*.mid;*.rmi)∣MPEG File
(*.mpeg)∣(*.mpeg)∣ All File (*.*)∣*.* ";
//用于設(shè)置FileDialog的文件類(lèi)型
CFileDialog FileDlg( TRUE, NULL, NULL, OFN_HIDEREADONLY, szFilter );
if( FileDlg.DoModal() == IDOK ) { CString PathName = FileDlg.GetPathName();
PathName.MakeUpper();
m_ActiveMovie.SetFileName(PathName);
}
}
OnOpen()函數(shù)的作用是顯示“打開(kāi)”對(duì)話(huà)框,通過(guò)該對(duì)話(huà)框選擇要執(zhí)行的文件。
利用Visual C++實(shí)現(xiàn)AVI文件的圖像截取
劉 濤 yesky
AVI文件就是我們所說(shuō)的
多媒體文件,所謂的AVI圖像就是視頻圖像,該文件是一個(gè)RIFF說(shuō)明文件,它用于獲取、編輯、演示音頻、視頻序列。一般的AVI文件包含音頻流和視頻流,有的特殊的AVI還包含一個(gè)控制路徑或MIDI路徑作為附加的數(shù)據(jù)流。
現(xiàn)在播放AVI文件的軟件很多,但大多無(wú)法從AVI視頻文件中讀取一幀圖像并生成BMP格式的文件。筆者在使用AVI文件開(kāi)發(fā)項(xiàng)目過(guò)程中對(duì)AVI文件的操作積累了一些經(jīng)驗(yàn),對(duì)于如何實(shí)現(xiàn)從AVI視頻流中獲取任意幀的圖像數(shù)據(jù)并存儲(chǔ)成BMP文件,其中最關(guān)鍵的是要從AVI文件中獲取具體某一幀的圖像數(shù)據(jù),為此我利用Windows提供的API函數(shù)實(shí)現(xiàn)了自定義的CAvi類(lèi),用于操作AVI文件。
在使用API函數(shù)操作AVI文件時(shí),一定要注意用AVIFileInit()來(lái)初始化AVI庫(kù),程序結(jié)束時(shí)用AVIFileExit()釋放AVI庫(kù),否則API函數(shù)無(wú)法使用。現(xiàn)以操作包含真彩色圖像的AVI文件為例,給出Cavi類(lèi)的部分函數(shù)的具體實(shí)現(xiàn),其中CaviCreate()函數(shù)用于讀取AVI文件信息并初始化Cavi類(lèi)的成員,例如根據(jù)AVI文件信息定義每幀圖像的寬、高、每幀圖像的信息頭結(jié)構(gòu)等等;函數(shù)AviRead(int mFrame)用于從AVI文件中讀取第mFrame幀。實(shí)現(xiàn)代碼顯示如下:
//Cavi類(lèi)頭文件定義;
class CAvi file://AVI類(lèi),處理AVI文件
{
public:
int cy;//圖象高
int cx;//圖象寬
file://long m_maxFrame;
BYTE *pData;//寸儲(chǔ)圖象數(shù)據(jù)
BITMAPINFO *m_pBMI;//位圖文件信息頭
PAVISTREAM pavi;//AVI流
PAVIFILE pfile;//AVI文件指針
AVIFILEINFO * pfi; file://AVI信息
BOOL AviRead(int mFrame);//讀AVI文件的第mFrame幀
CAvi();//標(biāo)準(zhǔn)構(gòu)造函數(shù)
CAviCreate(CString &string);//用文件名初始化AVI類(lèi)的成員
virtual ~CAvi();
};
//Cavi類(lèi)文件實(shí)現(xiàn)部分;
CAvi::CAvi()
{ AVIFileInit();//初始化AVI庫(kù)
cx=0;//定義圖象寬、高、等成員
cy=0;
m_pBMI=NULL;
pData=NULL;
file://m_maxFrame=0;
pfi=NULL;
}
CAvi::~CAvi()//析構(gòu)、釋放指針
{
// AVIFileClose(pfile);
AVIFileExit();
if(pData!=NULL)
delete pData;
pData=NULL;
if(m_pBMI!=NULL)
delete m_pBMI;
m_pBMI=NULL;
if(pfi!=NULL)
delete pfi;
pfi=NULL;
}
CAvi::CAviCreate(CString &string)//讀文件初始化該類(lèi)
{
HRESULT hr;
pfi=new AVIFILEINFO;
hr = AVIFileOpen(&pfile, // returned file pointer
string, // file name
OF_READ, // mode to open file with
NULL);
hr= AVIFileInfo(pfile, file://獲取AVI信息,放入pfi中
pfi,
sizeof(AVIFILEINFO)
);
cx=pfi->dwWidth;//圖象寬、高
cy=pfi->dwHeight;
hr=AVIFileGetStream(//將AVI變成視頻流
pfile,
&pavi,
streamtypeVIDEO,
0//LONG lParam
);
m_pBMI=new BITMAPINFO;//定義BMP信息頭
m_pBMI->bmiHeader.biBitCount=24;
m_pBMI->bmiHeader.biClrImportant=0;
m_pBMI->bmiHeader.biClrUsed=0;
m_pBMI->bmiHeader.biCompression=BI_RGB;
m_pBMI->bmiHeader.biHeight=cy;
m_pBMI->bmiHeader.biWidth=cx;
m_pBMI->bmiHeader.biPlanes=1;
m_pBMI->bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
m_pBMI->bmiHeader.biXPelsPerMeter=0;
m_pBMI->bmiHeader.biYPelsPerMeter=0;
m_pBMI->bmiHeader.biSizeImage=cx*cy*3;
pData=(BYTE*)new char[cx*cy*3];//根據(jù)AVI中BMP圖象的信息定義緩沖區(qū)
}
BOOL CAvi::AviRead(int mFrame)//將AVI文件的M幀數(shù)據(jù)讀入PData緩沖區(qū)
{
HRESULT hr;
hr= AVIStreamRead( pavi,
mFrame,
1,
pData,
cx*cy*3,
NULL,
NULL
);
if(hr==0)
return TRUE;
else
return FALSE;
}
上述Cavi類(lèi)實(shí)現(xiàn)部分所涉及到的API函數(shù)可以參考微軟提供的MSDN。Cavi類(lèi)中的pData緩沖區(qū)存放AVI文件中的具體某一幀圖像數(shù)據(jù),同時(shí)Cavi類(lèi)的m_pBMI為BMP圖像文件信息結(jié)構(gòu),這時(shí)可以根據(jù)圖像的大小定義BMP圖像文件頭結(jié)構(gòu),關(guān)于BMP文件的存儲(chǔ),由于篇幅的原因,我不在多講了,有興趣的讀者可以參見(jiàn)筆者的拙作"Visual C++6.0開(kāi)發(fā)灰度位圖處理"(天極網(wǎng)軟件欄目2001.9.10發(fā)表),該文里面講述了如何存取BMP文件。以上程序在
Windows2000、Visual C++6.0環(huán)境下順利編譯通過(guò),運(yùn)行正常。
摘要:本文講述了在
Microsoft Visual C++ 6.0下多幅碎片圖像無(wú)縫拼合技術(shù)的實(shí)現(xiàn)原理和過(guò)程,并給出了部分關(guān)鍵代碼以供參考。
關(guān)鍵字:Microsoft Visual C++ 6.0、圖像、無(wú)縫拼合、位圖文件
一、 引言
在測(cè)繪、文博等行業(yè)經(jīng)常會(huì)遇到這樣一種情況:觀(guān)測(cè)對(duì)象比較大,為保證分辨率又不能將其全部照下,只能進(jìn)行局部照相,事后再將這些局部照相的重合部分去掉,拼合成一幅完整的圖像。以前多采用手工拼合,誤差較大,往往不能很好的實(shí)現(xiàn)無(wú)縫拼合,即使有少量的專(zhuān)業(yè)設(shè)備,成本也普遍較高。其實(shí)只需將照片通過(guò)掃描儀將其錄入到計(jì)算機(jī)中,通過(guò)程序處理,完全能很好的實(shí)現(xiàn)多幅圖像的無(wú)縫拼合,滿(mǎn)足實(shí)際需要,而且對(duì)于文博行業(yè)中常會(huì)遇到的破碎的、不規(guī)則對(duì)象如古舊字畫(huà)殘片等也能很好的進(jìn)行無(wú)縫拼合。本文就對(duì)針對(duì)該程序的實(shí)現(xiàn)原理及過(guò)程做了簡(jiǎn)要的介紹。 二、 程序設(shè)計(jì)原理 首先我們從實(shí)際出發(fā),我們是通過(guò)進(jìn)行局部照相的手段來(lái)保存整體的全部信息,而要保證這些局部照片所含的信息之和能包括整體的全部信息就必然的使每?jī)煞徑膱D片有一部分交疊的部分,這樣才能保證在將整體對(duì)象劃分為若干局部照片而后再拼合成整體圖像的過(guò)程中不遺漏任何信息,即該劃分、拼合的整個(gè)過(guò)程是無(wú)損的。既然如此,我們只需能保證讓兩相鄰圖片的重疊部分能完全重合,那么我們也就能夠肯定在此狀態(tài)下的這兩幅圖像實(shí)現(xiàn)了無(wú)縫拼合。所以,問(wèn)題就轉(zhuǎn)換為使相鄰圖片的重疊部分能完全重合,而判斷兩相同的圖像片段是否完全重疊可以用光柵掩碼來(lái)進(jìn)行直觀(guān)的判斷,比如我們可以采用"異或"的掩碼,當(dāng)相同位置上的兩幅圖片的像素相同時(shí)就為0即黑色,所以可以對(duì)兩圖片進(jìn)行移動(dòng),只要重疊部分全黑,則表明此時(shí)兩圖像的重疊部分已準(zhǔn)確的重合了,而此時(shí)也實(shí)現(xiàn)了圖像的無(wú)縫拼合。此后只需再采用"或"的光柵掩碼將合并后的圖像顯示出來(lái),再通過(guò)拷屏等手段將其存盤(pán)即可。在實(shí)現(xiàn)拼合的全過(guò)程中主要涉及到圖像的拖放、圖像文件的讀取及顯示、光柵掩碼、拷屏以及內(nèi)存位圖的保存等多種技術(shù)。接下來(lái)就對(duì)這些技術(shù)的具體應(yīng)用進(jìn)行介紹。
三、 程序的具體實(shí)現(xiàn) 在進(jìn)行拼合之前,首先要將從掃描儀錄入的圖像從文件讀取到內(nèi)存中,并顯示出來(lái)。由于在拼合時(shí)采取的光柵操作掩碼是"異或",所以為保持圖像的原始面貌,可以在消息WM_ERASEBKGND 的響應(yīng)函數(shù)中用PatBlt函數(shù)將整個(gè)客戶(hù)區(qū)的初始背景設(shè)定為黑色:
…… pDC->PatBlt(0,0,rect.Width(),rect.Height(), BLACKNESS); return TRUE; |
讀取位圖文件可以用LoadImage函數(shù)來(lái)實(shí)現(xiàn),m_sPath1指定了文件的路徑,LR_LOADFROMFILE屬性指定從文件中讀取位圖,返回值為該位圖的句柄:
…… HBITMAP hbitmap; hbitmap=(HBITMAP)LoadImage(AfxGetInstanceHandle(), m_sPath1, IMAGE_BITMAP,0,0, LR_LOADFROMFILE|LR_CREATEDIBSECTION); |
之后我們就可以創(chuàng)建一個(gè)和當(dāng)前設(shè)備環(huán)境兼容的內(nèi)存設(shè)備環(huán)境hMemDC1,并將剛才讀取到內(nèi)存的位圖放置到該設(shè)備環(huán)境中:
hMemDC1=::CreateCompatibleDC(NULL); SelectObject(hMemDC1,hbitmap); ::DeleteObject(hbitmap); //釋放掉用過(guò)的位圖句柄 Invalidate(); |
至于位圖的顯示,由于需要頻繁的拖動(dòng)和其他處理,將其放置于OnDraw函數(shù)中較為合理,需要更新顯示時(shí)只需顯式地用Invalidate()函數(shù)刷新即可。OnDraw()中的顯示位圖部分最好用BitBlt函數(shù)來(lái)完成,該函數(shù)負(fù)責(zé)把hMemDC1中的位圖放置到pDC頁(yè)面中以完成內(nèi)存頁(yè)面的置換,其處理速度還是比較快的:
…… ::BitBlt(pDC->m_hDC,m_nX1,m_nY1, m_nWidth1,m_nHeight1, hMemDC1,0,0,m_dwRop); …… |
函數(shù)中的m_dwRop變量對(duì)光柵操作碼進(jìn)行設(shè)置,初始為SRCINVERT即光柵異或操作,當(dāng)拼合成功需要顯示合并后的效果時(shí)再將其設(shè)定為SRCPAINT光柵或操作。
我們可以通過(guò)對(duì)鼠標(biāo)消息響應(yīng)函數(shù)的編程來(lái)實(shí)現(xiàn)在客戶(hù)區(qū)內(nèi)的位圖拖放,按照
Windows系統(tǒng)的習(xí)慣,首先在鼠標(biāo)左鍵的響應(yīng)函數(shù)中通過(guò)PtInRect()函數(shù)判斷鼠標(biāo)在左鍵按下時(shí)是否是落在位圖上,如果是就可以在鼠標(biāo)左鍵彈起之前將圖片隨鼠標(biāo)拖動(dòng)了,顯然這部分應(yīng)在WM_MOUSEMOVE消息的響應(yīng)函數(shù)內(nèi)編寫(xiě)代碼:
…… if(m_bCanMove1==true) //在移動(dòng)之前鼠標(biāo)左鍵是在圖片上點(diǎn)擊的 { int dx=m_nOldX1-m_nX1; //計(jì)算鼠標(biāo)距離圖片原點(diǎn)的距離 int dy=m_nOldY1-m_nY1; m_nX1=point.x-dx; //計(jì)算新的圖片原點(diǎn)的坐標(biāo)(客戶(hù)區(qū)坐標(biāo)) m_nY1=point.y-dy; Invalidate(); //更新視圖 } m_nOldX1=point.x; //保存上一次的鼠標(biāo)位置 m_nOldY1=point.y; …… |
到此為止,可以運(yùn)行程序?qū)Χ喾槠瑘D像進(jìn)行拼合了,用鼠標(biāo)拖動(dòng)一幅圖像在另一幅圖像邊緣移動(dòng),由于采用了"異或"的光柵掩碼,兩幅圖片交疊的地方顏色會(huì)發(fā)生改變,但只有完全重合時(shí)才會(huì)全黑,表明此時(shí)的拼合是無(wú)縫的,將掩碼換為"或"即可將拼合后的圖像顯示出來(lái)。但此時(shí)只是保留在內(nèi)存中,還要經(jīng)過(guò)進(jìn)一步的處理,才能將合并后的圖像存盤(pán)保留。
首先要對(duì)合并后的圖像所在的矩形框的位置、大小進(jìn)行判斷,可以用下面的類(lèi)似代碼來(lái)完成(本例同時(shí)最多能有4幅圖像進(jìn)行拼合):
…… int temp1,temp2,x0,y0,x1,y1; temp1=m_nX1<m_nX2?m_nX1:m_nX2; if(m_sPath3!="")//如果有3幅圖片參與拼合 { if(m_sPath4!="")//如果有4幅圖片參與拼合 temp2=m_nX3<m_nX4?m_nX3:m_nX4; else temp2=m_nX3; x0=temp1<temp2?temp1:temp2; } else x0=temp1; …… temp1=m_nX1+m_nWidth1>m_nX2+m_nWidth2?m_nX1+m_nWidth1:m_nX2+m_nWidth2; if(m_sPath3!="") { if(m_sPath4!="") temp2=m_nX3+m_nWidth3>m_nX4+m_nWidth4?m_nX3+m_nWidth3:m_nX4+m_nWidth4; else temp2=m_nX3+m_nWidth3; x1=temp1>temp2?temp1:temp2; } else x1=temp1; |
可以用類(lèi)似的代碼計(jì)算出y0和y1。在進(jìn)行屏幕截圖之前必須將由x0,y0,x1,y1構(gòu)成的矩形由客戶(hù)坐標(biāo)轉(zhuǎn)換成屏幕坐標(biāo),可以用ClientToScreen()函數(shù)來(lái)實(shí)現(xiàn)。下面是將屏幕指定區(qū)域以位圖形式拷貝到內(nèi)存中去的函數(shù)的主要實(shí)現(xiàn)代碼:
HBITMAP CImageView::CopyScreenToBitmap(LPRECT lpRect) { …… // 確保選定區(qū)域不為空矩形 if(IsRectEmpty(lpRect)) return NULL; //為屏幕創(chuàng)建設(shè)備描述表 hScrDC = CreateDC("DISPLAY", NULL, NULL, NULL); //為屏幕設(shè)備描述表創(chuàng)建兼容的內(nèi)存設(shè)備描述表 hMemDC = CreateCompatibleDC(hScrDC); …… // 創(chuàng)建一個(gè)與屏幕設(shè)備描述表兼容的位圖 hBitmap = CreateCompatibleBitmap(hScrDC, lpRect->Width(),lpRect->Height()); // 把新位圖選到內(nèi)存設(shè)備描述表中 hOldBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap); // 把屏幕設(shè)備描述表拷貝到內(nèi)存設(shè)備描述表中 BitBlt(hMemDC, 0, 0, lpRect->Width(),lpRect->Height, hScrDC, lpRect->left lpRect->top, SRCCOPY); //得到屏幕位圖的句柄 hBitmap =(HBITMAP)SelectObject(hMemDC, hOldBitmap); //清除 DeleteDC(hScrDC); DeleteDC(hMemDC); …… // 返回位圖句柄 return hBitmap; } |
當(dāng)把拼合后的區(qū)域拷貝到內(nèi)存,并獲取到該內(nèi)存位圖的句柄后可以將其通過(guò)剪貼板傳送到其他圖形處理軟件中進(jìn)行進(jìn)一布的處理,也可以按照位圖的格式直接將其保存成文件,為方便計(jì),本例采用了后者。其實(shí)現(xiàn)過(guò)程主要是根據(jù)剛才獲取到的內(nèi)存位圖句柄按格式填充BMP文件的信息頭以及像素陣列,下面就結(jié)合實(shí)現(xiàn)的關(guān)鍵代碼進(jìn)行介紹:
首先獲取
設(shè)備描述表句柄,并用函數(shù)GetDeviceCaps()獲取到當(dāng)前顯示分辨率下每個(gè)像素所占字節(jié)數(shù),并據(jù)此計(jì)算出調(diào)色板的大小:
…… hDC = CreateDC("DISPLAY",NULL,NULL,NULL); iBits = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES); DeleteDC(hDC); if (iBits <= 1) wBitCount = 1; else if (iBits<= 4) wBitCount = 4; else if (iBits<= 8) wBitCount = 8; else if (iBits <= 24) wBitCount = 24; //計(jì)算調(diào)色板大小 …… |
然后就可以設(shè)置位圖信息頭結(jié)構(gòu)了,其中bi 是BITMAPINFOHEADER 結(jié)構(gòu)的實(shí)例對(duì)象:
…… if (wBitCount <= 8) dwPaletteSize = (1<<wBitCount) *sizeof(RGBQUAD); //設(shè)置位圖信息頭結(jié)構(gòu) GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap); bi.biSize = sizeof(BITMAPINFOHEADER); bi.biWidth = Bitmap.bmWidth; bi.biHeight = Bitmap.bmHeight; bi.biPlanes = 1; bi.biBitCount = wBitCount; bi.biCompression = BI_RGB; bi.biSizeImage = 0; bi.biXPelsPerMeter = 0; bi.biYPelsPerMeter = 0; bi.biClrUsed = 0; bi.biClrImportant = 0; |
用GlobalAlloc()函數(shù)根據(jù)計(jì)算的結(jié)果為位圖內(nèi)容分配內(nèi)存,并返回分配得到的內(nèi)存句柄hDib,并用GetStockObject()來(lái)設(shè)置缺省狀態(tài)下的調(diào)色板:
…… dwBmBitsSize = ((Bitmap.bmWidth*wBitCount+31)/32)*4*Bitmap.bmHeight; hDib = GlobalAlloc(GHND,dwBmBitsSize+dwPaletteSize+sizeof(BITMAPINFOHEADER)); lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib); *lpbi = bi; // 處理調(diào)色板 hPal = GetStockObject(DEFAULT_PALETTE); if (hPal) { hDC = ::GetDC(NULL); hOldPal =SelectPalette(hDC, (HPALETTE)hPal, FALSE); RealizePalette(hDC); } // 獲取該調(diào)色板下新的像素值 GetDIBits(hDC, hBitmap, 0, (UINT) Bitmap.bmHeight, (LPSTR)lpbi + sizeof(BITMAPINFOHEADER)+dwPaletteSize, (BITMAPINFO*)lpbi, DIB_RGB_COLORS); //恢復(fù)調(diào)色板 if (hOldPal) { SelectPalette(hDC,(HPALETTE)hOldPal, TRUE); RealizePalette(hDC); ::ReleaseDC(NULL,hDC); } …… |
最后的工作就是創(chuàng)建位圖文件了,需要把設(shè)置好的位圖文件頭和像素點(diǎn)陣信息依次保存到文件中,其中bmfHdr 是BITMAPFILEHEADER位圖文件頭結(jié)構(gòu)的實(shí)例對(duì)象,需要按照BMP位圖的存盤(pán)格式對(duì)其進(jìn)行設(shè)置:
…… fh = CreateFile(lpFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,NULL); // 設(shè)置位圖文件頭 bmfHdr.bfType = 0x4D42; // "BM" dwDIBSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwPaletteSize + dwBmBitsSize; bmfHdr.bfSize = dwDIBSize; bmfHdr.bfReserved1 = 0; bmfHdr.bfReserved2 = 0; bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER)+ dwPaletteSize; //寫(xiě)入位圖文件頭 WriteFile(fh, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER), &dwWritten, NULL); // 寫(xiě)入位圖文件其余內(nèi)容 WriteFile(fh, (LPSTR)lpbi, dwDIBSize,&dwWritten, NULL); …… |
四、程序的實(shí)例檢測(cè) 下面就通過(guò)一個(gè)實(shí)例--拼合一幅古代國(guó)畫(huà)殘片來(lái)對(duì)程序的拼合效果進(jìn)行檢測(cè)。其中圖一到圖三是拼合前的三幅古代國(guó)畫(huà)殘片,圖四是經(jīng)過(guò)本程序處理后存盤(pán)得到的經(jīng)過(guò)無(wú)縫合成的圖片。經(jīng)過(guò)檢測(cè),拼合效果還是相當(dāng)不錯(cuò)的,在碎片圖像的銜接處根本沒(méi)有接縫的存在:
小結(jié):
本程序通過(guò)一個(gè)實(shí)例講述了處理圖片無(wú)縫拼合的一種實(shí)用方法,在測(cè)繪、勘察、文博等行業(yè)均有較大的應(yīng)用潛力。在理解了程序的設(shè)計(jì)思路和編程思想的前提下,結(jié)合具體的實(shí)際需求,通過(guò)對(duì)本文具體代碼的改動(dòng)可以設(shè)計(jì)出更適合本單位實(shí)際情況的類(lèi)似軟件。另外,本文所講述的截取并保存屏幕技術(shù)在類(lèi)似程序的編制上也可以提供一定的參考。本程序在Windows 2000 Professional下,由
Microsoft Visual C++ 6.0編譯通過(guò)。
摘要:本文以
VC++ 6.0為編程工具,講述了采取逆濾波和維納濾波兩種圖像恢復(fù)算法對(duì)退化圖像的恢復(fù)實(shí)現(xiàn)過(guò)程。
引言
圖像恢復(fù)技術(shù)是
圖像處理領(lǐng)域一類(lèi)重要的處理技術(shù),與圖像增強(qiáng)等其他基本圖像處理技術(shù)類(lèi)似,該技術(shù)也是以獲取視覺(jué)質(zhì)量得到某種程度改善為目的的,所不同的是圖像恢復(fù)過(guò)程需要根據(jù)指定的圖像退化模型來(lái)完成,根據(jù)這個(gè)退化模型對(duì)在某種情況下退化或惡化了的退化圖像進(jìn)行恢復(fù),以獲取到原始的、未經(jīng)過(guò)退化的原始圖像。換句話(huà)說(shuō),圖像恢復(fù)的處理過(guò)程實(shí)際是對(duì)退化圖像品質(zhì)的提升,并通過(guò)圖像品質(zhì)的提升來(lái)達(dá)到圖像在視覺(jué)上的改善。本文以VC++作為開(kāi)發(fā)工具,講述了對(duì)退化圖像進(jìn)行逆濾波和維納濾波處理算法。
逆濾波處理 對(duì)圖像進(jìn)行恢復(fù)處理通常需要根據(jù)一定的圖像退化模型來(lái)進(jìn)行,一個(gè)簡(jiǎn)單的通用圖像退化模型可將圖像的退化過(guò)程模型化為一個(gè)作用在原始圖像f(x,y)上的退化系統(tǒng)H,作用結(jié)果與一個(gè)加性噪聲n(x,y)的聯(lián)合作用導(dǎo)致產(chǎn)生出了退化圖像g(x,y),表現(xiàn)為數(shù)學(xué)形式為g(x,y)=H[f(x,y)]+n(x,y)。根據(jù)上述退化系統(tǒng)H可以從給定的退化圖像g(x,y)得到原始圖像f(x,y)的一個(gè)近似結(jié)果。逆濾波處理就是其中一種無(wú)約束恢復(fù)的圖像恢復(fù)技術(shù),其恢復(fù)過(guò)程的數(shù)學(xué)形式可表示為F (u,v)=G(u,v)/H(u,v) (u,v=0,1,…,M-1),其中F(u,v)和G(u,v)分別為圖像f(x,y)和g(x,y)的頻域變換,H(u,v)可看作是一個(gè)濾波函數(shù)。由于圖像在退化過(guò)程中存在噪聲的干擾,因此通常情況下的濾波器往往不是正好的1/H(u,v),而是關(guān)于u和v的某個(gè)非線(xiàn)形的恢復(fù)轉(zhuǎn)移函數(shù)M(u,v)。經(jīng)過(guò)以上的分析,圖像的退化和恢復(fù)過(guò)程(模型)大致可用下圖來(lái)表示:
一種簡(jiǎn)便的恢復(fù)方法是在選取恢復(fù)轉(zhuǎn)移函數(shù)M(u,v) 時(shí),如果u2+v2≤w2,則取值1/H(u,v),否則為1。這樣處理雖然簡(jiǎn)單,但是恢復(fù)后的圖像往往存在較明顯的振鈴現(xiàn)象,通常為了消除振鈴現(xiàn)象,以H(u,v)的值作為判據(jù),如不大于d(0
由于恢復(fù)過(guò)程需要在頻域進(jìn)行,因此需要通過(guò)二維傅立葉變換將圖像由空域變換到頻域。二維的傅立葉變換較一維傅立葉變換要復(fù)雜的多,一般采取連續(xù)2次運(yùn)用一維離散快速傅立葉變換的方法來(lái)實(shí)現(xiàn),即先沿f(x,y)的每一個(gè)x對(duì)y求變換再乘以N得到F(x,v),完成第一步變換。然后再將得到的F(x,v)沿f(x,v)的每一個(gè)v對(duì)x求變換即可得到f(x,y)的最終變換F(u,v),這兩步的數(shù)學(xué)表達(dá)式如下:
F(x,v)=N*[(1/N)* f(x,y)exp[-j2πvy/N]] (v=0,1,……,N-1) F(u,v)=(1/N)* F(x,v)exp[-j2πux/N] (u,v=0,1,……,N-1) |
類(lèi)似也可以得出二維離散傅立葉變換逆變換用一維變換計(jì)算的表達(dá)式:
F(x,v)= F(u,v)exp[j2πux/N] (x,y=0,1,……,N-1) f(x,y)=(1/N)* F(x,v)exp[j2πvy/N]] (y=0,1,……,N-1) |
在分布進(jìn)行一維傅立葉變換時(shí),多采用"蝴蝶圖"的快速算法(詳見(jiàn)信號(hào)處理方面資料),其核心算法如下:
int N=(int)pow(2,M); file://N:序列長(zhǎng)度(2的整數(shù)次冪) ReverseOrder(A,N); file://對(duì)空間序列進(jìn)行倒序 for(int i=1;i<=M;i++){ int b=(int)pow(2,(i-1)); for(int j=0;j<=(b-1);j++) { float p=(float)(pow(2,(M-i))*j*2.0*PI/(float)N); for(int k=j;k<=(N-1);){ float tr=(float)(A[k+b].Re*cos(p)+A[k+b].Im*sin(p)); file://計(jì)算復(fù)數(shù)運(yùn)算A*U float ti=(float)(A[k+b].Im*cos(p)-A[k+b].Re*sin(p)); A[k+b].Re=A[k].Re-tr; file://復(fù)數(shù)運(yùn)算A-tr A[k+b].Im=A[k].Im-ti; A[k].Re+=tr; file://復(fù)數(shù)運(yùn)算A+tr A[k].Im+=ti; k+=b*2; } } } |
傅立葉逆變換的同傅立葉變換比較相似,只是在計(jì)算exp[j2πvy/N]時(shí)同正變換有符號(hào)的區(qū)別,以及在計(jì)算完成后逆變換需要將值除以N,因此不難寫(xiě)出一維傅立葉逆變換的實(shí)現(xiàn)代碼。在進(jìn)行二維傅立葉變換將圖像由空域變換到頻域之前,首先需要通過(guò)補(bǔ)0的手段將點(diǎn)數(shù)非2的整數(shù)次冪的非正方型
網(wǎng)格采樣構(gòu)造為一個(gè)長(zhǎng)寬均為2的整數(shù)次冪的正方型網(wǎng)格:
int WM=(int)(log(W)/log(2)+1.0f); file://計(jì)算圖像寬應(yīng)為2的多少次冪 int HM=(int)(log(H)/log(2)+1.0f); file://計(jì)算圖像高應(yīng)為2的多少次冪 WM=HM=max(WM,HM); file://取二者大值 int WN=(int)pow(2,WM); file://構(gòu)造網(wǎng)格寬度 int HN=(int)pow(2,HM); file://構(gòu)造網(wǎng)格高度 for{int i=0;i;for(int j=0;j if(i U[i*WN*3+j].Re=D[i*W*3+j]; file://D為圖像序列 U[i*WN*3+j].Im=0.0f; }else file://缺位補(bǔ)0 U[i*WN*3+j].Re=U[i*WN*3+j].Im=0.0f; } } |
預(yù)處理完畢后,可對(duì)構(gòu)造網(wǎng)格的每一列分別進(jìn)行一維快速傅立葉變換,并將結(jié)果存放在原位置,結(jié)果乘以N,完成第一步的轉(zhuǎn)換,求得F(x,v):
for(i=0;i for(int j=0;j UH[j].Re=U[j*WN*3+i].Re; UH[j].Im=U[j*WN*3+i].Im; } DFT_FFT(UH,HM); file://對(duì)UH進(jìn)行快速離散傅立葉變換 for(j=0;j U[j*WN*3+i].Re=HN*UH[j].Re; file://N=HN U[j*WN*3+i].Im=HN*UH[j].Im; } } |
隨即對(duì)構(gòu)造網(wǎng)格的每一行進(jìn)行傅立葉變換,得到最終的變換結(jié)果F(u,v):
for(i=0;i for(int k=0;k<3;k++){ file://對(duì)24位位圖的R、G、B三分量均各自進(jìn)行變換 for(int j=0;j UW[j].Re=U[i*WN*3+j*3+k].Re; UW[j].Im=U[i*WN*3+j*3+k].Im; } DFT_FFT(UW,WM); file://對(duì)UW序列進(jìn)行快速離散傅立葉變換 for(j=0;j U[i*WN*3+j*3+k].Re=UW[j].Re; U[i*WN*3+j*3+k].Im=UW[j].Im; } } } |
至于二維傅立葉逆變換則基本上是上述過(guò)程的逆過(guò)程,在此就不再贅述。根據(jù)逆濾波圖像恢復(fù)的設(shè)計(jì)方案,先通過(guò)前面的二維傅立葉變換將退化圖像g(x,y)從空域變換到頻域得到G(u,v),然后在頻域經(jīng)過(guò)恢復(fù)轉(zhuǎn)移函數(shù)M(u,v)的恢復(fù)處理并經(jīng)過(guò)二維傅立葉逆變換將結(jié)果由頻域轉(zhuǎn)換回空域,就可得到經(jīng)過(guò)恢復(fù)處理的近似原始圖像:
…… dsp.DFT_2D_FFT(m_cpBuffer+54,m_nWidth,m_nHeight,U); file://進(jìn)行二維傅立葉變換 for(int i=0;i for(int j=0;j int k=(int)(j/3); D1=(float)sqrt(i*i+k*k); H=1.0f/(1+(D1/D0)*(D1/D0)); file://H(u,v)=1/(1+(u2+v2)/D02)) if(H>0.45f){ file://閥值 d取0.45 U[i*3*WN+j].Re/=H; file://在頻域與M(u,v)相乘 U[i*3*WN+j].Im/=H; }else{ U[i*3*WN+j].Re*=0.6f; file://如未超過(guò)閥值則M(u,v)取常數(shù)k=0.6 U[i*3*WN+j].Im*=0.6f; } } } dsp.DFT_2D_IFFT(m_cpBuffer+54,m_nWidth,m_nHeight,U); file://進(jìn)行傅立葉逆變換 |
這里的逆濾波處理算法采用的是經(jīng)過(guò)改進(jìn)的恢復(fù)轉(zhuǎn)移函數(shù)M(u,v),因此恢復(fù)后的圖像不會(huì)出現(xiàn)振鈴現(xiàn)象。以標(biāo)準(zhǔn)檢測(cè)圖像Lina為處理對(duì)象應(yīng)用以上恢復(fù)處理算法,效果如下圖所示。其中間圖像為未經(jīng)過(guò)改進(jìn)的簡(jiǎn)單算法,在胳膊和臉部存在較明顯的振鈴現(xiàn)象,而采取了改進(jìn)措施的圖像則沒(méi)有任何振鈴現(xiàn)象出現(xiàn),圖像得到了較好的恢復(fù)。
維納濾波處理
維納(Wiener)濾波是對(duì)退化圖像進(jìn)行恢復(fù)處理的另一種常用算法,是一種有約束的恢復(fù)處理方法,其采用的維納濾波器是一種最小均方誤差濾波器,其數(shù)學(xué)形式比較復(fù)雜:
F(u,v)=[(1/H(u,v))*(|H(u,v)|2)/(|H(u,v)|2+s*[Sn(u,v)/Sf(u,v)])]*G(u,v) |
當(dāng)s為1時(shí),上式就是普通的維納濾波;如果s為變量,則為參數(shù)維納濾波,如果沒(méi)有噪聲干擾,即Sn(u,v)=0時(shí),上式實(shí)際就是前面的逆濾波。從其數(shù)學(xué)形式可以看出:維納濾波比逆濾波在對(duì)噪聲的處理方面要強(qiáng)一些。以上只是理論上的數(shù)學(xué)形式,在進(jìn)行實(shí)際處理時(shí),往往不知道噪聲函數(shù)Sn(u,v)和Sf(u,v)的分布情況,因此在實(shí)際應(yīng)用時(shí)多用下式進(jìn)行近似處理:
F(u,v)=[(1/H(u,v))* (|H(u,v)|2)/(|H(u,v)|2+K)]*G(u,v) |
其中K是一個(gè)預(yù)先設(shè)定的常數(shù)。由此可以寫(xiě)出維納濾波的實(shí)現(xiàn)代碼:
…… float K=0.05f; file://預(yù)先設(shè)定常數(shù)K dsp.DFT_2D_FFT(m_cpBuffer+54,m_nWidth,m_nHeight,U); file://轉(zhuǎn)換到頻域 for(int i=0;i for(int j=0;j int k=(int)(j/3); D1=(float)sqrt(i*i+k*k); float H=1.0f/(1+(D1/D0)*(D1/D0));//H(u,v)= 1/(1+(u2+v2)/D02)) U[i*3*WN+j].Re=(U[i*3*WN+j].Re*H)/(H*H+K); file://維納濾波 U[i*3*WN+j].Im=(U[i*3*WN+j].Im*H)/(H*H+K); } } dsp.DFT_2D_IFFT(m_cpBuffer+54,m_nWidth,m_nHeight,U);//返回到空域 |
對(duì)經(jīng)過(guò)退化的Lina圖像應(yīng)用維納濾波處理,可得到如右圖所示的恢復(fù)效果圖。由于維納濾波在進(jìn)行恢復(fù)時(shí)對(duì)噪聲進(jìn)行了處理,因此其恢復(fù)效果要比逆濾波要好,尤其是退化圖像的噪聲干擾較強(qiáng)時(shí)效果更為明顯。
小結(jié) 本文對(duì)比較常用的兩種圖像恢復(fù)算法逆濾波和維納濾波的實(shí)現(xiàn)過(guò)程作了較為詳細(xì)的講述,通過(guò)對(duì)圖像質(zhì)量較低的退化圖像應(yīng)用上述算法可以使圖像質(zhì)量得到一定程度的改善,在視覺(jué)上可以得到較好的改觀(guān)。類(lèi)似的圖像恢復(fù)算法還有有約束最小平方恢復(fù)算法等多種,應(yīng)視具體情況靈活選擇合適的算法以獲取最佳的恢復(fù)效果。本文所述程序在
Windows 98下,由
Microsoft Visual C++ 6.0編譯通過(guò)。