ISP图像信号处理——白平衡校正和标定介绍以及C++实现

从数码相机直接输出的未经过处理过的RAW图到平常看到的JEPG图有一系列复杂的图像信号处理过程,称作ISP(Image Signal Processing)。这个过程会经过图像处理和压缩

参考文章1:http://t.csdn.cn/LvHH5

参考文章2:http://t.csdn.cn/c97t5

参考文章3:http://t.csdn.cn/UbAOu

http://t.csdn.cn/HuI67

参考文章4:http://t.csdn.cn/lwUAc

参考文章5:http://t.csdn.cn/HuI67

参考文章(下面整理的笔记):Understanding White Balance Control - 知乎 (zhihu.com)

人眼具有颜色恒常性,可以避免光源变化带来的颜色变化,但是图像传感器不具备这种特性,从而造成色偏,白平衡就是需要校正这个颜色的偏差。

颜色恒常性是指在照度发生变化的条件下人们对物体表面颜色的知觉趋于稳定的心理倾向。

色温描述的是具有一定表面温度的“黑体”(blackbody)的辐射光的光谱特性。简单的理解就是颜色随温度的变化规律,比如生铁就是黑色,加热会变成橘红色,继续加热到液态会呈现偏白的颜色,这种随温度而产生的颜色变化就光谱特性。

白平衡校正

  • 手动白平衡:在拍照前通过拍摄一个18度灰的卡片,然后计算出当时环境的白平衡增益值对后面的图片进行校正;

  • 自动白平衡:相机通过本身的算法,通过获取的图像自动计算出增益值对图像进行校正的方式。

AWB(Automatic White Balance),自动白平衡理解

为了将拍摄场景中的白色物体在显示时正确还原为白色,首先需要知道真实的白色物体在sensor RGB 空间中呈现什么颜色。常用的方法是通过实验标定标准白色在一些典型色温下呈现的sensor RGB 颜色,然后通过几个标定的色温点可以外推白色在所有可能色温下呈现的颜色,从而建立场景色温与sensor RGB 白色的对应关系,这个对应关系通常用白平衡增益来描述。

白平衡增益通常记为(R/G, B/G),即红色和蓝色相对于绿色的比例。目前对白平衡增益存在两种不同的定义:

1.一部分厂家将(R/G, B/G)定义为sensor捕捉到的图像中红色和蓝色的统计值,因此在高色温(如D65)下B/G大于R/G。

海思的光源的色温曲线(普朗克曲线)表现为下图所示的形状。

通过对不同sensor的色温曲线标定数据可知:在D50-D65之间 R/G、B/G的比值基本相近,相差不大。在低色温的时候,主要是G和B分量在改变,在高色温的时候主要是G和B分量在改变。

以海思的色温曲线为例:从直角双曲线的两个坐标方向来看,在D50-D65这个R/G、B/G为分界点,沿着R/G的方向看,最后应该会有一个B/G的饱和点。同理,沿B/G方向看,R/G也会有一个饱和点,饱和区域的时候其两个坐标方向上看应该呈现一条类似接近直线的曲线段。

或者也可以这么理解:

色温越低,R/G的比值越大,B/G的比值越小,当R/G达到某一阈值后,B/G则缓慢变小直至达到一个最小值而不再改变。

同理,色温越高,B/G的比值越大,R/G的比值越小,当B/G达到某一阈值后,R/G则缓慢变小直至达到一个最小值而不再改变。

直角双曲线靠近坐标的部分与坐标轴近似平行的直线段,比较符合色温曲线的变化规律。

根据G/R和G/B用直角双曲线函数拟合的图像和海思的普朗克曲线十分相似,如下图所示。

在这种定义下,D65光源下的白色具有较大的B/G统计值。

2.另一部分厂家则将(R/G, B/G)定义成为了使sensor捕捉的图像达到白平衡需要施加的增益系数,因此在高色温下B/G小于R/G。

在这种定义下,D65光源下的白色需要较大的R/G增益才能达到白平衡。

以R/G,B/G值代表增益系数,而非图像的统计值,在此定义下,白平衡标定的大致过程是:

  • 以标定好的若干个色温点(A, TL84, D50, D65, D75等)为核心可以拟合出一条色温曲线
  • 以色温曲线上的点为圆心,以一定半径画椭圆,可以得到一些列椭圆。
  • 所有椭圆所覆盖的(R/G,B/G)集合就认为是真实场景中的白色物体在全部色温下的可能值。不落在这个范围内的颜色不认为是白色。

某些特殊光源(如CWF)光谱特征偏离黑体光谱较大,因此需要单独标定。

一个好的白平衡算法需要能够检测出画面中存在的特殊场景并加以针对性的强化。点云分析是一种非常有效的提取图像特征的方法,但由于需要分析每一个像素的白平衡增益,所以计算量非常大。

理想的白平衡控制时序如下图所示,其基本流程是:

  1. 在第N帧图像结束时ISP硬件采集关于白平衡的统计信息
  2. 白平衡算法根据第N帧的统计数据预测第N+1帧的控制参数
  3. 摄像机固件在第N+1帧图像到达之前将白平衡控制参数写入ISP硬件寄存器
  4. 第N+1帧图像使用新的白平衡参数进行曝光

但是实际上由于两帧的时间间隔往往很小,不容易保证算法完成一系列的计算和配置,因此实际的白平衡控制往往采用隔帧生效的时序,其基本流程如下图所示

  1. 在第N帧图像结束时ISP硬件采集关于白平衡的统计信息
  2. 在第N+1帧开始后,白平衡算法根据第N帧的统计数据预测第N+2帧的控制参数,需要在第N+1帧结束前完成所有计算
  3. 摄像机固件在第N+2帧图像到达之前将新的白平衡控制参数写入ISP硬件寄存器
  4. 第N+2帧图像使用新的白平衡参数进行曝光

白平衡校正算法

1.灰度世界算法

基于一个假说:任一幅图像,当它有足够多的色彩变化,则它的RGB分量的均值会趋于相等。这是一个在自动白平衡方面应用极为广泛的理论。对此算法的流程如下:

  • 计算各个颜色通道的平均值;
  • 寻找一个参考值K,一般情况选取Gmean;
  • 计算Rgain = Gmean/Rmean, Bgain = Gmean/Bmean;
  • 对图像中的每个像素都乘以对应的gain值进行校正;
  • 以下C++代码,不知道本人缺少什么运行条件,在自己电脑上最后显示结果不太理想;大家也可以试试,该代码出自:http://t.csdn.cn/edydD
//自动白平衡  //灰度世界算法
void GrayWorldAlgorithm(Mat& src,Mat& dst)
{assert(3==src.channels());//求BGR分量均值auto mean = mean(src);//需要调整的BGR分量的增益float gain_B(0),gain_G(0),gain_R(0);float K = (mean[0]+mean[1]+mean[2])/3.0f;gain_B = K/mean[0];gain_G = K/mean[1];gain_R = K/mean[2];vector<Mat> channels;split(src,channels);//调整三个通道各自的值channels[0] = channels[0]*gain_B;channels[1] = channels[1]*gain_G;channels[2] = channels[2]*gain_R;//通道合并cv::merge(channels,dst);
}

下面的灰度世界法是本人自己根据理解写的,但是显示结果存疑。有点怀疑是不是BGR的赋值跟输入图像的拜尔分布有关,赋值顺序有误?有能力者找到错误的可以帮我纠出(感谢感谢):

//灰度世界法,输入三通道彩色图
Mat awbgray(Mat img_rgb8)
{Mat img_awb = Mat::zeros(height, width, CV_32FC3);Mat imgR = Mat::zeros(height, width, CV_8UC1);Mat imgG = Mat::zeros(height, width, CV_8UC1);Mat imgB = Mat::zeros(height, width, CV_8UC1);double Rsum=0, Gsum=0,Bsum=0;/*unsigned char* srcdata;unsigned char *dstdata;*/for (int row = 0; row < height; row++){//ptr得到行数据的头指针,得到row行指针/*uchar* data = img_awb.ptr<uchar>(row);*/Vec3b* inptr = img_rgb8.ptr<Vec3b>(row);for (int col = 0; col < width; col++){//BGGR分布?//imgB.at<Vec3b>(row, col)[0]   b通道/*uchar bdata = data[col * img_awb.channels() + 0];*/imgB.at<uchar>(row, col) = (*(inptr + col))[0];imgG.at<uchar>(row, col) = (*(inptr + col))[1];imgR.at<uchar>(row, col) = (*(inptr + col))[2];Bsum += imgB.at<uchar>(row, col);Gsum += imgG.at<uchar>(row, col);Rsum += imgR.at<uchar>(row, col);}}double Rmean=0, Gmean=0, Bmean = 0;Rmean = Rsum / (height * width);Gmean = Gsum / (height * width);Bmean = Bsum / (height * width);double K = (Rmean + Gmean + Bmean) / 3;cout << "Rmeanvalue:" << Rmean << endl;cout << "Gmeanvalue:" << Gmean << endl;cout << "Bmeanvalue:" << Bmean << endl;cout << "rgbmeanvalue:" << K << endl;double Rgain = K / Rmean;double Ggain = K / Gmean;double Bgain = K / Bmean;//重新调整计算RGB值for (int row = 0; row < height; row++){Vec3b* inptr = img_rgb8.ptr<Vec3b>(row);Vec3f* outptr = img_awb.ptr<Vec3f>(row);for (int col = 0; col < width; col++){(*(outptr + col))[2] = Rgain * (*(inptr + col))[2];(*(outptr + col))[1] = Ggain * (*(inptr + col))[1];(*(outptr + col))[0] = Bgain * (*(inptr + col))[0];}}//convertScaleAbs(img_awb, img_awb);//转为CV_8UC1/*imshow("img_awb", img_awb);waitKey(0);*/return img_awb;
}

该方法运行得到的结果,通过图像监视观察函数中的图像变化,放大可以查看像素值,没有在VS安装的可以自行安装Imagewatch,安装教程:VS2022安装Image Watch插件_image watch for visual studio 2022_Aqder的博客-CSDN博客

感觉处理后的结果不太理想,不知道是不是代码有误,可指出。

(左为输入,右为输出,不是一一对应的截图):

2.完美反射法

(perfect Reflector)基于这样一种假设,一幅图像中最亮的像素相当于物体有光泽或镜面上的点,它传达了很多关于场景照明条件的信息。如果景物中有纯白的部分,那么就可以直接从这些像素中提取出光源信息。因为镜面或有光泽的平面本身不吸收光线,所以其反射的颜色即为光源的真实颜色,这是因为镜面或有光泽的平面的反射比函数在很长的一段波长范围内是保持不变的。那么在这个假设下,图像中就一定存在一个纯白色的的像素或者最亮的点。

完美反射法就是利用这种特性来对图像进行调整。算法执行时,将待检测图像中亮度最高的像素作为参考白点,以此点为基础就可计算出gain值从而进行校正。完美反射算法流程如下:
  (1)遍历原始图像,统计RGB三通道之和的直方图;
  (2)遍历原始图像,找到RGB三通道各自的最大值Bmax、Gmax、Rmax
  (3)设定比例 r ,对RGB之和的直方图进行倒叙遍历,找到使白点像素个数超过总像素个数比例的阈值,T;
  (4)遍历原始图像,计算RGB之和大于 T 的像素,各个通道取平均,得到Bavg、Gavg、Ravg;
  (5)遍历原始图像,分别计算RGB三通道的调整值Aout=A / Aavg * Amax;
  (6)防溢出处理,这里可以采用简单的截断即可。

有看到其他博文资料不使用比例和阈值的完美反射法,但是运行结果和上面一样存疑。

设定比例r为10%,使用C++,代码如下:

​
//白平衡校正,完美反射法,使用比例r和阈值T,
Mat awbreflect2(Mat img_rgb8)
{int histrgbsum[255 * 3 + 1] = { 0 };double Rmax = 0, Gmax = 0, Bmax = 0;//uchar maxrgb[3] = { 0 };for (int row = 0; row < height; row++){const uchar* inptr = img_rgb8.ptr<uchar>(row);for (int col = 0; col < width; col++){//统计RGB三通道之和的直方图int sum = *(inptr + 3 * col) + *(inptr + 3 * col + 1) + *(inptr + 3 * col + 2);histrgbsum[sum]++;//找到RGB三通道各自的最大值Bmax、Gmax、RmaxBmax = max(Bmax, (double)*(inptr + 3 * col));Gmax = max(Gmax, (double)*(inptr + 3 * col + 1));Rmax = max(Rmax, (double)*(inptr + 3 * col + 2));/*maxrgb[0] = max(maxrgb[0], *(inptr + 3 * col));maxrgb[1] = max(maxrgb[1], *(inptr + 3 * col+1));maxrgb[2] = max(maxrgb[2], *(inptr + 3 * col+2));*/}}//设定比例r为10%double num = 0,ratio=0.1;int threshold = 0;int len = 0;len=sizeof(histrgbsum) / sizeof(histrgbsum[0]);//int len = end(histrgbsum) - begin(histrgbsum);cout << "histagram length:" << len << endl;for (len; len >= 0; len--){num += histrgbsum[len];//计算R+G+B的数量超过像素总数的ratio的像素值if (num > height * width * ratio){//使白点像素个数超过总像素个数的比例时,为阈值Tthreshold = len;break;}}//计算RGB之和大于 T 的像素,对大于阈值的像素各通道取平均,得到Bavg、Gavg、Ravg;double Rsum = 0, Gsum = 0, Bsum = 0;double Ravg = 0, Gavg = 0, Bavg = 0;int pixnum = 0;for (int row = 0; row < height; row++){const uchar* inptr = img_rgb8.ptr<uchar>(row);for (int col = 0; col < width; col++){//计算RGB之和,上面的局部变量又用一遍int sum = *(inptr + 3 * col) + *(inptr + 3 * col + 1) + *(inptr + 3 * col + 2);if (sum > threshold){Bsum += *(inptr + 3 * col);Gsum += *(inptr + 3 * col + 1);Rsum += *(inptr + 3 * col + 2);pixnum++;}}}Ravg = Rsum / (double)pixnum;Gavg = Gsum / (double)pixnum;Bavg = Bsum / (double)pixnum;//创建与输入图像一样大小类型的矩阵Mat img_awb = Mat::zeros(img_rgb8.size(), img_rgb8.type());//量化0-255,重新计算RGB值,分别计算RGB三通道的调整值Aout=A / Aavg * Amaxdouble Rout = 0, Gout = 0, Bout = 0;for (int row = 0; row < height; row++){const uchar* inptr = img_rgb8.ptr<uchar>(row);uchar* outptr = img_awb.ptr<uchar>(row);for (int col = 0; col < width; col++){Bout = (double)*(inptr + 3 * col) / Bavg * Bmax;Gout = (double)*(inptr + 3 * col+1) / Gavg * Gmax;Rout = (double)*(inptr + 3 * col+2) / Ravg * Rmax;Bout = min(max((double)0, Bout), (double)255);Gout = min(max((double)0, Gout), (double)255);Rout = min(max((double)0, Rout), (double)255);//将计算好的RGB值赋给新矩阵*(outptr + 3 * col) = (uchar)Bout;*(outptr + 3 * col+1) = (uchar)Gout;*(outptr + 3 * col+2) = (uchar)Rout;}}return img_awb;}​

断点运行之后,图像监视下白平衡处理后得到的结果(左输入,右输出,不是一一对应的截图):

3.动态阈值法

YUV颜色空间(亦称YCrCb)主要用于优化彩色视频信号的传输,Y表示亮度,U和V表示色度(色调和饱和度)。亮度是通过RGB输入信号来建立的,方法是将RGB信号的特定部分叠加到一起。“色度”则定义了颜色的两个方面─色调与饱和度,分别用Cr和Cb来表示。其中,Cr反映了RGB输入信号红色部分与RGB信号亮度值之间的差异。而Cb反映的是RGB输入信号蓝色部分与RGB信号亮度值之同的差异。

动态阈值算法通过将RGB变化到YCrCb颜色空间进行分析来确定白点,其选择参考白点的阈值是动态变化的。我们通过对图片的YCrCb坐标空间的分析,可以找到一个接近白色的区域,该区域是包含着参考白点的,通过设定一个阈值来规定某些点为参考白点。因此该算法是一个动态的自适应白平衡算法。

白平衡算法通常分为两步:白色点的检测,白色点的调整。本方法采用一个动态的阀值来检测白色点。详细算法过程参考如下:

(1)把图像w*h从RGB空间转换到YCrCb空间。转换公式如下:

(2)通过限定YUV的区域来判断是否为白点,通过四个限制条件俩限制白点,满足条件的点就是白点,参与后续的计算,否则,点直接舍弃。

首先,为了增强算法的鲁棒性,将图像分为12部分,把图像分成宽高比为4:3个块(块数可选)。

然后对每个块,分别计算Cr,Cb的平均值Mr,Mb。

再对每个块,根据Mr,Mb,用下面公式分别计算Cr,Cb的方差Dr,Db。

最后判定每个块的近白区域(near-white region)。判别准则为:

其中sign为符号函数,即正数返回1,负数返回0。

设一个“参考白色点”的亮度矩阵RL,大小为w*h。若符合判别式,则作为“参考白色点”,并把该点(i,j)的亮度(Y分量)值赋给RL(i,j);若不符合,则该点的RL(i,j)值为0。

上面几步为白点检测,下面几步为白点调整:

(1)选取参考“参考白色点”中最大的10%的亮度(Y分量)值,并选取其中的最小值Lu_min;

(2)调整RL,若RL(i,j)<Lu_min, RL(i,j)=0; 否则,RL(i,j)=1;

(3)分别把R,G,B与RL相乘,得到R2,G2,B2。 分别计算R2,G2,B2的平均值,得到Rav,Gav,Bav;

(4)得到调整增益: Ymax=double(max(max(Y));

Rgain=Ymax/Rav;

Ggain=Ymax/Gav;

Bgain=Ymax/Bav;

(5)调整原图像:Ro= R*Rgain; Go= G*Ggain; Bo= B*Bgain。

实现代码本人未尝试,C++可以参考(过程较为复杂):

OpenCV图像处理专栏十一 | IEEE Xplore 2015的图像白平衡处理之动态阈值法 (qq.com)

4.其他算法

除了上面比较常见的几种,还有基于模糊逻辑,基于色温,基于边缘和多方法融合法等等。

最后,再次想吐槽csdn的发布文章的编辑页面,类似换行,空格,撤消等等,编辑与图片接近的地方经常会跳转编辑处,还很容易误删图片。有时候看别人文章,里面的一些公式,格式,图片等等,可能因为不兼容或者乱码等问题影响观看和理解,也是学习中一方面的阻碍。

写出一篇文章没想到最后的困难居然是编辑,用多了word,真心觉得这里编辑功能太不智能,需要花费更多时间。还挂着中国开发者网络的头衔,内部工作人员能不能更新升级以下这个编辑发布文章里面的页面和功能。

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

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

相关文章

WebSocket实战之四WSS配置

一、前言 上一篇文章WebSocket实战之三遇上PAC &#xff0c;碰到的问题只能上安全的WebSocket&#xff08;WSS&#xff09;才能解决&#xff0c;配置证书还是挺麻烦的&#xff0c;主要是每年都需要重新更新证书&#xff0c;我配置过的证书最长有效期也只有两年&#xff0c;搞不…

【数据结构】排序(2)—冒泡排序 快速排序

目录 一. 冒泡排序 基本思想 代码实现 时间和空间复杂度 稳定性 二. 快速排序 基本思想 代码实现 hoare法 挖坑法 前后指针法 时间和空间复杂度 稳定性 一. 冒泡排序 基本思想 冒泡排序是一种交换排序。两两比较数组元素&#xff0c;如果是逆序(即排列顺序与排序后…

定时任务管理平台青龙 QingLong

一、关于 QingLong 1.1 QingLong 介绍 青龙面板是支持 Python3、JavaScript、Shell、Typescript 多语言的定时任务管理平台&#xff0c;支持在线管理脚本和日志等。其功能丰富&#xff0c;能够满足大部分需求场景&#xff0c;值得一试。 主要功能 支持多种脚本语言&#xf…

我的企业证书是正常的但是下载应用app到手机提示无法安装“app名字”无法安装此app,因为无法验证其完整性解决方案

我的企业证书是正常的但是下载应用app到手机提示无法安装“app名字”无法安装此app&#xff0c;因为无法验证其完整性解决方案 首先&#xff0c;确保您从可信任的来源下载并安装企业开发者签名过的应用程序。如果您不确定应用程序的来源&#xff0c;建议您联系应用程序提供者…

你写过的最蠢的代码是?——AI领域的奇妙体验

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

Redis与分布式-哨兵模式

接上文 Redis与分布式-主从复制 1.哨兵模式 启动一个哨兵&#xff0c;只需要修改配置文件即可&#xff0c; sentinel monitor lbwnb 1247.0.0.1 6001 1先将所有服务关闭&#xff0c;然后修改配置文件&#xff0c;redis Master&#xff0c;redis Slave&#xff0c;redis Slave…

源码系列 之 ThreadLocal

简介 ThreadLocal的作用是做数据隔离&#xff0c;存储的变量只属于当前线程&#xff0c;相当于当前线程的局部变量&#xff0c;多线程环境下&#xff0c;不会被别的线程访问与修改。常用于存储线程私有成员变量、上下文&#xff0c;和用于同一线程&#xff0c;不同层级方法间传…

复习C语言数组的用法

实验内容 1.1设计一个函数fun&#xff0c;功能是有N*N的矩阵&#xff0c;根据给定的m值&#xff0c;m<N,将每行元素中的值&#xff0c;均往右移m个位置&#xff0c;左边置0 #include<stdio.h> void fun(int (*a)[3],int m){int n,j,i,k,num;int p2;//右移位置列数nu…

基于体素场景的摄像机穿模处理

基于上一篇一种基于体素的射线检测 使用射线处理第三人称摄像头穿模问题 基于体素的第三人称摄像机拉近简单处理 摄像机移动至碰撞点处 简单的从角色身上发射一条射线到摄像机&#xff0c;中途遇到碰撞就把摄像机移动至该碰撞点 public void UpdateDistance(float defaultDist…

OpenGL之光照贴图

我们需要拓展之前的系统,引入漫反射和镜面光贴图(Map)。这允许我们对物体的漫反射分量和镜面光分量有着更精确的控制。 漫反射贴图 我们希望通过某种方式对物体的每个片段单独设置漫反射颜色。我们仅仅是对同样的原理使用了不同的名字:其实都是使用一张覆盖物体的图像,让我…

软件测试教程 自动化测试selenium篇(二)

掌握Selenium常用的API的使用 一、webdriver API public class Main {public static void main(String[] args) {ChromeOptions options=new ChromeOptions();//参数表示允许所有请求options.addArguments("--remote-allow-origins=*");WebDriver webDriver=new Chr…

【Maven基础篇-黑马程序员】Maven项目管理从基础到高级,一次搞定!

文章目录 前言Maven简介Maven是什么Maven的作用 Maven的下载与安装Maven基础概念仓库坐标仓库配置全局setting与用户setting区别 第一个Maven程序&#xff08;手工制作&#xff09;第一个Maven程序&#xff08;IDEA生成&#xff09;使用模版&#xff08;骨架&#xff09;创建Ma…

vcruntime140.dll如何修复,快速修复vcruntime140.dll丢失的三种方法

vcruntime140.dll是Visual C 2015运行库的一个组件&#xff0c;它包含了许多运行时函数&#xff0c;用于支持各种程序的正常运行。当vcruntime140.dll文件丢失时&#xff0c;可能会导致一些程序无法正常运行。本文将详细介绍vcruntime140.dll的作用、丢失原因以及三种修复方法。…

AAD基础知识(identity/token/PRT)

简介 AAD(Azure Active Directory/Azure AD)是微软基于云身份验证和访问控制的解决方案&#xff0c;通过SSO登录其他o365应用(word/outlook/teams…) 微软在2023年7月把AAD重命名为Microsoft Entra ID&#xff0c;官网&#xff1a;https://www.microsoft.com/zh-cn/security/b…

uni-app实现图片预览

uni.previewImage预览图片 使用方法&#xff1a; <image class"poster" :src"imageUrl" mode"" click"previewImg(imageUrl)"></image>const previewImg (e) > {uni.previewImage({current: e,urls: image}); } 官…

【STM32基础 CubeMX】PWM输出

文章目录 前言一、PWM是什么&#xff1f;二、CubeMX配置PWM三、代码分析3.1 CubeMX生成代码3.2 PWM的几个库函数HAL_TIM_PWM_Start 3.3 PWM回调函数3.4 占空比占空比是什么__HAL_TIM_SET_COMPARE设置占空比 四、呼吸灯示例总结 前言 STM32微控制器是一系列功能强大的微控制器&…

FFmpeg 命令:从入门到精通 | FFmpeg 基本介绍

FFmpeg 命令&#xff1a;从入门到精通 | FFmpeg 基本介绍 FFmpeg 命令&#xff1a;从入门到精通 | FFmpeg 基本介绍FFmpeg 简介FFmpeg 基础知识复用与解复用编解码器码率和帧率 资料 FFmpeg 命令&#xff1a;从入门到精通 | FFmpeg 基本介绍 本系列文章要解决的问题&#xff1…

微信公众号开发基本流程(记录初级流程)

微信公众号开发基本流程 一、注册公众号二、了解公众号管理页面三、必备开发者工具的使用1. 开发者文档2. 在线接口调试工具3. Web开发者工具4. 公众平台测试账号 四、细读开发者文档五、开发流程重点解析1. 开发环境准备2. 服务器基本配置3. 存取access_token参数4. 公众号消息…

Go应用程序的安全最佳实践

在Go中预防漏洞、保护用户输入和防御代码注入和XSS攻击 在软件开发领域&#xff0c;安全性不是一个简单的事后考虑&#xff0c;而是建立强大和可信赖应用程序的基本基石。确保您的Go应用程序对潜在威胁具有韧性要求全面了解安全最佳实践。本指南深入探讨了您可以采取的重要措施…

基于蜉蝣优化的BP神经网络(分类应用) - 附代码

基于蜉蝣优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于蜉蝣优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.蜉蝣优化BP神经网络3.1 BP神经网络参数设置3.2 蜉蝣算法应用 4.测试结果&#xff1a;5.M…