QT调用大恒相机的开发教程

本文介绍了如何使用Qt5.12.10结合大恒USB3.0水星系列相机进行图像实时采集和显示,并以bmp格式保存图像。主要涉及环境搭建、库文件添加、相机初始化、图像处理和帧数控制等方面,通过自定义绘图类和事件处理回调函数实现了图像的实时显示和存储功能。
摘要由CSDN通过智能技术生成

Qt(5.12.10) 调用大恒相机,实时显示图像,并保存bmp文件

文章导读:相机为大恒相机,接口是USB3.0,水星系列产品。本项目采用Qt5.12.10版本编码,编译器为MSVC2017,opencv410版本对图像进行处理,最终完成了图像的实时采集图像显示,与图像存储为bmp格式且可控制存储帧数的功能。
【软件程序执行逻辑】:打开相机(初始化库-枚举列表-通过获取sn序列号的方式打开)—>开始采集(设置相机参数-实时显示帧数图像-保存到本地bmp文件)

一、环境搭建

步骤:
1.手动添加库文件;
2.pro中加入本项目需要使用到的功能声明

①添加相机使用的库

# Camera drive config
unix|win32: LIBS += -L$$PWD/build-FlawDetection_51210_MSVC2017_64-Release/CameraDrive/lib/x64/ -lGxIAPICPPEx

INCLUDEPATH += $$PWD/build-FlawDetection_51210_MSVC2017_64-Release/CameraDrive/lib/x64
DEPENDPATH += $$PWD/build-FlawDetection_51210_MSVC2017_64-Release/CameraDrive/lib/x64

DEPENDPATH += $$PWD/build-FlawDetection_51210_MSVC2017_64-Release/CameraDrive/inc
INCLUDEPATH += $$PWD/build-FlawDetection_51210_MSVC2017_64-Release/CameraDrive/inc
# Camera drive config

②添加opencv使用的库

# opencv config
win32:CONFIG(release, debug|release): LIBS += -L$$PWD/build-FlawDetection_51210_MSVC2017_64-Release/opencv/x64/vc15/lib/ -lopencv_world410
else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/build-FlawDetection_51210_MSVC2017_64-Release/opencv/x64/vc15/lib/ -lopencv_world410d
else:unix: LIBS += -L$$PWD/build-FlawDetection_51210_MSVC2017_64-Release/opencv/x64/vc15/lib/ -lopencv_world410

INCLUDEPATH += $$PWD/build-FlawDetection_51210_MSVC2017_64-Release/opencv/x64/vc15
INCLUDEPATH += $$PWD/build-FlawDetection_51210_MSVC2017_64-Release/opencv/opencv2
INCLUDEPATH += $$PWD/build-FlawDetection_51210_MSVC2017_64-Release/opencv

DEPENDPATH += $$PWD/build-FlawDetection_51210_MSVC2017_64-Release/opencv/x64/vc15
# opencv config

②本项目需要添加到pro里面的

QT       += core gui sql charts printsupport

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = RailMeasurement
CONFIG += c++11
LIBS += User32.LIB

# utf-8 字体设置,防止编译报错
msvc {
    QMAKE_CFLAGS += /utf-8
    QMAKE_CXXFLAGS += /utf-8
}
# utf-8 字体设置,防止编译报错

二、代码展示

在这里插入图片描述
camerawidget.h : 相机操作类,需要包含ui文件
在这里插入图片描述

cgxbitmap.h : 图像处理类
drawwidget.h :控件绘图类,这里采用的是自己定义的类,将控件widget提升为drawwidget类,来绘图,原因是可对图像进行细化操作,例如放大缩小及一些自定义处理。
camerawidget.h ↓

#ifndef CAMERAWIDGET_H
#define CAMERAWIDGET_H

#include "GalaxyIncludes.h"
#include "Log/logReport.h"
#include "cgxbitmap.h"
#include "drawwidget.h"

#include <QWidget>
#include <QLabel>
#include <QPixmap>
#include <QtGlobal>
#include <iostream>
#include <QMessageBox>
#include <opencv2/opencv.hpp>

namespace Ui {
class CameraWidget;
}

class CameraWidget : public QWidget
{
    Q_OBJECT

public:
    explicit CameraWidget(QWidget *parent = nullptr);
    ~CameraWidget(void);

public:
    class CSampleCaptureEventHandler : public ICaptureEventHandler
    {
    public:
        void DoOnImageCaptured(CImageDataPointer& objImageDataPointer, void* pUserParam)
        {
            if (objImageDataPointer->GetStatus() == GX_FRAME_STATUS_SUCCESS)
            {
                CameraWidget*  cameraShow = (CameraWidget*)pUserParam;
                //cameraShow->ShowImageProcess(objImageDataPointer);
                void* pRaw8Buffer = NULL;
                pRaw8Buffer = objImageDataPointer->ConvertToRaw8(GX_BIT_0_7);
                std::memcpy(cameraShow->mCurImgMat.data, pRaw8Buffer, (objImageDataPointer->GetHeight())*(objImageDataPointer->GetWidth()));
                //cv::flip(cameraShow->mCurImgMat, cameraShow->mCurImgMat, 0);//大恒的图像要进行翻转,但是此处似乎不需要翻转
                //调用自定义绘图函数进行绘制实时采集图像
                cameraShow->showCurImgInLabel(cameraShow->mImgShow, cameraShow->mCurImgMat);

                if (cameraShow->mFps < 10) {//存储帧数,可修改为通过传参方式设置需要保存的帧数,目前默认设置10帧(10张图片)
                    std::string PathAndName = cameraShow->mFilePath.toUtf8().data() + std::to_string(cameraShow->mFps) + ".bmp";
                    cameraShow->m_pBitmap->SaveBmp(objImageDataPointer, PathAndName);//保存单帧图像
                    cameraShow->mFps ++;
                }
            }
        }
    };

    class CSampleDeviceOfflineEventHandler : public IDeviceOfflineEventHandler
    {
    public:
        void DoOnDeviceOfflineEvent(void* pUserParam)
        {
            QMessageBox::warning(0, "警告", "设备相机已掉线!");
            writeLog("CSampleDeviceOfflineEventHandler", "设备相机已掉线!", TYPE_INFOR);
        }
    };

public:
    //绘制实时采集图像成比例
    void    showCurImgInLabel(QLabel* ptrLabelToShow, cv::Mat& CVMat);
    QImage  cvMatToQImage(const cv::Mat &inMat);  //cvMat转换为QImage
    QPixmap cvMatToQPixmap(const cv::Mat &inMat); //cvMat转换为Qpixmap

public slots:
    int  openCamera(void);  //打开相机
    void closeCamera(void); //关闭相机
    void startCollection(void); //开始采集
    void stopCollection(void);  //停止采集

public:
    //-----相机参数
    int64_t mWidth;                //图像宽度
    int64_t mHeight;               //图像高度
    QString mBitDepth;             //像素深度(Bpp8)
    QString mCamera, mCameraNumber;//相机名称, 型号
    QString mSerialNumName;        //序列号
    QString mIpName;               //ip地址
    QString mMaskName;             //掩码
    QString mMacName;              //MAC地址
    QString mAcquisitionMode;      //采集模式(单拍: SingleFrame, 连拍: Continuous)
    QString mTriggerMode = "off";  //触发模式(on:软件触发)
    QString mTriggerSource;        //触发源

    bool mIsOffline = false;       //设备是否掉线
    bool mIsOpenCam = false;       //相机是否打开
    bool mIsOpenStream = false;    //相机流是否打开
    bool mIsTrigger = false;       //相机是否触发
    float mExposeTime = 0;         //曝光时间

    CGXDeviceInfo mDeviceInfo;            //设备信息
    CGXDevicePointer mObjDevicePtr;       //当前界面操作的相机指针
    CGXStreamPointer mObjStreamPtr;       //流采集指针, 采集控制和采集数据统计的属性访问控制器
    GX_DEVICE_CLASS_LIST mObjDeviceClass; //设备类型
    CImageProcessConfigPointer mObjImageProcessPtr;             //图像处理对象
    CGXFeatureControlPointer mObjRemoteFeatureControlPtr;       //远端设备控制器
    CGXFeatureControlPointer mObjFeatureControlPtr;             //本地和流属性控制器
    ICaptureEventHandler* pCaptureEventHandler;                 //注册回调事件指针
    IDeviceOfflineEventHandler* pDeviceOfflineEventHandler = nullptr;     //注册掉线事件
    GX_DEVICE_OFFLINE_CALLBACK_HANDLE mDeviceOffline = nullptr;    //掉线事件
    //-----相机参数

    //图像参数及保存(bmp格式)
    int mFps = 0;
    DrawWidget* mDrawWidget = nullptr;//提升窗口指针,使用drawqimage显示实时图像
    QLabel *mImgShow = nullptr;
    cv::Mat mCurImgMat;     //实时窗口的Mat
    CGXBitmap*  m_pBitmap = nullptr;  //保存图像指针
    QString mFilePath;
    //图像参数及保存(bmp格式)

private:
    Ui::CameraWidget *ui;
};

#endif // CAMERAWIDGET_H

camerawidget.cpp↓

#include "camerawidget.h"
#include "ui_camerawidget.h"

#include <QDebug>
#include <QMessageBox>

CameraWidget::CameraWidget(QWidget *parent) :
    QWidget(parent), ui(new Ui::CameraWidget)
{
    ui->setupUi(this);
    setAttribute(Qt::WA_TranslucentBackground, true);
    setWindowFlags(Qt::FramelessWindowHint);//设置无边窗体

    //设置提升后的控件属性
    mImgShow = ui->label_imageShow;
    mDrawWidget = ui->widget;
    mDrawWidget->setSize();
    //设置提升后的控件属性

    mFilePath = MakeMyDirectory();//创建文件夹
    mFilePath = QDir::toNativeSeparators(mFilePath) + "\\";//将正'/' 转为'\\',因为接口函数调用保存图像路径是避免找不到
}

CameraWidget::~CameraWidget(void)
{
    delete ui;
}

int CameraWidget::openCamera(void)
{
    try {
        if (!mIsOpenCam) {
            IGXFactory::GetInstance().Init();//初始化库

            //枚举设备列表
            GxIAPICPP::gxdeviceinfo_vector vectorDeviceInfo;
            IGXFactory::GetInstance().UpdateDeviceList(1000, vectorDeviceInfo);
            if (vectorDeviceInfo.size() == 0) {
                writeLog("openCamera", "无可用设备,请检查相机连接状态!", TYPE_ERROR);
                return -1;
            }
            //枚举设备列表

            //打开第一台设备
            GxIAPICPP::gxstring strSN = vectorDeviceInfo[0].GetSN();
            mObjDevicePtr = IGXFactory::GetInstance().OpenDeviceBySN(strSN, GX_ACCESS_EXCLUSIVE);
            //打开第一台设备

            //相机控制对象赋值
            mObjRemoteFeatureControlPtr = mObjDevicePtr->GetRemoteFeatureControl();//包含主要设备信息,比如宽高、曝光增益等
            mObjFeatureControlPtr = mObjDevicePtr->GetFeatureControl();//包含一些本地属性,不同类型的设备具备的功能也不一样。
            //相机控制对象赋值

            //设备信息获取
            mDeviceInfo = mObjDevicePtr->GetDeviceInfo();
            //设备信息获取

            //设备具体值获取
            mCamera = mDeviceInfo.GetVendorName();          //获取相机名称
            mCameraNumber = mDeviceInfo.GetModelName();     //获取相机型号
            mSerialNumName = mDeviceInfo.GetSN();           //获取相机序列号
            mIpName = mDeviceInfo.GetIP();                  //获取相机IP
            mMaskName = mDeviceInfo.GetSubnetMask();        //获取相机掩码
            mMacName = mDeviceInfo.GetMAC();                //获取相机MAC地址

            mWidth = mObjRemoteFeatureControlPtr->GetIntFeature("AAROIWidth")->GetValue();   //获取图像的宽
            mHeight = mObjRemoteFeatureControlPtr->GetIntFeature("AAROIHeight")->GetValue(); //获取图像的宽
            mBitDepth = mObjRemoteFeatureControlPtr->GetEnumFeature("PixelSize")->GetValue();
            //设备具体值获取

            //为Mat矩阵开辟空间
            mCurImgMat.create(mHeight, mWidth, CV_8UC1);
            //为Mat矩阵开辟空间

            //判断图像对象是否为空
            if (m_pBitmap != nullptr) {
                delete m_pBitmap;
                m_pBitmap = nullptr;
            }
            //判断图像对象是否为空

            //为画图对象分配内存
            m_pBitmap = new CGXBitmap(mObjDevicePtr);
            //为画图对象分配内存

            mIsOpenCam = true;
            writeLog("openCamera", "open camera success", TYPE_KEY);
            return 1;
//            //软件默认设置曝光时间
//            if (mExposeTime > 0 && mExposeTime < 200000)
//                mObjRemoteFeatureControlPtr->GetFloatFeature("ExposureTime")->SetValue(mExposeTime);
//            else
//                mExposeTime = 30000;
//            //软件默认设置曝光时间

//            //采集模式:(单拍:, 连拍:Continuous)
//            mObjRemoteFeatureControlPtr->GetEnumFeature("AcquisitionMode")->SetValue("Continuous");
//            //触发模式
//            mObjRemoteFeatureControlPtr->GetEnumFeature("TriggerSelector")->SetValue("FrameStart");
//            mObjRemoteFeatureControlPtr->GetEnumFeature("TriggerMode")->SetValue("Off");
//            //自动曝光
//            mObjRemoteFeatureControlPtr->GetEnumFeature("ExposureAuto")->SetValue("Off");
//            mCurImgMat.create(mHeight, mWidth, CV_8UC1);//为Mat矩阵开辟空间
        }
    }  catch (CGalaxyException& e) {
        writeLog("openCamera", QString("错误码:%1, 错误描述信息:%2").arg(e.GetErrorCode()).arg(e.what()), TYPE_ERROR);
    }

    writeLog("openCamera", "open camera faild, The camera is on", TYPE_KEY);
    return -2;
}

void CameraWidget::closeCamera(void)
{
    if (mIsOpenCam) {
        if (mIsOpenStream) {
            //发送停采命令
            mObjRemoteFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute();
            mObjStreamPtr->StopGrab();

            mObjStreamPtr->UnregisterCaptureCallback();//注销采集回调
            mObjStreamPtr->Close(); //关闭相机流
            mIsOpenStream = false;
            writeLog("openCamera", "close camera stream", TYPE_INFOR);
        }
        if (mObjDeviceClass == GX_DEVICE_CLASS_GEV) {
            //注销掉线回调事件
            mObjDevicePtr->UnregisterDeviceOfflineCallback(mDeviceOffline);
            delete pDeviceOfflineEventHandler;
            pDeviceOfflineEventHandler = nullptr;
        }

        mObjDevicePtr->Close();
        IGXFactory::GetInstance().Uninit(); //释放GxIAPICPPEx申请的所有资源
        mIsOpenCam = false;
        writeLog("openCamera", "close camera", TYPE_KEY);
    }   
}

void CameraWidget::startCollection(void)
{
    try {
        if (mIsOpenCam) {
            //千兆网系列相机设置
            mObjDeviceClass = mObjDevicePtr->GetDeviceInfo().GetDeviceClass();
            if (mObjDeviceClass == GX_DEVICE_CLASS_GEV) {
                //提高网络相机的采集性
                if (mObjFeatureControlPtr->IsImplemented("GevSCPSPacketSize")) {//判断设备是否支持流通道数据包功能
                    //获取当前网络环境的最优包长值
                    int nPacketSize = mObjStreamPtr->GetOptimalPacketSize();
                    //将最优包长值设置为当前设备的流通道包长值
                    mObjRemoteFeatureControlPtr->GetIntFeature("GevSCPSPacketSize")->SetValue(nPacketSize);
                }
            }

            //注册掉线事件
            pDeviceOfflineEventHandler = new CSampleDeviceOfflineEventHandler();
            mDeviceOffline = mObjDevicePtr->RegisterDeviceOfflineCallback(pDeviceOfflineEventHandler, this);
            //注册掉线事件

            //千兆网系列相机设置
            uint32_t nStreamNum = mObjDevicePtr->GetStreamCount();
            if ((nStreamNum > 0) && (!mIsOpenStream)) {
                mObjStreamPtr = mObjDevicePtr->OpenStream(0);
                mIsOpenStream = true;
            }

            //注册采集回调函数
            pCaptureEventHandler = new CSampleCaptureEventHandler();
            mObjStreamPtr->RegisterCaptureCallback(pCaptureEventHandler, this);
            //注册采集回调函数

            //给设备发送开采命令
            mObjStreamPtr->StartGrab();
            mObjRemoteFeatureControlPtr->GetCommandFeature("AcquisitionStart")->Execute();
            //给设备发送开采命令

            writeLog("startCollection", "start collection...", TYPE_KEY);
        }
    }  catch (CGalaxyException& e) {
        writeLog("startCollection", QString("错误码:%1, 错误描述信息:%2").arg(e.GetErrorCode()).arg(e.what()), TYPE_ERROR);
    }
}

void CameraWidget::stopCollection(void)
{
    if (mIsOpenStream) {
        //发送停采命令
        mObjRemoteFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute();
        mObjStreamPtr->StopGrab();
        mIsOpenStream = false;
        writeLog("stopCollection", "stop collection...", TYPE_KEY);
    }
}

void CameraWidget::showCurImgInLabel(QLabel *ptrLabelToShow, cv::Mat &CVMat)
{
    //获取要显示图片的label的大小
    QSize LabelSize = ptrLabelToShow->size();
    QImage QSrcImg = cvMatToQImage(CVMat);//获取一个QImage
    QImage QSrcImgRatio = QSrcImg.scaled(LabelSize, Qt::IgnoreAspectRatio);//重新调整图像大小以适应窗口
    ptrLabelToShow->setPixmap(QPixmap::fromImage(QSrcImgRatio));//显示
}

QImage CameraWidget::cvMatToQImage(const cv::Mat &inMat)
{
    switch (inMat.type()) {
       // 8-bit, 4 channel
    case CV_8UC4:
    {
          QImage image( inMat.data, inMat.cols, inMat.rows, static_cast<int>(inMat.step), QImage::Format_ARGB32);
          return image;
    }

       // 8-bit, 3 channel
    case CV_8UC3:
    {
          QImage image(inMat.data, inMat.cols, inMat.rows, static_cast<int>(inMat.step), QImage::Format_RGB888);
          return image.rgbSwapped();
    }
       // 8-bit, 1 channel
    case CV_8UC1:
    {
#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
          QImage image(inMat.data, inMat.cols, inMat.rows, static_cast<int>(inMat.step), QImage::Format_Grayscale8);
#else
          static QVector<QRgb>  sColorTable;

          // only create our color table the first time
          if (sColorTable.isEmpty()) {
             sColorTable.resize(256);

             for (int i = 0; i < 256; ++i) {
                sColorTable[i] = qRgb(i, i, i);
             }
          }

          QImage image(inMat.data, inMat.cols, inMat.rows, static_cast<int>(inMat.step), QImage::Format_Indexed8 );
          image.setColorTable(sColorTable);
#endif
          return image;
       }

       default:
          //qWarning() << "ASM::cvMatToQImage() - cv::Mat image type not handled in switch:" << inMat.type();
          break;
    }

    return QImage();
}

QPixmap CameraWidget::cvMatToQPixmap(const cv::Mat &inMat)
{
    return QPixmap::fromImage(CameraWidget::cvMatToQImage(inMat));
}

cgxbitmap.h↓

#ifndef CGXBITMAP_H
#define CGXBITMAP_H

//------------------------------------------------------------------------
/*
\file		GXBitmap.h
\brief		此类主要用于图像的显示和存储,图像显示和存储可以自适应黑白彩色相机,
图像存储可以存储为Bmp、Raw,对图像显示和存储进行了声明
*/
//------------------------------------------------------------------------
#include "GalaxyIncludes.h"
#include "Log/logReport.h"

#include <string>
#include <Windows.h>
#include <QDir>//文件夹需要包含的头文件
#include <QFile> //文件需要包含的头文件
#include <QCoreApplication> //用于获取运行路径需要包含的头文件

class CGXBitmap
{
public:
    CGXBitmap(CGXDevicePointer& objCGXDevicePointer);
    ~CGXBitmap(void);

    void Show(CImageDataPointer& objCImageDataPointer);//显示图像
    void ShowImageProcess(CImageProcessConfigPointer& objCfg,CImageDataPointer& objCImageDataPointer);//图像处理后并显示图像
    void SaveBmp(CImageDataPointer& objCImageDataPointer,const std::string& strFilePath);//存储Bmp图像
    void SaveRaw(CImageDataPointer& objCImageDataPointer,const std::string& strFilePath);//存储Raw图像
    GX_VALID_BIT_LIST GetBestValudBit(GX_PIXEL_FORMAT_ENTRY emPixelFormatEntry);//通过GX_PIXEL_FORMAT_ENTRY获取最优Bit位

private:
    bool __IsPixelFormat8(GX_PIXEL_FORMAT_ENTRY emPixelFormatEntry);//判断PixelFormat是否为8位
    void __ColorPrepareForShowImg(); //为彩色相机图像显示准备资源
    void __MonoPrepareForShowImg();  //为黑白相机图像显示准备资源
    bool __IsCompatible(BITMAPINFO *pBmpInfo, uint64_t nWidth, uint64_t nHeight);//判断是否兼容
    void __UpdateBitmap(CImageDataPointer& objCImageDataPointer);//更新Bitmap的信息
    int64_t __GetStride(int64_t nWidth, bool bIsColor);//计算宽度所占的字节数

private:
    bool    m_bIsColor  = false;  //是否支持彩色相机
    int64_t m_nImageWidth  = 0;   //原始图像宽
    int64_t m_nImageHeight = 0;   //原始图像高

    char m_chBmpBuf[2048];             //BIMTAPINFO 存储缓冲区,m_pBmpInfo即指向此缓冲区
    BYTE *m_pImageBuffer = nullptr;    //保存翻转后的图像用于显示
    BITMAPINFO *m_pBmpInfo = nullptr;  //BITMAPINFO 结构指针,显示图像时使用

private:
    CGXBitmap& operator = (const CGXBitmap&);
    CGXBitmap(const CGXBitmap&);
};

//全局函数
std::wstring StringToWString( const std::string &s);//将string转换为wstring类,便于利用大恒接口写文件
QString MakeMyDirectory();//创建文件夹Image
//全局函数

#endif // CGXBITMAP_H

cgxbitmap.cpp↓

#include "cgxbitmap.h"
#include <cstring>

//------------------------------------------------------------------------
/*
\file		GXBitmap.cpp
\brief		此类主要用于图像的显示和存储,图像显示和存储可以自适应黑白彩色相机,
图像存储可以存储为Bmp、Raw,对图像显示和存储进行了实现
*/
//------------------------------------------------------------------------
CGXBitmap::CGXBitmap(CGXDevicePointer& objCGXDevicePointer)
{
    if (objCGXDevicePointer.IsNull()) {
        writeLog("CGXBitmap", "Argument is error", TYPE_ERROR);
    }

    memset(m_chBmpBuf, 0, sizeof(m_chBmpBuf));
    gxstring strValue = "";

    //获得图像宽度、高度等
    m_nImageWidth = (int64_t)objCGXDevicePointer->GetRemoteFeatureControl()->GetIntFeature("Width")->GetValue();
    m_nImageHeight = (int64_t)objCGXDevicePointer->GetRemoteFeatureControl()->GetIntFeature("Height")->GetValue();

    //获取是否为彩色相机
    if (objCGXDevicePointer->GetRemoteFeatureControl()->IsImplemented("PixelColorFilter")) {
        strValue = objCGXDevicePointer->GetRemoteFeatureControl()->GetEnumFeature("PixelColorFilter")->GetValue();

        if (strValue != "None") {
            m_bIsColor = true;
        }
    }

    if (m_bIsColor) {
        __ColorPrepareForShowImg();
    }
    else {
        __MonoPrepareForShowImg();
    }
}

CGXBitmap::~CGXBitmap(void)
{
    //释放pDC
    //::ReleaseDC(m_pWnd->m_hWnd, m_hDC);

    if (m_pImageBuffer != NULL) {
        delete m_pImageBuffer;
        m_pImageBuffer = NULL;
    }
}

//----------------------------------------------------------------------------------
/*
\brief     判断PixelFormat是否为8位
\param     emPixelFormatEntry 图像数据格式
\return    true为8为数据,false为非8位数据
*/
//----------------------------------------------------------------------------------
bool CGXBitmap::__IsPixelFormat8(GX_PIXEL_FORMAT_ENTRY emPixelFormatEntry)
{
    bool bIsPixelFormat8 = false;
    const unsigned  PIXEL_FORMATE_BIT = 0x00FF0000;  //用于与当前的数据格式进行与运算得到当前的数据位数
    unsigned uiPixelFormatEntry = (unsigned)emPixelFormatEntry;
    if ((uiPixelFormatEntry & PIXEL_FORMATE_BIT) == GX_PIXEL_8BIT) {
        bIsPixelFormat8 = true;
    }
    return bIsPixelFormat8;
}


//----------------------------------------------------------------------------------
/*
\brief     通过GX_PIXEL_FORMAT_ENTRY获取最优Bit位
\param     emPixelFormatEntry 图像数据格式
\return    最优Bit位
*/
//----------------------------------------------------------------------------------
GX_VALID_BIT_LIST CGXBitmap::GetBestValudBit(GX_PIXEL_FORMAT_ENTRY emPixelFormatEntry)
{
    GX_VALID_BIT_LIST emValidBits = GX_BIT_0_7;
    switch (emPixelFormatEntry) {
    case GX_PIXEL_FORMAT_MONO8:
    case GX_PIXEL_FORMAT_BAYER_GR8:
    case GX_PIXEL_FORMAT_BAYER_RG8:
    case GX_PIXEL_FORMAT_BAYER_GB8:
    case GX_PIXEL_FORMAT_BAYER_BG8:
    {
        emValidBits = GX_BIT_0_7;
        break;
    }
    case GX_PIXEL_FORMAT_MONO10:
    case GX_PIXEL_FORMAT_BAYER_GR10:
    case GX_PIXEL_FORMAT_BAYER_RG10:
    case GX_PIXEL_FORMAT_BAYER_GB10:
    case GX_PIXEL_FORMAT_BAYER_BG10:
    {
        emValidBits = GX_BIT_2_9;
        break;
    }
    case GX_PIXEL_FORMAT_MONO12:
    case GX_PIXEL_FORMAT_BAYER_GR12:
    case GX_PIXEL_FORMAT_BAYER_RG12:
    case GX_PIXEL_FORMAT_BAYER_GB12:
    case GX_PIXEL_FORMAT_BAYER_BG12:
    {
        emValidBits = GX_BIT_4_11;
        break;
    }
    case GX_PIXEL_FORMAT_MONO14:
    {
        //暂时没有这样的数据格式待升级
        break;
    }
    case GX_PIXEL_FORMAT_MONO16:
    case GX_PIXEL_FORMAT_BAYER_GR16:
    case GX_PIXEL_FORMAT_BAYER_RG16:
    case GX_PIXEL_FORMAT_BAYER_GB16:
    case GX_PIXEL_FORMAT_BAYER_BG16:
    {
        //暂时没有这样的数据格式待升级
        break;
    }
    default:
        break;
    }
    return emValidBits;
}

//---------------------------------------------------------------------------------
/*
\brief   为彩色相机图像显示准备资源

\return  无
*/
//----------------------------------------------------------------------------------
void CGXBitmap::__ColorPrepareForShowImg()
{
    //--------------------------------------------------------------------
    //---------------------------初始化bitmap头---------------------------
    m_pBmpInfo								= (BITMAPINFO *)m_chBmpBuf;
    m_pBmpInfo->bmiHeader.biSize			= sizeof(BITMAPINFOHEADER);
    m_pBmpInfo->bmiHeader.biWidth			= (LONG)m_nImageWidth;
    m_pBmpInfo->bmiHeader.biHeight			= (LONG)m_nImageHeight;

    m_pBmpInfo->bmiHeader.biPlanes			= 1;
    m_pBmpInfo->bmiHeader.biBitCount        = 24;
    m_pBmpInfo->bmiHeader.biCompression		= BI_RGB;
    m_pBmpInfo->bmiHeader.biSizeImage		= 0;
    m_pBmpInfo->bmiHeader.biXPelsPerMeter	= 0;
    m_pBmpInfo->bmiHeader.biYPelsPerMeter	= 0;
    m_pBmpInfo->bmiHeader.biClrUsed			= 0;
    m_pBmpInfo->bmiHeader.biClrImportant	= 0;
}

//---------------------------------------------------------------------------------
/*
\brief   为黑白相机图像显示准备资源

\return  无
*/
//----------------------------------------------------------------------------------
void CGXBitmap::__MonoPrepareForShowImg()
{
    //---------------------------------------------------------------------
    //----------------------初始化bitmap头---------------------------------
    m_pBmpInfo								= (BITMAPINFO *)m_chBmpBuf;
    m_pBmpInfo->bmiHeader.biSize			= sizeof(BITMAPINFOHEADER);
    m_pBmpInfo->bmiHeader.biWidth			= (LONG)m_nImageWidth;
    m_pBmpInfo->bmiHeader.biHeight			= (LONG)m_nImageHeight;

    m_pBmpInfo->bmiHeader.biPlanes			= 1;
    m_pBmpInfo->bmiHeader.biBitCount		= 8; // 黑白图像为8
    m_pBmpInfo->bmiHeader.biCompression		= BI_RGB;
    m_pBmpInfo->bmiHeader.biSizeImage		= 0;
    m_pBmpInfo->bmiHeader.biXPelsPerMeter	= 0;
    m_pBmpInfo->bmiHeader.biYPelsPerMeter	= 0;
    m_pBmpInfo->bmiHeader.biClrUsed			= 0;
    m_pBmpInfo->bmiHeader.biClrImportant	= 0;

    // 黑白图像需要初始化调色板
    for(int i=0;i<256;i++)
    {
        m_pBmpInfo->bmiColors[i].rgbBlue	=i;
        m_pBmpInfo->bmiColors[i].rgbGreen	=i;
        m_pBmpInfo->bmiColors[i].rgbRed		=i;
        m_pBmpInfo->bmiColors[i].rgbReserved=i;
    }

    //为经过翻转后的图像数据分配空间
    if (m_pImageBuffer != NULL)
    {
        delete m_pImageBuffer;
        m_pImageBuffer = NULL;
    }

    m_pImageBuffer = new BYTE[(size_t)(m_nImageWidth * m_nImageHeight)];
    if (m_pImageBuffer == NULL)
    {
        throw std::runtime_error("Fail to allocate memory");
    }
}

//----------------------------------------------------------------------------------
/*
\brief     判断是否兼容
\param     pBmpInfo BITMAPINFO指针
\param     nWidth 图像宽
\param     nHeight 图像高
\return    true为一样,false不一样
*/
//----------------------------------------------------------------------------------
bool CGXBitmap::__IsCompatible(BITMAPINFO *pBmpInfo, uint64_t nWidth, uint64_t nHeight)
{
    if (pBmpInfo == NULL || pBmpInfo->bmiHeader.biHeight != nHeight || pBmpInfo->bmiHeader.biWidth != nWidth) {
        return false;
    }
    return true;
}

//----------------------------------------------------------------------------------
/*
\brief     检查图像是否改变并更新Buffer并为图像显示准备资源
\param     objCImageDataPointer  图像数据对象
\return    无
*/
//----------------------------------------------------------------------------------
void CGXBitmap::__UpdateBitmap(CImageDataPointer& objCImageDataPointer)
{
    if (!__IsCompatible(m_pBmpInfo, objCImageDataPointer->GetWidth(), objCImageDataPointer->GetHeight())) {
        m_nImageWidth = objCImageDataPointer->GetWidth();
        m_nImageHeight = objCImageDataPointer->GetHeight();
        if (m_bIsColor) {
            __ColorPrepareForShowImg();
        } else {
            __MonoPrepareForShowImg();
        }
    }
}

//----------------------------------------------------------------------------------
/*
\brief     计算宽度所占的字节数
\param     nWidth  图像宽度
\param     bIsColor  是否是彩色相机
\return    图像一行所占的字节数
*/
//----------------------------------------------------------------------------------
int64_t CGXBitmap::__GetStride(int64_t nWidth, bool bIsColor)
{
    return bIsColor ? nWidth * 3 : nWidth;
}

//----------------------------------------------------------------------------------
/*
\brief     用于显示图像
\param     objCImageDataPointer  图像数据对象
\return    无
*/
//----------------------------------------------------------------------------------
void CGXBitmap::Show(CImageDataPointer& objCImageDataPointer)
{
    GX_VALID_BIT_LIST emValidBits = GX_BIT_0_7;
    BYTE* pBuffer = NULL;

    if (objCImageDataPointer.IsNull()){
        writeLog("Show", "NULL pointer dereferenced", TYPE_ERROR);
        return;
    }

    //检查图像是否改变并更新Buffer
    __UpdateBitmap(objCImageDataPointer);

    emValidBits = GetBestValudBit(objCImageDataPointer->GetPixelFormat());
    if (m_bIsColor) {
        pBuffer = (BYTE*)objCImageDataPointer->ConvertToRGB24(emValidBits, GX_RAW2RGB_NEIGHBOUR, true);
    } else {
        if (__IsPixelFormat8(objCImageDataPointer->GetPixelFormat())) {
            pBuffer = (BYTE*)objCImageDataPointer->GetBuffer();
        } else {
            pBuffer = (BYTE*)objCImageDataPointer->ConvertToRaw8(emValidBits);
        }

        // 黑白相机需要翻转数据后显示
        for (int i = 0; i < m_nImageHeight; i++) {
            memcpy(m_pImageBuffer+i*m_nImageWidth, pBuffer+(m_nImageHeight-i-1)*m_nImageWidth, (size_t)m_nImageWidth);
        }
    }
}

//----------------------------------------------------------------------------------
/*
\brief     用于图像处理后并显示图像
\param     objCfg  图像处理调节参数对象
\param     objCImageDataPointer  图像数据对象
\return    无
*/
//----------------------------------------------------------------------------------
void CGXBitmap::ShowImageProcess(CImageProcessConfigPointer& objCfg,CImageDataPointer& objCImageDataPointer)
{
    if ((objCfg.IsNull()) || (objCImageDataPointer.IsNull())) {
        writeLog("ShowImageProcess", "NULL pointer dereferenced", TYPE_ERROR);
        return;
    }

    //检查图像是否改变并更新Buffer
    __UpdateBitmap(objCImageDataPointer);

    BYTE* pBuffer = (BYTE*)objCImageDataPointer->ImageProcess(objCfg);

    if (!m_bIsColor) {
        // 黑白相机需要翻转数据后显示
        for (int i = 0; i < m_nImageHeight; i++) {
            memcpy(m_pImageBuffer + i * m_nImageWidth, pBuffer + (m_nImageHeight - i -1) * m_nImageWidth, (size_t)m_nImageWidth);
        }
    }
}

//----------------------------------------------------------------------------------
/*
\brief     存储Bmp图像
\param     objCImageDataPointer  图像数据对象
\param     strFilePath  显示图像文件名
\return    无
*/
//----------------------------------------------------------------------------------
void CGXBitmap::SaveBmp(CImageDataPointer& objCImageDataPointer,const std::string& strFilePath)
{
    GX_VALID_BIT_LIST emValidBits = GX_BIT_0_7;
    BYTE* pBuffer = NULL;

    if ((objCImageDataPointer.IsNull()) || (strFilePath == "")) {
        writeLog("SaveBmp", "Argument is error", TYPE_ERROR);
        return;
    }

    //检查图像是否改变并更新Buffer
    __UpdateBitmap(objCImageDataPointer);

    emValidBits = GetBestValudBit(objCImageDataPointer->GetPixelFormat());

    if (m_bIsColor) {
        pBuffer = (BYTE*)objCImageDataPointer->ConvertToRGB24(emValidBits, GX_RAW2RGB_NEIGHBOUR, true);
    } else {
        if (__IsPixelFormat8(objCImageDataPointer->GetPixelFormat())) {
            pBuffer = (BYTE*)objCImageDataPointer->GetBuffer();
        } else {
            pBuffer = (BYTE*)objCImageDataPointer->ConvertToRaw8(emValidBits);
        }
        // 黑白相机需要翻转数据后显示
        for(int i = 0;i < m_nImageHeight; i++) {
            memcpy(m_pImageBuffer + i * m_nImageWidth, pBuffer + (m_nImageHeight - i -1) * m_nImageWidth, (size_t)m_nImageWidth);
        }
        pBuffer = m_pImageBuffer;
    }

    DWORD		         dwImageSize = (DWORD)(__GetStride(m_nImageWidth,m_bIsColor) * m_nImageHeight);
    BITMAPFILEHEADER     stBfh	     = {0};
    DWORD		         dwBytesRead = 0;

    stBfh.bfType	= (WORD)'M' << 8 | 'B';			 //定义文件类型
    stBfh.bfOffBits = m_bIsColor ?sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)
        :sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (256 * 4);	//定义文件头大小true为彩色,false为黑白
    stBfh.bfSize	= stBfh.bfOffBits + dwImageSize; //文件大小

    DWORD dwBitmapInfoHeader = m_bIsColor ?sizeof(BITMAPINFOHEADER)
        :sizeof(BITMAPINFOHEADER) + (256 * 4);	//定义BitmapInfoHeader大小true为彩色,false为黑白

    //创建文件
    std::wstring s = StringToWString(strFilePath);
    LPCTSTR LPCSTR_ImgFileName = s.c_str();//使用了我自定义的一个转换函数,并且连续调用了.c_str(),并对下面CreateFile进行了修改
    HANDLE hFile = ::CreateFile(LPCSTR_ImgFileName,
        GENERIC_WRITE,
        0,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL);

    if (hFile == INVALID_HANDLE_VALUE) {
        writeLog("SaveBmp", "Handle is invalid", TYPE_ERROR);
        return;
    }

    ::WriteFile(hFile, &stBfh, sizeof(BITMAPFILEHEADER), &dwBytesRead, NULL);
    ::WriteFile(hFile, m_pBmpInfo, dwBitmapInfoHeader, &dwBytesRead, NULL); //黑白和彩色自适应
    ::WriteFile(hFile, pBuffer, dwImageSize, &dwBytesRead, NULL);

    CloseHandle(hFile);
}

//----------------------------------------------------------------------------------
/*
\brief     存储Raw图像
\param     objCImageDataPointer  图像数据对象
\param     strFilePath  显示图像文件名
\return    无
*/
//----------------------------------------------------------------------------------
void CGXBitmap::SaveRaw(CImageDataPointer& objCImageDataPointer,const std::string& strFilePath)
{
    if ((objCImageDataPointer.IsNull()) || (strFilePath == "")) {
        writeLog("SaveRaw", "Argument is error", TYPE_ERROR);
        return;
    }

    //检查图像是否改变并更新Buffer
    __UpdateBitmap(objCImageDataPointer);

    DWORD   dwImageSize = (DWORD)objCImageDataPointer->GetPayloadSize();  // 写入文件的长度
    DWORD   dwBytesRead = 0;                // 文件读取的长度

    BYTE* pbuffer = (BYTE*)objCImageDataPointer->GetBuffer();
    if (!m_bIsColor) {
        // 黑白相机需要翻转数据后显示
        for(int i = 0; i < m_nImageHeight; i++) {
            memcpy(m_pImageBuffer + i * m_nImageWidth, pbuffer + (m_nImageHeight - i -1) * m_nImageWidth, (size_t)m_nImageWidth);
        }

        pbuffer = m_pImageBuffer;
    }

    // 创建文件
    std::wstring s = StringToWString(strFilePath);
    LPCTSTR LPCSTR_ImgFileName = s.c_str();//使用了我自定义的一个转换函数,并且连续调用了.c_str(),并对下面CreateFile进行了修改
    HANDLE hFile = ::CreateFile(LPCSTR_ImgFileName,
        GENERIC_WRITE,
        FILE_SHARE_READ,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL);

    if (hFile == INVALID_HANDLE_VALUE) {   // 创建失败则返回

        throw std::runtime_error("Handle is invalid");
    } else {                                // 保存Raw图像
        ::WriteFile(hFile, pbuffer, dwImageSize, &dwBytesRead, NULL);
        CloseHandle(hFile);
    }
}

std::wstring StringToWString(const std::string &s)
{
    std::wstring wsTmp(s.begin(), s.end());
    std::cout << s;
    return wsTmp;
}

QString MakeMyDirectory()
{
    QString strTemp, strAppPath;
    strTemp = QCoreApplication::applicationDirPath();//运行路径

    int m = strTemp.lastIndexOf("/");
    if (m != -1) {
        strAppPath = strTemp.left(m);
    }

    //设置日志存放路径
    QString strLogDir;//创建单独存放日志的文件夹
    strLogDir = strAppPath + "/Image";

    //创建
    if (createDir(strLogDir)) {
        return strLogDir;
    } else {
        return "";
    }
}

drawwidget.h↓

#ifndef DRAWWIDGET_H
#define DRAWWIDGET_H

#include <QImage>
#include <QWidget>
#include <QPainter>

class DrawWidget : public QWidget
{
    Q_OBJECT

public:
    explicit DrawWidget(QWidget *parent = nullptr);

    void ReSizeImg(QImage inputImg);//将输入的Qimage调整为和窗口大小一致
    void setSize();

protected:
    void paintEvent(QPaintEvent *event);

public:
    QImage imgToDraw;

private:
    QSize drawWigetSize;
};

#endif // DRAWWIDGET_H

drawwidget.cpp↓

#include "drawwidget.h"

DrawWidget::DrawWidget(QWidget *parent) : QWidget(parent)
{

}

void DrawWidget::ReSizeImg(QImage inputImg)
{
    imgToDraw = inputImg.scaled(drawWigetSize, Qt::IgnoreAspectRatio);
}

void DrawWidget::setSize()
{
    drawWigetSize = this->size();
}

void DrawWidget::paintEvent(QPaintEvent *event)
{
    QPainter p(this);
    p.drawImage(0, 0, imgToDraw);
}

补充说明:代码中的创建文件夹函数(createDir()函数)是在另外一个类中写的,如下补充截图:
在这里插入图片描述

  • 5
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值