Windows程序调用OpenCV进行图像捕捉和处理显示
1. OpenCV的视频接口VideoCapture 很简洁,显示可以直接把图片帧数据转换成BMP,用StretchDIBits显示,方便又灵活。
2. Windows用BMP位图显示,BMP位图是行数据4字节对齐的,Y轴是倒的。和Mat数据转换的时候要注意行数据长度。
3. 对于单通道图的显示,有调色板,比较烦,在显示时可以直接转换成4通道图,比较直观。
4.程序界面如下,在左边显示摄像头视频,右边显示对一帧图像的处理结果。
OpenCV Win32程序创建:
1.下载opencv-4.6.0-vc14_vc15.exe,解压到一个目录
2. VS2022新建Win32工程,加入头文件和lib路径。
3. 调试运行程序把opencv_world460d.dll放到工作目录下,也可以用配置系统路径的方法,看个人习惯。
4. 程序中要加入头文件和库文件
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\highgui\highgui_c.h>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\opencv.hpp>
#pragma comment(lib,"opencv_world460d.lib");
using namespace cv;
using namespace std;
源程序OC3.cpp
// OC3.cpp : Defines the entry point for the application. // #include "framework.h" #include "OC3.h"
#include <Commctrl.h> #include <opencv2\core\core.hpp> #include <opencv2\highgui\highgui.hpp> #include <opencv2\highgui\highgui_c.h> //cvGetWondowHandle #include <opencv2\imgproc\imgproc.hpp> #include <opencv2\opencv.hpp> #pragma comment(lib,"comctl32.lib") #pragma comment(lib,"opencv_world460d.lib"); using namespace cv; using namespace std; #define MAX_LOADSTRING 100 //xgz====================== #define IDC_RICHEDIT 1010 #define IDC_EDIT 1011 #define IDC_LIST 1012 #define IDC_EDITINPUT 1013 #define IDC_MAINTOOLSBAR 1014 #define IDM_TEST_TEST1 1015 #define IDM_TEST_TEST2 1016 #define IDM_TEST_TEST3 1017 #define IDM_TEST_TEST4 1018 #define IDM_TEST_TEST5 1019 #define IDM_TEST_TEST6 1020 HWND hWndMain; HWND hWndMainToolbar; HWND hWndEdit; //-------------------------------------------- // Global Variables: HINSTANCE hInst; // current instance WCHAR szTitle[MAX_LOADSTRING]; // The title bar text WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name // Forward declarations of functions included in this code module: ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
//xgz==================================== int OnCreate(HWND, UINT, WPARAM, LPARAM); int OnSize(HWND, UINT, WPARAM, LPARAM); int OnPaint(HWND, UINT, WPARAM, LPARAM); int OnVScroll(HWND, UINT, WPARAM, LPARAM); int OnTimer(HWND, UINT, WPARAM, LPARAM); int OnTest1(HWND, UINT, WPARAM, LPARAM); int OnTest2(HWND, UINT, WPARAM, LPARAM); int OnTest3(HWND, UINT, WPARAM, LPARAM); int OnTest4(HWND, UINT, WPARAM, LPARAM); int OnTest5(HWND, UINT, WPARAM, LPARAM); int OnTest6(HWND, UINT, WPARAM, LPARAM); int OnListSelChange(HWND, UINT, WPARAM, LPARAM); int OnEditInputUpdate(HWND, UINT, WPARAM, LPARAM); int OnEditInputChange(HWND, UINT, WPARAM, LPARAM); int iStart = 0; int iGetPicture = 0; int iGetPictureOK = 0; Mat mPicture; VOID ThreadCapture(PVOID pData); //打印输出 //int PRINT(TCHAR* fmt, ...) TCHAR buffer[10000]; int PRINT(const TCHAR* fmt, ...) { va_list argptr; int cnt; int iEditTextLength; HWND hWnd = hWndEdit; if (NULL == hWnd) return 0; va_start(argptr, fmt); //cnt = _vstprintf(buffer, fmt, argptr); // A or W but ISO C //cnt = vswprintf(buffer, fmt, argptr); // only W cnt = wvsprintf(buffer, fmt, argptr); // not %f va_end(argptr); iEditTextLength = GetWindowTextLength(hWnd); if (iEditTextLength + cnt > 30000) // edit text max length is 30000 { SendMessage(hWnd, EM_SETSEL, 0, 10000); SendMessage(hWnd, WM_CLEAR, 0, 0); iEditTextLength = iEditTextLength - 10000; } SendMessage(hWnd, EM_SETSEL, iEditTextLength, iEditTextLength); SendMessage(hWnd, EM_REPLACESEL, 0, (LPARAM)buffer); return(cnt); } //--------------------------------------- int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // TODO: Place code here. // Initialize global strings LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadStringW(hInstance, IDC_OC3, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance); // Perform application initialization: if (!InitInstance (hInstance, nCmdShow)) { return FALSE; } HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_OC3)); MSG msg; // Main message loop: while (GetMessage(&msg, nullptr, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return (int) msg.wParam; } ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEXW wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_OC3)); wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_OC3); wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); return RegisterClassExW(&wcex); } BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { hInst = hInstance; // Store instance handle in our global variable HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr); if (!hWnd) { return FALSE; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd);
return TRUE; } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_COMMAND: { int wmId = LOWORD(wParam); // Parse the menu selections: switch (wmId) { case IDM_TEST_TEST1: OnTest1(hWnd, message, wParam, lParam); break; case IDM_TEST_TEST2: OnTest2(hWnd, message, wParam, lParam); break; case IDM_TEST_TEST3: OnTest3(hWnd, message, wParam, lParam); break; case IDM_TEST_TEST4: OnTest4(hWnd, message, wParam, lParam); break; case IDM_TEST_TEST5: OnTest5(hWnd, message, wParam, lParam); break; case IDM_TEST_TEST6: OnTest6(hWnd, message, wParam, lParam); break; case IDM_ABOUT: DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } } break; case WM_CREATE: OnCreate(hWnd, message, wParam, lParam); break; case WM_SIZE: OnSize(hWnd, message, wParam, lParam); break; case WM_PAINT: OnPaint(hWnd, message, wParam, lParam); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } // Message handler for about box. INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: return (INT_PTR)TRUE; case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; } break; } return (INT_PTR)FALSE; } HIMAGELIST g_hImageList = NULL; HWND CreateSimpleToolbar(HWND hWndParent) { // Declare and initialize local constants. const int ImageListID = 0; const int numButtons = 4; const int bitmapSize = 16; const DWORD buttonStyles = BTNS_AUTOSIZE; // Create the toolbar. HWND hWndToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, WS_CHILD | TBSTYLE_WRAPABLE, 0, 0, 0, 0, hWndParent, NULL, hInst, NULL); if (hWndToolbar == NULL) return NULL; // Create the image list. g_hImageList = ImageList_Create(bitmapSize, bitmapSize, // Dimensions of individual bitmaps. ILC_COLOR16 | ILC_MASK, // Ensures transparent background. numButtons, 0); // Set the image list. SendMessage(hWndToolbar, TB_SETIMAGELIST, (WPARAM)ImageListID, (LPARAM)g_hImageList); // Load the button images. SendMessage(hWndToolbar, TB_LOADIMAGES, (WPARAM)IDB_STD_SMALL_COLOR, (LPARAM)HINST_COMMCTRL); // Initialize button info. // IDM_NEW, IDM_OPEN, and IDM_SAVE are application-defined command constants. TBBUTTON tbButtons[numButtons] = { { MAKELONG(STD_FILENEW, ImageListID), IDM_TEST_TEST1, TBSTATE_ENABLED, buttonStyles, {0}, 0, (INT_PTR)L"Cap" }, { MAKELONG(STD_FILEOPEN, ImageListID), IDM_TEST_TEST2, TBSTATE_ENABLED, buttonStyles, {0}, 0, (INT_PTR)L"Open"}, { MAKELONG(STD_PRINTPRE, ImageListID), IDM_TEST_TEST3, TBSTATE_ENABLED, buttonStyles, {0}, 0, (INT_PTR)L"Cap1"}, { MAKELONG(STD_FILESAVE, ImageListID), IDM_TEST_TEST3, 0, buttonStyles, {0}, 0, (INT_PTR)L"Save"} }; // Add buttons. SendMessage(hWndToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0); SendMessage(hWndToolbar, TB_ADDBUTTONS, (WPARAM)numButtons, (LPARAM)&tbButtons); // Resize the toolbar, and then show it. SendMessage(hWndToolbar, TB_AUTOSIZE, 0, 0); ShowWindow(hWndToolbar, TRUE); return hWndToolbar; } int OnCreate(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { InitCommonControls();
hWndEdit = CreateWindow(_T("edit"), NULL, WS_CHILD | WS_BORDER | WS_VISIBLE | ES_MULTILINE | WS_VSCROLL | ES_READONLY, 0, 0, 0, 0, hWnd, (HMENU)IDC_EDIT, hInst, NULL); hWndMainToolbar = CreateSimpleToolbar(hWnd); hWndMain = hWnd; return 1; } //留一个状态条的位置 int OnSize(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int cxClient, cyClient; cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); MoveWindow(hWndEdit, 5, cyClient - 130, cxClient - 10, 120, TRUE); return DefWindowProc(hWnd, message, wParam, lParam); } int OnPaint(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; HDC hdc; hdc = BeginPaint(hWnd, &ps); EndPaint(hWnd, &ps); return 1; } //xgz 这个函数为pBmp分配了内存,用完要释放 int Mat2Bmp(cv::Mat* pMat, unsigned char*& pBmp, unsigned long& bmpsize) { if (!pMat) { return -1; } Mat image; if (pMat->channels() == 1) { cvtColor(*pMat, image, CV_GRAY2BGRA); //XGZ 单通道图改成4通道 } else { image = *pMat; } int depth = image.depth(); int channels = image.channels(); int width = image.cols; int height = image.rows; unsigned int pixelSize = (8 << (depth / 2)) * channels; // 计算像素的bit数 int colorTableSize = 0; //xgz 不用调色板 unsigned long bmplineSize = ((width * pixelSize + 31) >> 5) << 2; // xgz bfWidth *3+skip unsigned long matlineSize = width * pixelSize / 8; // XGZ Mat line SIZE int skp = bmplineSize - matlineSize; //填充数 // bmp图片的大小, sizeof(BITMAPFILEHEADER) = 14, sizeof(BITMAPINFOHEADER) = 40 bmpsize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + colorTableSize + height * bmplineSize; pBmp = (uchar*)malloc(bmpsize); //XGZ 分配内存,用完要释放 if (!pBmp) { return -2; } memset(pBmp, 0, bmpsize); //===构造bmp图片的文件头==== BITMAPFILEHEADER* pFileHead = (BITMAPFILEHEADER*)pBmp; pFileHead->bfType = 0x4D42; // 0x4D42 代表 “BM”,位图标志 pFileHead->bfSize = bmpsize; pFileHead->bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + colorTableSize; // 图像数据偏移量 //===构造bmp图片的信息头=== BITMAPINFOHEADER* pInfoHead = (BITMAPINFOHEADER*)(&pBmp[sizeof(BITMAPFILEHEADER)]); pInfoHead->biSize = 40; // 信息头的大小 pInfoHead->biWidth = width; pInfoHead->biHeight = height; pInfoHead->biPlanes = 1; // 图像平面数,必须是1 pInfoHead->biBitCount = pixelSize; // 图像每个像素所占的位数 pInfoHead->biCompression = 0; // 0:不压缩,1:REL8, 2:REL4 pInfoHead->biSizeImage = height * bmplineSize; // 图像数据大小 pInfoHead->biXPelsPerMeter = 0; // 水平方向像素/米,分辨率 pInfoHead->biYPelsPerMeter = 0; // 垂直方向像素/米,分辨率 pInfoHead->biClrUsed = 0; // BMP图像使用的颜色,0:表示使用全部颜色 pInfoHead->biClrImportant = 0; // 重要的颜色数,0:所有的颜色都重要,当显卡不能够显示所有颜色时,辅助驱动程序显示颜色 // BMP 和 Mat 数据都是自左向右,但是BMP是自下而上,Mat是自上而下,故而在数据转换时需要颠倒数据上下位置 unsigned char* pBmpData = pBmp + pFileHead->bfOffBits + height * bmplineSize; // 最后一行尾地址 unsigned char* pMatData = image.data; // 第一行首地址 // 将Mat从上往下一行一行拷给BMP for (int i = 0; i < height; ++i) { // 每次拷贝一行 pBmpData -= bmplineSize; memcpy(pBmpData, pMatData, matlineSize); pMatData += matlineSize; } return 0; } //BMP文件模式 void ShowBMP(HWND hWnd, void* Data, int Len, int k) { unsigned char* pData; int DataLen; pData = (unsigned char*)Data; DataLen = Len; if (DataLen == 0) return; //if (DataLen > 10000) DataLen = 10000; BITMAPFILEHEADER hdr; LPBITMAPINFOHEADER lpbi; BITMAPINFOHEADER bmpInfo;//信息头 memcpy(&hdr, pData, 14); memcpy(&bmpInfo, pData + 14, 40); unsigned char* p = NULL; unsigned char* pBmpdata = NULL; p = (unsigned char*)pData + 14; pBmpdata = (unsigned char*)(pData + hdr.bfOffBits); lpbi = (LPBITMAPINFOHEADER)(p); RECT rt; GetClientRect(hWnd, &rt); HDC hdc = GetDC(hWnd); SetStretchBltMode(hdc, COLORONCOLOR); int x = 0; int y = 0; int width = (rt.right - rt.left) / 4; int hight = (rt.bottom - rt.top) / 3; switch (k) { case 0: x = width +20; y = 50; break; case 1: x = width+ width+20; y = 50; break; case 2: x = width + width+ width +20; y = 50; break; case 3: x = width + 20; y = hight + 60; break; case 4: x = width + width+ 20; y = hight + 60; break; case 5: x = width + width + width + 20; y = hight + 60; break; defalut: x = width + 20; y = 50; break; } StretchDIBits(hdc, x, y, (rt.right - rt.left) / 4, (rt.bottom - rt.top)/4, 0, 0, lpbi->biWidth, lpbi->biHeight, pBmpdata, (BITMAPINFO*)&bmpInfo, DIB_RGB_COLORS, SRCCOPY); //DIB_PAL_COLORS //DIB_RGB_COLORS } int OnTest1(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int i; if (0 == iStart) { iStart = 1; _beginthread(ThreadCapture, 0, NULL); } else { iStart = 0; } return 1; } int OnTest2(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { Mat imageSource = imread("E:\\XGZ\\Test\\veer-163341221.jpg"); // Read the file Mat image; GaussianBlur(imageSource, image, Size(3, 3), 0); Canny(imageSource, image, 100, 250); imshow("w1", image); //轮廓 namedWindow("w1", CV_WINDOW_AUTOSIZE); HWND hWndMat = (HWND)cvGetWindowHandle("w1"); HWND hWndFrameMat = GetParent(hWndMat); SetParent(hWndMat, hWnd); ShowWindow(hWndFrameMat, SW_HIDE); resizeWindow("w1", 200, 200); waitKey(0); // Wait for a keystroke in the window return 1; } int OnTest3(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { iGetPictureOK = 0; iGetPicture = 1; unsigned char* pBmp = NULL; unsigned long bmpsize; for (int i = 0; i < 100; i++) { if (1 == iGetPictureOK) { PRINT(_T("\r\n OK")); Mat image; image = mPicture.clone(); Mat2Bmp(&image, pBmp, bmpsize); ShowBMP(hWnd, pBmp, bmpsize, 0); free(pBmp); cvtColor(image, image, COLOR_BGR2GRAY); //转灰度 Mat2Bmp(&image, pBmp, bmpsize); ShowBMP(hWnd, pBmp, bmpsize, 1); free(pBmp); blur(image, image, Size(3, 3)); //平滑 // equalizeHist(image, image); //直方图均衡 // GaussianBlur(image, image, Size(3, 3), 0); Canny(image, image, 100, 250); Mat2Bmp(&image, pBmp, bmpsize); ShowBMP(hWnd, pBmp, bmpsize, 4); free(pBmp); pBmp = NULL; iGetPictureOK = 0; } Sleep(10); } return 1; } int OnTest4(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { return 1; } int OnTest5(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { return 1; } int OnTest6(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { return 1; } VOID ThreadCapture(PVOID pData) { PRINT(_T("\r\n ThreadCapture Begin..")); HWND hWnd = hWndMain; HDC hdc = GetDC(hWnd); SetStretchBltMode(hdc, COLORONCOLOR); VideoCapture capture(1); Mat frame; capture >> frame; int depth = frame.depth(); int channels = frame.channels(); int width = frame.cols; int height = frame.rows; // 获取图像每个像素的宽度 unsigned int pixelSize = (8 << (depth / 2)) * channels; // pixelSize >= 8 int colorTableSize = 0; //xgz 不用调色板 unsigned long bmplineSize = ((width * pixelSize + 31) >> 5) << 2; // xgz bfWidth *3+skip BMP行4字节对齐 unsigned long matlineSize = width * pixelSize / 8; // XGZ Mat line SIZE int skp = bmplineSize - matlineSize; // bmp图片的大小, sizeof(BITMAPFILEHEADER) = 14, sizeof(BITMAPINFOHEADER) = 40 int bmpsize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + colorTableSize + height * bmplineSize; int bmpdataoffset = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + colorTableSize; uchar* pBmp = (uchar*)malloc(bmpsize); if (!pBmp) { return; } memset(pBmp, 0, bmpsize); BITMAPINFOHEADER* pInfoHead = (BITMAPINFOHEADER*)(&pBmp[sizeof(BITMAPFILEHEADER)]); pInfoHead->biSize = 40; // 信息头的大小 pInfoHead->biWidth = width; pInfoHead->biHeight = height; pInfoHead->biPlanes = 1; // 图像平面数,必须是1 pInfoHead->biBitCount = pixelSize; // 图像每个像素所占的位数 pInfoHead->biCompression = 0; // 0:不压缩,1:REL8, 2:REL4 pInfoHead->biSizeImage = height * bmplineSize; // 图像数据大小 pInfoHead->biXPelsPerMeter = 0; // 水平方向像素/米,分辨率 pInfoHead->biYPelsPerMeter = 0; // 垂直方向像素/米,分辨率 pInfoHead->biClrUsed = 0; // BMP图像使用的颜色,0:表示使用全部颜色 pInfoHead->biClrImportant = 0; // 重要的颜色数,0:所有的颜色都重要,当显卡不能够显示所有颜色时,辅助驱动程序显示颜色 // BMP 和 Mat 数据都是自左向右,但是BMP是自下而上,Mat是自上而下,故而在数据转换时需要颠倒数据上下位置 unsigned char* pBmpDataCopy = pBmp + bmpdataoffset + height * bmplineSize; // 最后一行尾地址 unsigned char* pMatData = frame.data; // 第一行首地址 for (int i = 0; i < 10000; i++) { if (0 == iStart) break; capture >> frame; //capture 尺寸应该不会变 if (frame.empty()) break; if (iGetPicture == 1) { mPicture = frame.clone(); iGetPictureOK = 1; iGetPicture = 0; } pMatData = frame.data; pBmpDataCopy = pBmp + bmpdataoffset + height * bmplineSize; // 将Mat从上往下一行一行拷给BMP for (int i = 0; i < height; ++i) { pBmpDataCopy -= bmplineSize; memcpy(pBmpDataCopy, pMatData, matlineSize); pMatData += matlineSize; } unsigned char* pBmpdata = (unsigned char*)(pBmp + bmpdataoffset); RECT rt; GetClientRect(hWnd, &rt); StretchDIBits(hdc, rt.left+5, rt.top+50, (rt.right-rt.left)/4,(rt.bottom-rt.top)/3, 0, 0, pInfoHead->biWidth, pInfoHead->biHeight, pBmpdata, (BITMAPINFO*)pInfoHead, DIB_RGB_COLORS, SRCCOPY); //DIB_PAL_COLORS //DIB_RGB_COLORS Sleep(100); //10帧 每秒 } PRINT(_T("\r\n ThreadProc1 Exit.")); free(pBmp); _endthread(); }
标签:WPARAM,LPARAM,OpenCV,Windows,hWnd,image,Win32,int,HWND From: https://www.cnblogs.com/xgz21/p/16648543.html