工业相机采图方式、图像格式(BYTE、HObject和Mat)转换

news2024/11/29 6:27:01

1、概述

机器视觉项目中,如何采集到合适的图像是项目的第一步,也是最重要的一步,直接关系到后面图像处理算法及最终执行的结果。所以采用不同的工业相机成像以及如何转换成图像处理库所需要的格式成为项目开发中首先要考虑的问题。

2、工业相机图像采集方式

这里工业相机选择就不赘述了,因为相对于项目或设备来讲,需要根据项目需要挑选适合的硬件,而针对图像采集也有基于SDK开发或直接采用Halcon的相机采集助手。

2.1 相机自带的SDK采图

这种方式是厂家比较推荐的,也是比较常用的方法,原因之一是每个厂家对自己的产品比较熟知,提供的SDK也是比较稳定的,而且也会有一定的技术支持;第二个原因就是基于SDK的开发可以获取更多相机及图像的原始参数,这样在做项目中的灵活度也会更高,一般大一些的品牌相机的SDK和demo程序会更详细,而且针对相机封装的函数也更完善,这里我在项目中用的较多的是Dalsa和basler,国产的用的较多的是海康、京航和方诚。这里针对Dalsa和balsa的面阵相机做一下简单的介绍。(编程环境是Qt5)

2.1.1 Dalsa相机采图方式

初始化相机

void CaptureThread::initCamera()
{
    m_AcqDevice = new SapAcqDevice(SapLocation("Nano-M2420_1", 0),false);
    BOOL Status = m_AcqDevice->Create();
    if(Status){
        qDebug()<<"相机连接成功"<<endl;
        m_Buffers = new SapBufferWithTrash(3,m_AcqDevice);
        m_AcqDevice->GetFeatureValue("Width",&Img_Width);
        m_AcqDevice->GetFeatureValue("Height",&Img_Height);
        //        qDebug()<<"Width:"<<Img_Width<<"Height:"<<Img_Height<<endl;
        m_Xfer    = new SapAcqDeviceToBuf(m_AcqDevice,m_Buffers,XferCallback,this);

    }
    if (!CreateObjects()) { return; }
}

回调函数主要是用于获取图像数据

void CaptureThread::XferCallback(SapXferCallbackInfo *pInfo)
{
    CaptureThread *pDlg = (CaptureThread *) pInfo->GetContext();
    if(pInfo->IsTrash()){
        qDebug()<<"IsTrash"<<endl;
        return;
    }
    //    qDebug()<<"相机向缓存中写入数据"<<endl;
    HObject hv_Current_Image;

    BYTE *pData;
    pDlg->m_Buffers->GetAddress((void**)&pData);
    GenImage1(&hv_Current_Image,"byte",pDlg->Img_Width,pDlg->Img_Height,(Hlong)pData);
    //    GenImageInterleaved(&hv_Current_Image,(Hlong)pData,"rgb",pDlg->Img_Width,pDlg->Img_Height,-1,"byte",0,0,0,0,-1,0);

    //判断图像处理工作方式
    if(pDlg->TestImg_WorkType == 0)
    {
        emit pDlg->startImgProcess(hv_Current_Image);
        pDlg->TestImg_WorkType = -1;
    }
    else if(pDlg->TestImg_WorkType == 1)
    {
        emit pDlg->startImgProcess(hv_Current_Image);
    }
    else{
        emit pDlg->canShowImg(hv_Current_Image);
    }
    pDlg->m_Buffers->ReleaseAddress((void *)pData);
    pDlg->m_Buffers->Clear(pDlg->m_Buffers->GetIndex());
}

最后是调用Dalsa相机的采图函数来触发

void CaptureThread::OnGrab()
{
    m_Xfer->Grab();
}

void CaptureThread::OnSnap()
{
    m_Xfer->Snap();
}

void CaptureThread::OnFreeze()
{
    SnapSignal = true;
    m_Xfer->Freeze();
}

2.1.2 basler相机采图方式

首先初始化相机:

void CBaslerCameraControl::initCamera()
{
    PylonInitialize();      //初始化相机
    m_basler.RegisterImageEventHandler(this, RegistrationMode_Append, Cleanup_Delete);     //注册图像事件程序,模式为Append插入;
   
    //    m_basler.RegisterConfiguration(this, RegistrationMode_ReplaceAll, Cleanup_None);     //注册图像事件,模式为单张覆盖;
    m_basler.Attach(CTlFactory::GetInstance().CreateFirstDevice(),Cleanup_Delete);
    qDebug()<<"Using device " << m_basler.GetDeviceInfo().GetModelName()<<endl;
    m_basler.Open();        //打开相机
    if (!m_basler.IsOpen() || m_basler.IsGrabbing())
    {
        qDebug()<<"camera open failed"<<endl;
        return;
    }
}

下一步就可以调用库函数进行采集

//连续采图的回调函数
void CBaslerCameraControl::OnImageGrabbed(CInstantCamera &camera, const CGrabResultPtr &grabResult)
{
        m_mutexLock.lock();

    if (grabResult->GrabSucceeded())
    {
        m_ptrGrabResult = grabResult;//将捕获到的图像传递出去
//        qDebug() <<"Captureok"<<endl;
        HObject hv_CurrentImg;
        CopyImgToHObject(m_ptrGrabResult,hv_CurrentImg);
        emit canShowImg(hv_CurrentImg);
        qDebug() <<"Captureok"<<endl;
    }
    m_mutexLock.unlock();
}

void CBaslerCameraControl::StartAcquire()
{
    if ( !m_basler.IsGrabbing() ){
        GrabOnLine_Signal = true;
        m_basler.StartGrabbing(GrabStrategy_LatestImageOnly,GrabLoop_ProvidedByInstantCamera);
    }
}

void CBaslerCameraControl::StartSnap()
{
    m_basler.StartGrabbing(1);
    CBaslerUniversalGrabResultPtr ptrGrabResult;
    m_basler.RetrieveResult( 5000, ptrGrabResult, TimeoutHandling_ThrowException);
    if (ptrGrabResult->GrabSucceeded())
    {
        qDebug()<<"snapok"<<endl;
        HObject hv_CurrentImg;
        CopyImgToHObject(ptrGrabResult,hv_CurrentImg);
        emit canShowImg(hv_CurrentImg);
    }
}

void CBaslerCameraControl::CloseCamera()
{
    if(m_basler.IsOpen()) {
        m_basler.DetachDevice();
        m_basler.Close();
        m_basler.DestroyDevice();
        m_ptrGrabResult.Release();
    }
}

void CBaslerCameraControl::deleteAll()
{
    //停止采集
     if(m_isOpenAcquire) {
        StopAcquire();
     }
    //关闭摄像头
    try
    {
        CloseCamera();
        m_basler.DeregisterImageEventHandler(this);

        //关闭库
        qDebug() << "SBaslerCameraControl deleteAll: PylonTerminate" ;
        PylonTerminate();
    }
    catch (const Pylon::GenericException& e)
    {
        qDebug() << e.what();
    }
}

2.1.3 小结

大部分的相机SDK大体都类似,都是通过相机句柄去调用图像采集的回调函数或图像buff,在以上介绍的程序中SDK获取的是图像的buff,再通过buff里的图像数据转换成HObject或者Mat格式,这个详细操作下节再讲。通过相机SDK进行Grab或Snap,其优势是成像高效稳定,搞懂相机自带的SDK程序和Demo程序,可以很快的实现采图测试,而且BYTE格式的原始图像数据,可以使用c++进行paint或转成Qt的Qimage进行显示操作。其不便之处就在于,将BYTE转换成HObject或Mat的耗时,可能会影响图像实时显示,所以需要考虑图像显示和处理的效率问题。对于有编程基础的同学,推荐使用此方法进行图像采集。
##2.2 Halcon自带的图像采集助手进行采图
1.使用basler相机自带的 IPConfigurator软件设置好电脑的ip地址,保证电脑和相机已连接成功,然后使用basler的采图工具pylon viewer测试是否能采集到图像;
2.打开halcon,助手选项下选择“打开新的Image Acquisition”
相机助手.png
3.自动检测接口,选择对应的相机接口文件,如“pylon”,然后点击“代码生成”即可
相机接口文件.PNG

2.3 采图方法总结

使用Halcon采图助手进行采图比较适合新手,没有太大的编程量,也可以用于项目前期的方案制定,图像处理评估等;第一种采图方法的适用性更广,可以满足不同项目的开发需求。

3、图像格式的互相转换(BYTE、HObject、Mat和QImage)

在项目开发过程中,会用到不同的图像处理库、不同的图形显示环境,所以需要针对图像的格式进行对应的转换

3.1 BYTE转HObject

这个比较常用,通过相机SDK采集后的图像数据转换成Halcon格式,便于后期的图像处理操作;

void CBaslerCameraControl::CopyImgToHObject(CGrabResultPtr pInBuffer, HObject &hv_Image)
{
    HBYTE *pData = (HBYTE*)pInBuffer->GetBuffer();
    int nHeight = pInBuffer->GetHeight();
    int nWidth = pInBuffer->GetWidth();
    GenImage1(&hv_Image,"byte",nWidth,nHeight,(Hlong)pData);
//    HObject hv_Current_Image;
//    //图像格式转换方法一:数据拷贝
//    //    int size = (pDlg->Img_Width)* (pDlg->Img_Height)*sizeof(BYTE);
//    //    BYTE *dataBuf = new BYTE[size];
//    //    dataBuf = (byte *)malloc(size);
//    //    pDlg->m_Buffers->Read(0,(pDlg->Img_Width)* (pDlg->Img_Height),dataBuf);
//    //    GenImage1(&hv_Current_Image,"byte",pDlg->Img_Width,pDlg->Img_Height,(Hlong)dataBuf);
//    //图像格式转换方法二:数据指针
//    BYTE *pData;
//    pDlg->m_Buffers->GetAddress((void**)&pData);
//    GenImage1(&hv_Current_Image,"byte",pDlg->Img_Width,pDlg->Img_Height,(Hlong)pData);
//    //    GenImageInterleaved(&hv_Current_Image,(Hlong)pData,"rgb",pDlg->Img_Width,pDlg->Img_Height,-1,"byte",0,0,0,0,-1,0);
}

上述转换有两种方法,一种使用数据拷贝,一种使用数据指针,相比较第二种耗时少,推荐使用。

3.2 BYTE转Mat、QImage

void CBaslerCameraControl::CopyImgToMat(CGrabResultPtr pInBuffer, Mat &Mat_Img)
{
    HBYTE *pData = (HBYTE*)pInBuffer->GetBuffer();
    int nHeight = pInBuffer->GetHeight();
    int nWidth = pInBuffer->GetWidth();
    unsigned char *pImageBuffer = (unsigned char *)pInBuffer->GetBuffer();
    //黑白图像
    const uint8_t *pImageBuffer = (uint8_t *) ptrGrabResult->GetBuffer();
    Mat_img =Mat(cv::Size(nWidth , nHeight ), CV_8U, (void*)pImageBuffer, cv::Mat::AUTO_STEP);
    QImg = QImage(pImageBuffer ,nWidth ,nHeight ,QImage::Format_Indexed8);
     //彩色图像
    // 新建pylon ImageFormatConverter对象.
	CImageFormatConverter formatConverter;
    //确定输出像素格式
	formatConverter.OutputPixelFormat = PixelType_BGR8packed;
	//将抓取的缓冲数据转化成pylon image.
	formatConverter.Convert(m_bitmapImage, pInBuffer);
	// 将 pylon image转成OpenCV image.
	Mat_img = cv::Mat(pInBuffer->GetHeight(), pInBuffer->GetWidth(), CV_8UC3, (uint8_t *)m_bitmapImage.GetBuffer());
    QImg = QImage(pImageBuffer ,nWidth /3,nHeight ,nWidth ,QImage::Format_RGB888);
}

3.3 HObject与Mat互转

using namespace cv;
using namespace Halcon;

//HObject转Mat
Mat HObject2Mat(HObject Hobj)
{
    HTuple htCh = HTuple();
    HString cType;
    cv::Mat Image;
    ConvertImageType(Hobj,&Hobj,"byte");
    CountChannels(Hobj,&htch);
    Hlong wid = 0;
    Hlong hgt = 0;
    if(htch[0].I() == 1)
    {
        HImage hImg(Hobj);
        void *ptr = hImg.GetImagePointer1(&cType,&wid,&hgt);
        int W = wid;
        int H = hgt;
        Image.create(H,W,CV_8UC1);
        unsigned char *pdata = static_case<unsigned char*>(ptr);
        memcpy(Image.data,pdata,W*H);
     }
     else if (htch[0].I()  == 3)
     {
        void *Rptr;
        void *Gptr;
        void *Bptr;
        HImage hImg(Hobj);
        hImg.GetImagePointer3(&Rptr,&Gptr,&Bptr,&cType,&wid,&hgt);
        int W = wid;
        int H = hgt;
        Image.create(H,W,CV_8UC3);
        vector<cv::Mat> VecM(3);
        VecM[0].create(H,W,CV_8UC1);
        VecM[1].create(H,W,CV_8UC1);
        VecM[2].create(H,W,CV_8UC1);
        unsigned char *R = (unsigned char *)Rptr;
        unsigned char *G = (unsigned char *)Gptr;
        unsigned char *B = (unsigned char *)Bptr;
        memcpy(VecM[2].data,R,W*H);
        memcpy(VecM[1].data,G,W*H);
        memcpy(VecM[0].data,B,W*H);
        cv::merge(VecM,Image);
     }
     return Image;
}
//Mat转Hobject
Hobject Mat2Hobject(Mat& image)
{
	Hobject Hobj = Hobject();
	int hgt = image.rows;
	int wid = image.cols;
	int i;
	if (image.type() == CV_8UC3)
	{
		vector<Mat> imgchannel;
		split(image, imgchannel);
		Mat imgB = imgchannel[0];
		Mat imgG = imgchannel[1];
		Mat imgR = imgchannel[2];
		uchar* dataR = new uchar[hgt*wid];
		uchar* dataG = new uchar[hgt*wid];
		uchar* dataB = new uchar[hgt*wid];
		for (i = 0; i < hgt; i++)
		{
			memcpy(dataR + wid*i, imgR.data + imgR.step*i, wid);
			memcpy(dataG + wid*i, imgG.data + imgG.step*i, wid);
			memcpy(dataB + wid*i, imgB.data + imgB.step*i, wid);
		}
		gen_image3(&Hobj, "byte", wid, hgt, (Hlong)dataR, (Hlong)dataG, (Hlong)dataB);
		delete[]dataR;
		delete[]dataG;
		delete[]dataB;
		dataR = NULL;
		dataG = NULL;
		dataB = NULL;
	}
	else if (image.type() == CV_8UC1)
	{
		uchar* data = new uchar[hgt*wid];
		for (i = 0; i < hgt; i++)
			memcpy(data + wid*i, image.data + image.step*i, wid);
		gen_image1(&Hobj, "byte", wid, hgt, (Hlong)data);
		delete[] data;
		data = NULL;
	}
	return Hobj;
}

3.4 HObject转QImage

void HObjectToQImage(HObject hv_image, QImage &qimage)
{
    HTuple hChannels,htype,hpointer;
    HTuple width=0;
    HTuple height=0;
    ConvertImageType(hv_image,&hv_image,"byte");//将图片转化成byte类型
    CountChannels(hv_image,&hChannels);       //判断图像通道数

    if(hChannels[0].I()==1)//单通道图
    {
        unsigned char *ptr;
        GetImagePointer1(hv_image,&hpointer,&htype,&width,&height);
        ptr=(unsigned char *)hpointer[0].L();
        qimage= QImage(ptr,width,height,QImage::Format_Indexed8);
    }
    else if(hChannels[0].I()==3)//三通道图
    {
        unsigned char *ptr3;
        HObject ho_ImageInterleaved;

        rgb3_to_interleaved(hv_image, &ho_ImageInterleaved);
        GetImagePointer1(ho_ImageInterleaved, &hpointer, &htype, &width, &height);

        ptr3=(unsigned char *)hpointer[0].L();
        qimage= QImage(ptr3,width/3,height,width,QImage::Format_RGB888);
    }
}
void rgb3_to_interleaved (HObject ho_ImageRGB, HObject *ho_ImageInterleaved)
{
    // Local iconic variables
    HObject  ho_ImageAffineTrans, ho_ImageRed, ho_ImageGreen;
    HObject  ho_ImageBlue, ho_RegionGrid, ho_RegionMoved, ho_RegionClipped;

    // Local control variables
    HTuple  hv_PointerRed, hv_PointerGreen, hv_PointerBlue;
    HTuple  hv_Type, hv_Width, hv_Height, hv_HomMat2DIdentity;
    HTuple  hv_HomMat2DScale;

    GetImagePointer3(ho_ImageRGB, &hv_PointerRed, &hv_PointerGreen, &hv_PointerBlue,
                     &hv_Type, &hv_Width, &hv_Height);
    GenImageConst(&(*ho_ImageInterleaved), "byte", hv_Width*3, hv_Height);
    //
    HomMat2dIdentity(&hv_HomMat2DIdentity);
    HomMat2dScale(hv_HomMat2DIdentity, 1, 3, 0, 0, &hv_HomMat2DScale);
    AffineTransImageSize(ho_ImageRGB, &ho_ImageAffineTrans, hv_HomMat2DScale, "constant",
                         hv_Width*3, hv_Height);
    //
    Decompose3(ho_ImageAffineTrans, &ho_ImageRed, &ho_ImageGreen, &ho_ImageBlue);
    GenGridRegion(&ho_RegionGrid, 2*hv_Height, 3, "lines", hv_Width*3, hv_Height+1);
    MoveRegion(ho_RegionGrid, &ho_RegionMoved, -1, 0);
    ClipRegion(ho_RegionMoved, &ho_RegionClipped, 0, 0, hv_Height-1, (3*hv_Width)-1);

    ReduceDomain(ho_ImageRed, ho_RegionClipped, &ho_ImageRed);
    MoveRegion(ho_RegionGrid, &ho_RegionMoved, -1, 1);
    ClipRegion(ho_RegionMoved, &ho_RegionClipped, 0, 0, hv_Height-1, (3*hv_Width)-1);
    ReduceDomain(ho_ImageGreen, ho_RegionClipped, &ho_ImageGreen);
    MoveRegion(ho_RegionGrid, &ho_RegionMoved, -1, 2);
    ClipRegion(ho_RegionMoved, &ho_RegionClipped, 0, 0, hv_Height-1, (3*hv_Width)-1);
    ReduceDomain(ho_ImageBlue, ho_RegionClipped, &ho_ImageBlue);
    OverpaintGray((*ho_ImageInterleaved), ho_ImageRed);
    OverpaintGray((*ho_ImageInterleaved), ho_ImageGreen);
    OverpaintGray((*ho_ImageInterleaved), ho_ImageBlue);
    return;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1529903.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Micropython ESP32】RTC时钟

文章目录 前言一、RTC时钟的介绍1.1 RTC时钟的作用1.2 Micropython中时钟于硬件时钟的区别 二、machine.RTC 类2.1 machine.RTC 类的构造方法2.2 初始化 RTC 设备起始时间2.3 关闭 RTC 设备2.4 获取当前时间 三、示例代码总结 前言 在嵌入式设备开发中&#xff0c;实时时钟&am…

001_【基础篇】SpringBoot入门案例创建与实现

要求&#xff1a;使用 Springboot 开发一个 web 程序&#xff0c;浏览器发起请求/hello后&#xff0c;给浏览器返回字符串 hello springboot 使用 springboot 只需要引入一个起步依赖 <dependency><groupId>org.springframework.boot</groupId><artifac…

Python 自然语言处理库之stanza使用详解

概要 在自然语言处理(NLP)领域,Python Stanza 库是一个备受推崇的工具,它提供了强大的功能和易用的接口,帮助开发者处理文本数据、进行语言分析和构建NLP应用。本文将深入探讨 Stanza 库的特性、用法,并通过丰富的示例代码展示其在实际项目中的应用。 Stanza 简介 Stan…

docker小白第十四天之Portainer与CIG

Portainer简介 Portainer是一款轻量级的应用&#xff0c;它提供了图形化界面&#xff0c;用于方便地管理Docker环境&#xff0c;包括单机环境和集群环境。 Portainer命令安装 # 一个容器可以同时起多个-p端口&#xff0c;restartalways表示随时在线&#xff0c;重启机器后也…

Tomcat 服务器部署和 IDEA 配置 Tomcat

(一) Tomcat 简介 Tomcat是Apache软件基金会一个核心项目&#xff0c;是一个开源免费的轻量级Web服务器&#xff0c;支持Servlet/JSP少量JavaEE规范。 概念中提到了JavaEE规范&#xff0c;那什么又是JavaEE规范呢? JavaEE: Java Enterprise Edition,Java企业版。指Java企业级…

CentOS 7.9 常用环境配置

文章目录 环境准备安装docker安装Java安装maven安装git安装MYSQL安装Redis安装RabbitMq安装minio 环境准备 操作系统版本为centos 7.9&#xff0c;内核版本需要在3.10以上 sudo uname -rsudo cat /etc/redhat-release1.确认环境好后&#xff0c;安装工具包并设置仓库 sudo yum…

解决由于历史原因解析tflite失败的问题

文章目录 0. 背景1. tflite 历史遗留问题2. schema3. flatbuffers 编译器3.1 安装 FlatBuffers 编译器3.2. 编译 FlatBuffers schema 文件3.3 使用生成的 Python 文件 4 问题未解决终极解决方案 写在最前面&#xff1a;解决方法是升级tensorflow版本&#xff0c;重新生成tflite…

算法---二分查找练习-2(寻找旋转排序数组中的最小值)

寻找旋转排序数组中的最小值 1. 题目解析2. 讲解算法原理3. 编写代码 1. 题目解析 题目地址&#xff1a;点这里 2. 讲解算法原理 首先&#xff0c;检查数组的最后一个元素是否大于第一个元素。如果是&#xff0c;说明数组没有进行旋转&#xff0c;直接返回第一个元素作为最小值…

ISIS接口明文认证实验简述

默认情况下&#xff0c;ISIS接口认证通过在ISIS协议数据单元&#xff08;PDU&#xff09;中添加认证字段&#xff0c;例如&#xff1a;一个密钥或密码&#xff0c;用于验证发送方的身份。 ISIS接口认证防止未经授权的设备加入到网络中&#xff0c;并确保邻居之间的通信是可信的…

华为配置HTTPS服务器实验

配置HTTPS服务器示例 组网图形 图1 配置HTTPS服务器组网图 组网需求配置思路配置注意事项操作步骤配置文件 组网需求 如图1所示&#xff0c;用户通过Web方式访问网关设备AP。 为了防止传输的数据不被窃听和篡改&#xff0c;实现对设备的安全管理&#xff0c;网络管理员要…

【Unity】从0到1的横版2d制作笔记-DAY1

写在前面&#xff1a; 感谢旻子提供的Unity2d课程捏&#xff0c;红豆泥阿里嘎多 创建项目 测试Visual Studio的使用 右键选择【create】&#xff0c;右键创建C# Script&#xff0c;待文件创建完毕后双击查看能否正确跳转。 正确跳转的结果是能看见代码中注释标注有&#xff1a;…

策略模式实战

项目推荐最近开发完成的项目中使用到了策略模式&#xff0c;实现多种支付方式&#xff0c;避免了后期支付方式if-else代码的冗余&#xff0c;也有利于后期支付的一个扩展。同时这个项目非常适合于做毕设&#xff0c;想了解这个项目的同学可以联系我QQ&#xff1a;3808898981 前…

android studio 安装lombok插件

android studio 安装lombok插件 由于 AS 不是基于 IDEA release 版本进行开发的&#xff0c;因此lombok对idea的插件可能再as中无法查看到。因此再as中通过plugins管理无法安装lombok插件。这就导致再gradle引入lombok后&#xff0c;虽然编译可能会通过&#xff0c;但是代码在查…

二、Kubernetes(k8s)中部署项目wordpress(php博客项目,数据库mysql)

前期准备 1、关机顺序 2、开机顺序 (1)、k8s-ha1、k8s-ha2 (2)、master01、master02、master03 (3)、node01、node02 一、集群服务对外提供访问&#xff0c;需要通过Ingress代理发布域名 mast01上传 ingress-nginx.yaml node01、node02 上传 ingress-nginx.tar 、kube-webh…

10-项目部署_持续集成-黑马头条

项目部署_持续集成 1 今日内容介绍 1.1 什么是持续集成 持续集成&#xff08; Continuous integration &#xff0c; 简称 CI &#xff09;指的是&#xff0c;频繁地&#xff08;一天多次&#xff09;将代码集成到主干 持续集成的组成要素 一个自动构建过程&#xff0c; 从…

uniapp 云开发省钱之调整函数执行内存大小

我这个5块钱一个月的服务空间配置&#xff1a; 现在还只有少量的用户和自己测试之用&#xff0c;目前消耗的情况&#xff1a; 云函数的使用量还是挺高的&#xff0c;目前还是正好能覆盖一个月的使用量&#xff0c;等用户量上来肯定是不行的&#xff0c;所以得想想办法压榨一下云…

问GPT:将Excel中一行转换为一列的方法

问GPT&#xff1a;将excel中一行转换为一列的方法 函数&#xff1a; TRANSPOSE(A2:E2)

前端项目,个人笔记(二)【Vue-cli - 引入阿里矢量库图标 + 吸顶交互 + setup语法糖】

目录 1、项目中引入阿里矢量库图标 2、实现吸顶交互 3、语法糖--<script setup> 3.1、无需return 3.2、子组件接收父组件的值-props的使用 3.3、注册组件 1、项目中引入阿里矢量库图标 步骤一&#xff1a;进入阿里矢量库官网中&#xff1a;iconfont-阿里巴巴矢量…

内网横向移动小结

windows Windows-Mimikatz 适用环境&#xff1a; 微软为了防止明文密码泄露发布了补丁 KB2871997&#xff0c;关闭了 Wdigest 功能。当系统为 win10 或 2012R2 以上时&#xff0c;默认在内存缓存中禁止保存明文密码&#xff0c;此时可以通过修改注册表的方式抓取明文&#xff…

MacOS Xcode 使用LLDB调试Qt的 QString

环境&#xff1a; MacOS&#xff1a; 14.3Xcode&#xff1a; Version 15.0Qt&#xff1a;Qt 6.5.3 前言 Xcode 中显示 预览 QString 特别不方便, 而Qt官方的 lldb 脚本debugger/lldbbridge.py一直加载失败&#xff0c;其他第三方的脚本都 不兼容当前的 环境。所以自己研究写…