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()函数)是在另外一个类中写的,如下补充截图: