首页 > 解决方案 > 在 OpenCV 中使用 Windows 剪贴板图像

问题描述

我正在尝试使用 win32api 将剪贴板位图数据转换为 opencv Mat 对象。

我尝试了以下方法:

#include <iostream>
#include <windows.h>

#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>

using namespace cv;

int main(int argc, char *argv[]) {

    OpenClipboard(NULL);

    HGLOBAL ClipboardDataHandle = (HGLOBAL)GetClipboardData(CF_DIBV5);
    if(ClipboardDataHandle){
        BITMAPINFOHEADER *BitmapInfoHeader = (BITMAPINFOHEADER *)GlobalLock(ClipboardDataHandle);
        if(BitmapInfoHeader){
            Mat mat(BitmapInfoHeader->biHeight, BitmapInfoHeader->biWidth, CV_8UC4);
            memcpy(mat.data, BitmapInfoHeader, GlobalSize(ClipboardDataHandle));
            cvtColor(mat, mat, COLOR_BGR2GRAY);
            threshold(mat, mat, 120, 255, THRESH_BINARY);
            imwrite("res.bmp", mat);
            GlobalUnlock(BitmapInfoHeader);
        }
    }
    
    CloseClipboard();
}

这解决了很多问题。

这在大多数情况下都适用于垂直翻转问题。

并且在复制包含透明像素信息的图像时,它以退出代码 0xc0000374(A 堆已损坏)中止。

我认为问题在于Mat的第三个参数固定为CV_8UC4。

如果将其保存到文件并再次读取,则没有问题:

#include <iostream>
#include <windows.h>

#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>

using namespace cv;

int main(int argc, char *argv[]) {

    OpenClipboard(NULL);

    HGLOBAL ClipboardDataHandle = (HGLOBAL)GetClipboardData(CF_DIBV5);
    if(ClipboardDataHandle){
        BITMAPINFOHEADER *BitmapInfoHeader = (BITMAPINFOHEADER *)GlobalLock(ClipboardDataHandle);
        if(BitmapInfoHeader){

            SIZE_T ClipboardDataSize = GlobalSize(ClipboardDataHandle);
            INT PixelDataOffset = BitmapInfoHeader->biSize;
            if (BitmapInfoHeader->biSize == sizeof(BITMAPINFOHEADER))
            {
                if (BitmapInfoHeader->biBitCount > 8)
                {
                    if (BitmapInfoHeader->biCompression == BI_BITFIELDS)
                    {
                        PixelDataOffset += 3 * sizeof(RGBQUAD);
                    }
                    else if (BitmapInfoHeader->biCompression == 6)
                    {
                        PixelDataOffset += 4 * sizeof(RGBQUAD);
                    }
                }
            }

            if (BitmapInfoHeader->biClrUsed > 0)
            {
                PixelDataOffset += BitmapInfoHeader->biClrUsed * sizeof(RGBQUAD);
            }
            else
            {
                if (BitmapInfoHeader->biBitCount <= 8)
                {
                    PixelDataOffset += sizeof(RGBQUAD) << BitmapInfoHeader->biBitCount;
                }
            }

            BITMAPFILEHEADER BitmapFileHeader = {};
            BitmapFileHeader.bfType = 0x4D42;
            BitmapFileHeader.bfSize = (DWORD)(sizeof(BITMAPFILEHEADER) + ClipboardDataSize);
            BitmapFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + PixelDataOffset;


            HANDLE FileHandle = CreateFileA("temp.bmp", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
            if (FileHandle != INVALID_HANDLE_VALUE)
            {
                DWORD dummy = 0;
                WriteFile(FileHandle, &BitmapFileHeader, sizeof(BITMAPFILEHEADER), &dummy, NULL);
                WriteFile(FileHandle, BitmapInfoHeader, (DWORD)ClipboardDataSize, &dummy, NULL);
                CloseHandle(FileHandle);
            }

            GlobalUnlock(BitmapInfoHeader);
        }
    }
    
    CloseClipboard();
    Mat mat = imread("temp.bmp");
    cvtColor(mat, mat, COLOR_BGR2GRAY);
    threshold(mat, mat, 120, 255, THRESH_BINARY);
    imwrite("res.bmp", mat);
}

这很好用。没有翻转问题,也没有错误。但我不想使用临时文件。请帮忙。

标签: opencvwinapi

解决方案


我还找不到解决方案。

只是我回答自己,因为经过大量的努力,我找到了一个临时的方法。

我需要很多反馈,因为对这段代码没有信心。

#include <iostream>
#include <windows.h>

#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>

using namespace cv;

int main(int argc, char *argv[]) {
    if(IsClipboardFormatAvailable(CF_DIBV5)){
        OpenClipboard(NULL);

        HGLOBAL clipboard = GetClipboardData(CF_DIBV5);

        if(clipboard){
            BITMAPV5HEADER* bitmapV5Header = (BITMAPV5HEADER*)GlobalLock(clipboard);

            if(bitmapV5Header){
                int type;
                switch(bitmapV5Header->bV5BitCount){
                    case 32:
                    {
                        type = CV_8UC4;
                        break;
                    }
                    case 24:
                    {
                        type = CV_8UC3;
                        break;
                    }
                    default:
                    {
                        type = -1;
                        std::cout << "no support" << std::endl;
                        break;
                    }
                }

                if(type != -1){
                    Mat tmp(bitmapV5Header->bV5Height, bitmapV5Header->bV5Width, type, (BYTE*)bitmapV5Header + (GlobalSize(clipboard) - bitmapV5Header->bV5SizeImage), bitmapV5Header->bV5SizeImage / bitmapV5Header->bV5Height); // data not copy
                    Mat mat;
                    flip(tmp, mat, 0); // flip with data copy
                    cvtColor(mat, mat, COLOR_BGR2GRAY); // just for test.
                    threshold(mat, mat, 80, 255, THRESH_BINARY);  // just for test.
                    imwrite("result.png", mat);
                }

                GlobalUnlock(clipboard);
            }

        }

        CloseClipboard();
    }
}

我认为“bV5BitCount”值不是 24 和 32 的情况会更多。

而且我仍然不知道垂直翻转问题的原因。但是由于我在使用 step 或 stride 时找不到复制数据的方法,因此使用 openCV,同时翻转和复制。


推荐阅读