subject_19747.htm

来自「一些关于vc的问答」· HTM 代码 · 共 12 行

HTM
12
字号
<p>
序号:19747 发表者:五位 发表日期:2002-10-31 10:40:20
<br>主题:请教:徐景周
<br>内容:请问如何用VC在摄像区域画线,请赐教,谢谢
<br><a href="javascript:history.go(-1)">返回上页</a><br><a href=http://www.copathway.com/cndevforum/>访问论坛</a></p>
<hr size=1>
<blockquote><p>
<font color=red>答案被接受</font><br>回复者:徐景周 回复日期:2002-10-31 11:13:38
<br>内容:可先将它选入内存位图中,再在上面画线等,然后保存试试。<BR><BR>类似如下:<BR>二、 程序设计原理<BR><BR>首先我们从实际出发,我们是通过进行局部照相的手段来保存整体的全部信息,而要保证这些局部照片所含的信息之和能包括整体的全部信息就必然的使每两幅邻近的图片有一部分交叠的部分,这样才能保证在将整体对象划分为若干局部照片而后再拼合成整体图像的过程中不遗漏任何信息,即该划分、拼合的整个过程是无损的。既然如此,我们只需能保证让两相邻图片的重叠部分能完全重合,那么我们也就能够肯定在此状态下的这两幅图像实现了无缝拼合。所以,问题就转换为使相邻图片的重叠部分能完全重合,而判断两相同的图像片段是否完全重叠可以用光栅掩码来进行直观的判断,比如我们可以采用"异或"的掩码,当相同位置上的两幅图片的像素相同时就为0即黑色,所以可以对两图片进行移动,只要重叠部分全黑,则表明此时两图像的重叠部分已准确的重合了,而此时也实现了图像的无缝拼合。此后只需再采用"或"的光栅掩码将合并后的图像显示出来,再通过拷屏等手段将其存盘即可。在实现拼合的全过程中主要涉及到图像的拖放、图像文件的读取及显示、光栅掩码、拷屏以及内存位图的保存等多种技术。接下来就对这些技术的具体应用进行介绍。<BR><BR>三、 程序的具体实现<BR><BR>在进行拼合之前,首先要将从扫描仪录入的图像从文件读取到内存中,并显示出来。由于在拼合时采取的光栅操作掩码是"异或",所以为保持图像的原始面貌,可以在消息WM_ERASEBKGND 的响应函数中用PatBlt函数将整个客户区的初始背景设定为黑色:<BR><BR>……<BR>pDC-&gt;PatBlt(0,0,rect.Width(),rect.Height(), BLACKNESS);<BR>return TRUE;<BR><BR>读取位图文件可以用LoadImage函数来实现,m_sPath1指定了文件的路径,LR_LOADFROMFILE属性指定从文件中读取位图,返回值为该位图的句柄:<BR><BR>……<BR>HBITMAP hbitmap;<BR>hbitmap=(HBITMAP)LoadImage(AfxGetInstanceHandle(),<BR>m_sPath1,<BR>IMAGE_BITMAP,0,0,<BR>LR_LOADFROMFILE|LR_CREATEDIBSECTION);<BR><BR>之后我们就可以创建一个和当前设备环境兼容的内存设备环境hMemDC1,并将刚才读取到内存的位图放置到该设备环境中:<BR><BR>hMemDC1=::CreateCompatibleDC(NULL);<BR>SelectObject(hMemDC1,hbitmap);<BR>::DeleteObject(hbitmap); //释放掉用过的位图句柄 <BR>Invalidate();<BR><BR>至于位图的显示,由于需要频繁的拖动和其他处理,将其放置于OnDraw函数中较为合理,需要更新显示时只需显式地用Invalidate()函数刷新即可。OnDraw()中的显示位图部分最好用BitBlt函数来完成,该函数负责把hMemDC1中的位图放置到pDC页面中以完成内存页面的置换,其处理速度还是比较快的:<BR><BR>……<BR>::BitBlt(pDC-&gt;m_hDC,m_nX1,m_nY1, m_nWidth1,m_nHeight1,hMemDC1,0,0,m_dwRop);<BR>……<BR><BR>函数中的m_dwRop变量对光栅操作码进行设置,初始为SRCINVERT即光栅异或操作,当拼合成功需要显示合并后的效果时再将其设定为SRCPAINT光栅或操作。<BR><BR>我们可以通过对鼠标消息响应函数的编程来实现在客户区内的位图拖放,按照Windows系统的习惯,首先在鼠标左键的响应函数中通过PtInRect()函数判断鼠标在左键按下时是否是落在位图上,如果是就可以在鼠标左键弹起之前将图片随鼠标拖动了,显然这部分应在WM_MOUSEMOVE消息的响应函数内编写代码:<BR><BR>……<BR>if(m_bCanMove1==true) //在移动之前鼠标左键是在图片上点击的<BR>{<BR>int dx=m_nOldX1-m_nX1; //计算鼠标距离图片原点的距离<BR>int dy=m_nOldY1-m_nY1;<BR>m_nX1=point.x-dx; //计算新的图片原点的坐标(客户区坐标)<BR>m_nY1=point.y-dy;<BR>Invalidate(); //更新视图<BR>}<BR>m_nOldX1=point.x; //保存上一次的鼠标位置<BR>m_nOldY1=point.y;<BR>……<BR><BR>到此为止,可以运行程序对多幅碎片图像进行拼合了,用鼠标拖动一幅图像在另一幅图像边缘移动,由于采用了"异或"的光栅掩码,两幅图片交叠的地方颜色会发生改变,但只有完全重合时才会全黑,表明此时的拼合是无缝的,将掩码换为"或"即可将拼合后的图像显示出来。但此时只是保留在内存中,还要经过进一步的处理,才能将合并后的图像存盘保留。<BR><BR>首先要对合并后的图像所在的矩形框的位置、大小进行判断,可以用下面的类似代码来完成(本例同时最多能有4幅图像进行拼合):<BR><BR>……<BR>int temp1,temp2,x0,y0,x1,y1;<BR>temp1=m_nX1&lt;M_NX2?M_NX1:M_NX2;&lt; td&gt;<BR>if(m_sPath3!="")//如果有3幅图片参与拼合<BR>{<BR>if(m_sPath4!="")//如果有4幅图片参与拼合<BR>temp2=m_nX3&lt;M_NX4?M_NX3:M_NX4;&lt; td&gt;<BR>else<BR>temp2=m_nX3;<BR>x0=temp1&lt;TEMP2?TEMP1:TEMP2;&lt; td&gt;<BR>}<BR>else<BR>x0=temp1;<BR>……<BR>temp1=m_nX1+m_nWidth1&gt;m_nX2+m_nWidth2?m_nX1+m_nWidth1:m_nX2+m_nWidth2;<BR>if(m_sPath3!="")<BR>{<BR>if(m_sPath4!="")<BR>temp2=m_nX3+m_nWidth3&gt;m_nX4+m_nWidth4?m_nX3+m_nWidth3:m_nX4+m_nWidth4;<BR>else<BR>temp2=m_nX3+m_nWidth3;<BR>x1=temp1&gt;temp2?temp1:temp2;<BR>}<BR>else<BR>x1=temp1;<BR><BR>可以用类似的代码计算出y0和y1。在进行屏幕截图之前必须将由x0,y0,x1,y1构成的矩形由客户坐标转换成屏幕坐标,可以用ClientToScreen()函数来实现。下面是将屏幕指定区域以位图形式拷贝到内存中去的函数的主要实现代码:<BR><BR>HBITMAP CImageView::CopyScreenToBitmap(LPRECT lpRect)<BR>{<BR>……<BR>// 确保选定区域不为空矩形 <BR>if(IsRectEmpty(lpRect)) <BR>return NULL;<BR>//为屏幕创建设备描述表 <BR>hScrDC = CreateDC("DISPLAY", NULL, NULL, NULL);<BR>//为屏幕设备描述表创建兼容的内存设备描述表 <BR>hMemDC = CreateCompatibleDC(hScrDC); <BR>……<BR>// 创建一个与屏幕设备描述表兼容的位图<BR>hBitmap = CreateCompatibleBitmap(hScrDC, lpRect-&gt;Width(),lpRect-&gt;Height());<BR>// 把新位图选到内存设备描述表中<BR>hOldBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap); <BR>// 把屏幕设备描述表拷贝到内存设备描述表中<BR>BitBlt(hMemDC, 0, 0, lpRect-&gt;Width(),lpRect-&gt;Height, <BR>hScrDC, lpRect-&gt;left lpRect-&gt;top, SRCCOPY); <BR>//得到屏幕位图的句柄<BR>hBitmap =(HBITMAP)SelectObject(hMemDC, hOldBitmap); <BR>//清除<BR>DeleteDC(hScrDC);<BR>DeleteDC(hMemDC);<BR>……<BR>// 返回位图句柄 <BR>return hBitmap;<BR>}<BR><BR>当把拼合后的区域拷贝到内存,并获取到该内存位图的句柄后可以将其通过剪贴板传送到其他图形处理软件中进行进一布的处理,也可以按照位图的格式直接将其保存成文件,为方便计,本例采用了后者。其实现过程主要是根据刚才获取到的内存位图句柄按格式填充BMP文件的信息头以及像素阵列,下面就结合实现的关键代码进行介绍:<BR><BR>首先获取设备描述表句柄,并用函数GetDeviceCaps()获取到当前显示分辨率下每个像素所占字节数,并据此计算出调色板的大小:<BR><BR>……<BR>hDC = CreateDC("DISPLAY",NULL,NULL,NULL);<BR>iBits = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES);<BR>DeleteDC(hDC);<BR>if (iBits &lt;= 1)<BR>wBitCount = 1; <BR>else if (iBits&lt;= 4)<BR>wBitCount = 4; <BR>else if (iBits&lt;= 8)<BR>wBitCount = 8;<BR>else if (iBits &lt;= 24)<BR>wBitCount = 24; //计算调色板大小<BR>……<BR>然后就可以设置位图信息头结构了,其中bi 是BITMAPINFOHEADER 结构的实例对象:<BR>……<BR>if (wBitCount &lt;= 8)<BR>dwPaletteSize = (1&lt;&lt; *sizeof(RGBQUAD);&gt;<BR>GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap);<BR>bi.biSize = sizeof(BITMAPINFOHEADER);<BR>bi.biWidth = Bitmap.bmWidth;<BR>bi.biHeight = Bitmap.bmHeight;<BR>bi.biPlanes = 1;<BR>bi.biBitCount = wBitCount; <BR>bi.biCompression = BI_RGB;<BR>bi.biSizeImage = 0; <BR>bi.biXPelsPerMeter = 0;<BR>bi.biYPelsPerMeter = 0;<BR>bi.biClrUsed = 0;<BR>bi.biClrImportant = 0;<BR>用GlobalAlloc()函数根据计算的结果为位图内容分配内存,并返回分配得到的内存句柄hDib,<BR>并用GetStockObject()来设置缺省状态下的调色板:<BR>……<BR>dwBmBitsSize = ((Bitmap.bmWidth*wBitCount+31)/32)*4*Bitmap.bmHeight; <BR>hDib = GlobalAlloc(GHND,dwBmBitsSize+dwPaletteSize+sizeof(BITMAPINFOHEADER));<BR>lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib); <BR>*lpbi = bi; // 处理调色板 <BR>hPal = GetStockObject(DEFAULT_PALETTE); <BR>if (hPal)<BR>{<BR>hDC = ::GetDC(NULL);<BR>hOldPal =SelectPalette(hDC, (HPALETTE)hPal, FALSE);<BR>RealizePalette(hDC); <BR>}<BR>// 获取该调色板下新的像素值<BR>GetDIBits(hDC, hBitmap, 0, (UINT) Bitmap.bmHeight,<BR>(LPSTR)lpbi + sizeof(BITMAPINFOHEADER)+dwPaletteSize,<BR>(BITMAPINFO*)lpbi, DIB_RGB_COLORS);<BR>//恢复调色板 <BR>if (hOldPal)<BR>{<BR>SelectPalette(hDC,(HPALETTE)hOldPal, TRUE); <BR>RealizePalette(hDC);<BR>::ReleaseDC(NULL,hDC); <BR>}<BR>……<BR><BR>最后的工作就是创建位图文件了,需要把设置好的位图文件头和像素点阵信息依次保存到文件中,其中bmfHdr 是BITMAPFILEHEADER位图文件头结构的实例对象,需要按照BMP位图的存盘格式对其进行设置:<BR><BR>……<BR>fh = CreateFile(lpFileName, <BR>GENERIC_WRITE, 0, NULL,<BR>CREATE_ALWAYS,<BR>FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, <BR>NULL);<BR>// 设置位图文件头<BR>bmfHdr.bfType = 0x4D42; // "BM"<BR>dwDIBSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)<BR>+ dwPaletteSize + dwBmBitsSize; <BR>bmfHdr.bfSize = dwDIBSize;<BR>bmfHdr.bfReserved1 = 0;<BR>bmfHdr.bfReserved2 = 0; <BR>bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + <BR>(DWORD)sizeof(BITMAPINFOHEADER)+ dwPaletteSize; <BR>//写入位图文件头<BR>WriteFile(fh, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER), &dwWritten, NULL); <BR>// 写入位图文件其余内容<BR>WriteFile(fh, (LPSTR)lpbi, dwDIBSize,&dwWritten, NULL);<BR>……<BR><BR>
<br>
<a href="javascript:history.go(-1)">返回上页</a><br><a href=http://www.copathway.com/cndevforum/>访问论坛</a></p></blockquote>

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?