Chapter_06 更改图像的对比度和亮度

article/2025/8/19 3:11:35

文章目录

      • 一. 本章目标
        • ① 目标
        • ② 理论
        • ③ 代码
      • 二. 实例
        • ① 亮度和对比度调整
        • ② 伽马校正
        • ③ 纠正曝光不足的图像
      • 三. 图形渲染中的伽马校正(附加资源)

一. 本章目标

① 目标

本教程中我们将学习如何:

  • 访问像素值
  • 用零初始化矩阵
  • 学习cv::saturate_cast是做什么的,以及它有什么用
  • 获取关于像素变换的一些比较酷的信息
  • 提高图像亮度的一个实际例子

② 理论

注意:

下面的解释来自Richard Szeliski的《计算机视觉:算法和应用》一书

图像处理

  1. 一般的图像处理运算符是一个函数,它接收一个或多个输入图像并生成输出图像.
  2. 图像变换可以看成是这样的
    - 点运算符(像素变换)
    - 邻域(基于区域)的运算符

像素变换

  1. 在这种图像处理变换中,每个输出像素的值仅依赖于相应的输入像素值(可能还要加上一些全局采集的信息或者参数)
  2. 这里操作的例子包括亮度和对比度的调整以及颜色校正和变换.

亮度和对比度调整:

  • 两种常用的点处理方法是乘以一个常数再加一个常数
    在这里插入图片描述

  • 通常来说 α > 0 和 β 被称为增益和偏值参数.有时候这些参数也被分别称为控制对比度和亮度参数

  • 你可以认为f(x)是源图像像素,g(x)是输出图像像素. 然后,更直观的表达式可以写成:

在这里插入图片描述
其中ij表示像素位于第i行,第’j’列.

③ 代码

  • 一下的代码执行操作 g(i,j) = α * f(i,j) + β
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
// we're NOT "using namespace std;" here, to avoid collisions between the beta variable and std::beta in c++17
using std::cin;
using std::cout;
using std::endl;
using namespace cv;int main(int argc, char **argv)
{CommandLineParser parser(argc, argv, "{@input | lena.jpg  | input image}");Mat image = imread(samples::findFile(parser.get<String>("@input")));if (image.empty()){cout << "Could not open or find the image!\n" << endl;cout << "Usage: " << argv[0] << "<Input image> " << endl;return EXIT_FAILURE;}Mat new_image = Mat::zeros(image.size(), image.type());double alpha = 1.0; /* < Simple contrast control >*/int beta = 0; /*<Simple brightness control >*/cout << "Basic Linear Transforms" << endl;cout << " --------------------------" << endl;cout << "* Enter the alpha value [1.0 - 3,0]";cin >> alpha;cout << "* Enter the beta value [0-100]";cin >> beta;for (int y = 0; y < image.rows; y++){for (int x = 0; x < image.cols; x++){for (int c = 0; c < image.channels();c++){new_image.at<Vec3b>(y,x)[c] = saturate_cast<uchar>(alpha * image.at<Vec3b>(y, x)[c] + beta);}}}imshow("Original Image", image);imshow("New Image", new_image);waitKey(0);return EXIT_SUCCESS;
}

解释

  • 我们使用cv::imread加载了一个图像,并且把它保存为Mat对象
 CommandLineParser parser( argc, argv, "{@input | lena.jpg | input image}" );Mat image = imread( samples::findFile( parser.get<String>( "@input" ) ) );if( image.empty() ){cout << "Could not open or find the image!\n" << endl;cout << "Usage: " << argv[0] << " <Input image>" << endl;return -1;}
  • 现在,由于我们要对图像进行一些变换,我们需要一个新的Mat对象来存储它.此外,我们希望它具有如下的特性:

    1. 初始像素值全部是0
    2. 大小和类型与原图像相同
 Mat new_image = Mat::zeros( image.size(), image.type() );

我们注意到cv::Mat::zeros返回一个基于image.size()image.type()matlab风格的全部为0的初始化器.

  • 我们现在要求用户输入α和β的值:
 	double alpha = 1.0; /*< Simple contrast control */int beta = 0;       /*< Simple brightness control */cout << " Basic Linear Transforms " << endl;cout << "-------------------------" << endl;cout << "* Enter the alpha value [1.0-3.0]: "; cin >> alpha;cout << "* Enter the beta value [0-100]: ";    cin >> beta;
  • 现在,为了计算g(i,j)=α⋅f(i,j)+β运算,我们访问图像中的每个像素点.由于我们使用的是BGR图像,所以每个像素有三个值(B,G和R),因此我们也将分别访问他们.下面是这段代码:
  for( int y = 0; y < image.rows; y++ ) {for( int x = 0; x < image.cols; x++ ) {for( int c = 0; c < image.channels(); c++ ) {new_image.at<Vec3b>(y,x)[c] =saturate_cast<uchar>( alpha*image.at<Vec3b>(y,x)[c] + beta );}}}

注意以下问题(仅限C++代码):

  1. 为了访问图像中的每个像素,我们使用以下语法:image.at<Vec3b>(y,x)[c],其中y是行,x是列,cB,G,R(0,2,或2)
  2. 由于操作α⋅p(i,j)+β可以给出范围外的值或不是整数(如果α是浮点数),我们使用cv::saturate_cast来确保这些值是有效的
  3. 最后,我们创建窗口并显示图像,通常的做法是:
 	imshow("Original Image", image);imshow("New Image", new_image);waitKey(0);

注意:

我们可以简单地使用以下的命令,而不是使用for循环来访问每个像素

image.convertTo(new_image, -1, alpha, beta);

其中cv::Mat::convertTo将有效地执行new_image = a * image +beta. 然而,我们想向您展示如何访问每个像素.在任何情况下,这两种方式得到的结果是相同的.但是convertTo更优化,工作速度更快.

结果:

  • 运行我们的代码并且使用 α=2.2 and β=50
$ ./BasicLinearTransforms lena.jpg
Basic Linear Transforms
-------------------------
* Enter the alpha value [1.0-3.0]: 2.2
* Enter the beta value [0-100]: 50
  • 我们得到结果:

二. 实例

在这一段中,我们将把我们所学到的通过调整图像的亮度和对比度来纠正曝光不足的图像付诸实践.我们还可以看到另外以纵横矫正图像亮度的技术,称为伽马校正.

① 亮度和对比度调整

增加(/减少)β值将会对每一个像素都增加(/较少)一个常量的像素.在[0,255]的范围之外的量会是饱和的(例如: 如果一个像素值大于(/小于)255(/0) 将会被赋值为255(/0))


浅灰色为原始图像的直方图,深灰色为在Gimp中亮度=80的时候

直方图表示每个颜色级别对应的像素数.深色图像会有很多颜色值较低的像素,因此直方图的左侧会小狐仙一个峰值.当添加一个恒定的偏差时,直方图会向右移动,因为我们已经为所有的像素添加了一个恒鼎的偏差.

α参数将改变能级的扩散方式。 如果α<1,颜色等级将被压缩,结果将是一个对比度较低的图像

浅灰色为原始图像的直方图,深灰色是在Gimp中对比度小于0的时候

请注意,这些直方图是使用Gimp软件中的亮度-对比度工具获取的.亮度工具应该与β偏置参数相同,但是对比度工具视乎和α增益不同,其中输出范文似乎以Gimp为中心(正如你可以在之前的直方图注意到的那样).

使用β偏置可以提高亮度,但同时图像会小狐仙轻微的遮盖,因为对比度会降低.α增益可以用来减少这种效果,但由于饱和度,我们将失去在原来的明亮区域的一些细节.

② 伽马校正

伽马校正可以通过使用输入值和映射输出值之间的非线性变换来矫正图像的亮度:


由于这种关系是非线性的,因此效果对所有像素都不一样,取决于它们的原始值.

绘制不同伽马值的结果图

当γ<1,原来的暗区域会变量,直方图会向右移动,而当γ>1的时候则相反.

③ 纠正曝光不足的图像

下面的图像已经被修正: α=1.3, β=40。

整体亮度提高了,但是你可以注意到,由于使用了数值饱和度(照片中的高光剪切),云层现在非常的饱和.

下面的图像被纠正: γ=0.4 .


由于映射是非线性的,并且不可能像前一种方法那样存在数值饱和,因此伽马校正应该倾向于增加较少的饱和效果.


上图比较了三幅图像的直方图(三幅直方图的y范围不相同).你可以注意到大多数像素值都位于原始图像直方图的下方. 经过α, β校正后,由于饱和导致右移,我们可以在255处观察到一个较大的峰.高兴过伽马校正后,直方图向右偏移,但暗区域的相比两区域的像素偏移更大(见伽马曲线图)

在本教程中,你已经看到了两种简单的方法来调整图像的对比度和亮度.他们是基本的技术,不打算用来代替光栅图形编辑器

代码:

伽马校正的代码

Mat lookUpTable(1,256,CU_8U);
uchar* p = lookUpTable.ptr();
for(int i = 0; i < 256;i++)p[i] = saturate_cast<uchar>(pow(i / 255.0,gamma_) * 255.0);
Mat res = img.clone();
LUT(img,lookUpTable,res);

为了提高性能,我们使用了查询表,因为一次只需要计算256个值

本教程的完整代码

#include <iostream>
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"// we're NOT "using namespace std;" here, to avoid collisions between the beta variable and std::beta in c++17
using std::cout;
using std::endl;
using namespace cv;namespace
{/* Global Variables*/int alpha = 100;int beta = 100;int gammaCor = 100;Mat imgOriginal, imgCorrected, imgGammaCorrected;void basicLinearTransform(const Mat &img, const double alpha_, const int beta_){Mat res;img.convertTo(res, -1, alpha_, beta_);hconcat(img, res, imgCorrected); // 图像拼接imshow("Brightness and contrast adjustments", imgCorrected);}void gammaCorrection(const Mat &img, const double gamma_){CV_Assert(gamma_ >= 0);//! [changing-contrast-brightness-gamma-correction]Mat lookUpTable(1, 256, CV_8U);uchar *p = lookUpTable.ptr();for (int i = 0; i < 256; i++){p[i] = saturate_cast<uchar>(pow(i / 255.0, gamma_) * 255.0);}Mat res = img.clone();LUT(img, lookUpTable, res);//! [changing-contrast-brightness-gamma-correction]hconcat(img, res, imgGammaCorrected);imshow("Gamma correction", imgGammaCorrected);}void onLinearTransformAlphaTrackbar(int, void *){double alphaValue = alpha / 100.0;int betaValue = beta - 100;basicLinearTransform(imgOriginal, alphaValue, betaValue);}void onLinearTransformBetaTrackbar(int, void *){double alphaValue = alpha / 100.0;int betaValue = beta - 100;basicLinearTransform(imgOriginal, alphaValue, betaValue);}void onGammaCorrectionTrackbar(int, void *){double gammaValue = gammaCor / 100.0;gammaCorrection(imgOriginal, gammaValue);}}int main(int argc,char** argv)
{CommandLineParser parser(argc, argv, "{@input | lena.jpg | input image}");imgOriginal = imread(samples::findFile(parser.get<String>("@input")));if (imgOriginal.empty()){cout << "Could not open or find the image!\n" << endl;cout << "Usage: " << argv[0] << "<Input image> " << endl;return EXIT_FAILURE;}imgCorrected = Mat(imgOriginal.rows, imgOriginal.cols * 2, imgOriginal.type());imgGammaCorrected = Mat(imgOriginal.rows, imgOriginal.cols * 2, imgOriginal.type());hconcat(imgOriginal, imgOriginal, imgCorrected);hconcat(imgOriginal, imgOriginal, imgGammaCorrected);namedWindow("Brightness and constrast adjustments");namedWindow("Gamma correction");createTrackbar("Alpha gain(contrast)", "Brightness and constrast adjustments", &alpha, 500, onLinearTransformAlphaTrackbar);createTrackbar("Beta bias(brightness)", "Brightness and constrast adjustments",&beta, 200, onLinearTransformBetaTrackbar);createTrackbar("Gamma correction", "Gamma correction",&gammaCor, 200, onGammaCorrectionTrackbar);onLinearTransformAlphaTrackbar(0, 0);onGammaCorrectionTrackbar(0, 0);waitKey(0);imwrite("linear_transform_correction.png", imgCorrected);imwrite("gamma_correction.png", imgGammaCorrected);return EXIT_SUCCESS;
}

三. 图形渲染中的伽马校正(附加资源)

一旦我们遇到要计算最终的像素颜色的情况,我们将不得不显示它们在显示器上.在过去的数字成像时代,大多数显示器是阴极射线管(CRT)显示器.这些显示器的物体特性是:两倍的输入电压不会导致两倍的亮度.将输入的电压增加一倍,其亮度域显示器的伽马值大致为2.2的指数关系.这恰好与人类测量亮度的方式非常的吻合,因为朗读也显示出类似的(逆)幂关系.为了更好的理解这一切意味着什么,请看下面的图片:

上面的线看起来像人眼的正确的亮度刻度,亮度翻了一番(例如从0.1到0.2)确实看起来像两倍的亮度,因为零度具有良好的一致性差异.大事,当我们谈论光的物理量度时,例如离开光源的光子数量,底部刻度实际上显示了正确的亮度.在底部的刻度中,双倍的亮度返回正确的物理亮度,但由于我们的眼睛对亮度的感知不同(更容易受深色变化的影响),所以它看起来很奇怪.

由于人眼喜欢根据高比例看到亮度颜色,显示器(至今仍是)使用幂关系来显示输出颜色,从而将原始物理强度颜色映射到最高比例中的非线性亮度颜色

这种非现象映射的监视器能够输出对于我们的眼睛来说更令人愉快的结果,但是,当谈到渲染图形的时候,有一个问题:
我们在我们的应用程序中配置的所有的颜色和亮度都是基于我们我们从显示器那里看到的,因此多有的选项都是非线性亮度/颜色.看看下面的图表:

虚线表示线性空间中的颜色/光值,实线表示显示器显示的颜色空间.如果我们在线性空间中对一种颜色加倍,其结果确实是值加倍.例如,取一个光的颜色向量(0.5,0.0,0.0),它代表一个半暗红色的光.如果我们将这个光在线性空间中加倍,它将变成(1.0,0.0,0.0),如图所示.但是从图中可以看到,原始颜色在显示器上显示为(0.218,0.0,0.0).这就是问题出现的地方:一旦我们将线性空间中的暗红色增加一倍,它在显示器上亮度就会增加4.5倍以上.

在本章之前,我们一直假设我们是在线性空间中工作,但我们实际上是在显示器的输出空间中工作,所以我们配置所有颜色和照明变量在物理上都是不正确的,而只是在显示器上看起来(有点)正确.出于这个原因,我们(以及美工)通常会将照明值设置得比实际更亮(因为显示器会将其变暗),这导致大多数线性空间计算不正确.注意显示器(CRT)和线性图开始和结束的位置相同;中间值会被显示屏变暗

因为颜色是根据显示器的输出配置的,所以线性空间中的所有中间(照明)计算在物理上都是不正确的.随着更高级的找平算法的使用,这点将变得更加明显,如下图所示:

在这里插入图片描述
你可以看到伽马校正,(更新过)的颜色值看起来结合的更漂亮一些并且深色区域显示更多的细节.总的来说,经过一些小的修改,图像质量更高.

没有正确的纠正这个显示器伽马,照明看起来错误,艺术家将会有很难得到显示和好看的结果的情况.解决办是应用伽马校正.

伽马校正

伽马校正的思想是在显示器显示之前应用显示器伽马的倒数得到最终的输出颜色.回头看本章前面的伽马曲线图,我们看到另一条虚线,它是显示器伽马曲线的倒数.我们将每一个线性输出颜色乘以这个逆伽马曲线(使得它们更亮),一旦这些颜色在显示器上显示出来,显示器的伽马曲线就被应用,得到的颜色就变成线性的.我们有效地使中间颜色变量,这样一旦显示器变暗它们,就会平衡所有颜色.

我们再举一个例子.假设我们还是深红色(0.5,0,0).在显示器显示该颜色之前,我们首先对颜色应用伽马校正曲线.显示器显示线性颜色大致是2.2的倍数,所以反比需要将颜色缩放为1/2.2的倍数.因此伽马校正后的深红色颜色变为
(0.5,0.0.0.0)^(1/2.2) = (0.73,0.0,0.0).然后,将校正后的颜色输入显示器,结果颜色为(0.73,0,0)^2.2 = (0.5,0,0).你可以看到,通过使用伽马校正,现在显示器最终显示的颜色为我们再应用程序中线性设置的那样.

2.2的伽马值是默认的伽马值,它粗略地估计了大多数显示的平均值.在颜色空间中,这个作为2.2倍伽马的结果被称为sRGB颜色空间(不是100%准确,但接近).每个显示器都有自己的伽马曲线,但是在大多数显示器上,伽马值为2.2就可以得到很好的结果.出于这个原因,游戏通常允许玩家改变游戏的伽马设置,因为每个显示器的伽马设置略有不同.

有两种方法可以对场景进行gamma校正:

  • 通过使用OpenGL内置的sRGB帧缓冲区支持
  • 通过在片段着色器中自己做伽马校正

第一个选项可能是最简单的,但是也给你较少的控制权.通过启用GL_FRAMEBUFFER_SRGB,你告诉OpenGL每个后续绘制命令应该首先gamma正确的颜色(从sRGB颜色空间),然后将它们存储在颜色缓冲区(s).sRGB是一个颜色空间,大致对应伽马2.2和大多数设备的标准.在启用GL_FRAMEBUFFER_SRGB后,
OpenGL在每个片段着色器运行到所有后续帧缓冲区(包括默认帧缓冲区)后自动执行伽马校正.

启用GL_FRAMEBUFFER_SRGB就像调用glEnable一样简单:

glEnable(GL_FRAMEBUFFER_SRGB);

从现在开始,你渲染图像将进行伽马校正,因为这是由硬件完成的,它是完全没有花费的.关于这种方法(以及其他方法),你应该记住的是伽马校正(也)将颜色从线性恐案换到非线性空间,所以只在最后一步进行伽马校正是非常重要的.如果在最终输出之前对颜色进行伽马校正,那么对这些颜色的所有后续操作都将对不正确的值进行操作.例如,如果你使用多个framebuffer,你可能希望在framebuffer之间传递的中间结果保持在线性空间中,只有最后一个framebuffer在发送给显示器之前应用伽马校正

第二种方法需要更多的工作,但是我们可以完全控制伽马操作.我们在每个相关的随便着色器运行结束时应用伽马校正,所以最终的颜色在发送到显示器之前结束伽马校正:

void main()
{// do super fancy lighting in linear space[...]// apply gamma correctionfloat gamma = 2.2;FragColor.rgb = pow(fragColor.rgb.vec3(1.0/gamma));
}

最后一行代码有效地将fragColor的每个单独的颜色提到到1.0/gamma,就正这个片段着色器运行的输出颜色.

这种方法的一个问题是,为了保持一致,你必须对每个碎片着色器应用伽马校正,这有助于最终输出.如果你有12个碎片着色器用于多个对象,你必须添加gamma校正代码到每个这些着色器.一个更简单的解决方案是在你的渲染循环中引入一个后期处理截断,并在后期处理的四边形上应用伽马校正作为最后一步,你只需要做一次.

这一行代表了伽马校正的技术实现.不是所有的都太令人印象深刻,但是又一些额外的事情,你必须考虑再做伽马校正.

sRGB 纹理
因为显示器使用伽马显示颜色,所以每当你在计算机上画画,编辑,绘制一幅图像时,你都是根据在显示器看到的内容选择颜色.这充分说明你创建或编辑的所有图芯片不是在线性空间,而是在sRGB空间,例如根据感知到的亮度再屏幕上加倍暗红色,不等于加倍红色分量.

因此,当纹理美术师通过眼睛创造图像时,所有纹理的值都在sRGB空间中,所以如果我们在渲染应用中使用了这些纹理,我们必须考虑到这一点.在我们知道伽马校正之前,这并不是一个真正的问题,因为纹理在sRGB空间中看起来很好,这也是我们工作的空间:纹理显示完全一样,这是好的,然而,现在我们再线性空间中显示所有内容,纹理颜色将关闭,如下图所示:

纹理图像太亮了,这是因为它实际上被伽马校正了两次! 想想看,当我们基于在显示器上看到的内容创阿金图像时,我们有效地对图像的颜色进行了伽马校正,以便它在监视器上看起来正确.因为我们渲染器中再次使用了伽马校正,图像最终变得太亮了.

为了解决这个问题,我们必须确保纹理艺术家在线性空间工作.然而,由于在sRGB空间中工作更容易,而且大多数工具甚至不正确地支持线性纹理,这可能不是首选的解决方案.

另一个解决方案是在对它们的颜色进行任何计算之前,重新纠正或者转换这些sRGB纹理到线性空间.我们可以这样做:

float gamma = 2.2;
vec3 diffuseColor = pow(texture(diffuse,texCoords).rgb,vec3(gamma));

sRGB空间中的每个纹理都这样做事相当麻烦的.幸运的是,OpenGL给了我们另一个解决方案,通过给我们GL_SRGBGL_SRGB_ALPHA内部纹理格式

如果我们用这两种sRGB纹理格式中的任何一种在OpenGL中创建纹理,OpenGL将在我们使用它们的时候自动将颜色纠正到线性空间,允许我们正确地在线性空间中工作.我们可以如下方式指定一个纹理为sRGB纹理:

glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);  

如果你还想在纹理中包含alpha组件,你必须指定纹理的内部格式为GL_SRGB_ALPHA.

当你再sRGB空间中指定纹理时,你应该小心,因为不是所有的纹理实际上都会在sRGB中.用于着色对象的纹理(如漫反射纹理)几乎总是在sRGB空间中.用于检索照明参数的纹理(如高光贴图和法线贴图)几乎总是在线性空间中,所以如果你将这些配置为sRGB纹理,照明将看起来很奇怪.在指定为sRGB的纹理时要小心.

我们的漫反射纹理指定为sRGB纹理,你会再次得到你期望的视觉输出,但是这一次所有的伽马校正就只有一次.

弱化
伽马校正的另一个不同之处是光照的弱化.在真实的物理世界中,光纤的衰减与光源距离的平方成反比.在普通的英语中,它的意思是光的强度随着光源距离的平方而减小,如下图所示:

float attenuation = 1.0 / (distance * distance);

然而,在使用这个方程时,衰减效应通常太强烈了,使得光线的半径很小,看起来不太对.出于这个原因,我们使用了其他的衰减函数(就像我们再基础照明章节中讨论的)来提供更多的控制,或者使用线性变换:

float attenuation = 1.0 / distance;  

与没有伽马校正的二次变换相比,线性等效式给出了更可信的结果,但当我们进行伽马校正的时候,线性衰减看起来太弱,而无力上正确的二次衰减会突然给出更好的结果.下图显示了不同之处:

在这里插入图片描述
造成这种差异的原因是光衰减函数会改变亮度,因为我们不能在线性空间中可视化我们的场景,所以我们选择了再显示器上看起来最好的衰减函数,但物理上并不正确.考虑衰减函数的平方:如果我们使用这个函数而不进行伽马校正,衰减函数在显示器上显示时有效地变成:(1.0/distance^2)^2.2.这与我们最初的预期相比造成了更大的弱化.这也解释了为什么线性等价在没有伽马校正的情况下更有意义,因为它有效地变成了(1.0/distance)^2.2 = 1.0 / distanec^2.2,这与它的物理等价更近似.

总的来说,gamma校正允许我们在线性空间中进行所有的着色器/照明计算.因为线性空间在物理世界中是有意义的,大多数物理方程在实际上给出了很好的结果(比如真实的光衰减).你的照明越先进,就越容器得到好看(和现实)的伽马校正结果.这也是为什么它建议只调整你的照明阐述一旦你用伽马校正到位.


http://chatgpt.dhexx.cn/article/KACm74s6.shtml

相关文章

MATLAB和Intislim,matlab灰阶变换函数imadjust和stretchlim的c++实现

灰阶变换 首先介绍一下灰阶变换&#xff0c;一幅图像数据本身在存储的数据结构上为一个二维的矩阵&#xff0c;即一幅图像为m*n个密密麻麻的像素点构成。 image.png 然后&#xff0c;这些像素点有着一个值&#xff0c;这个值成为灰度值或者亮度值&#xff0c;值的范围为[0,255]…

一个app开发周期是多久?快速开发才是主流

app的出现&#xff0c;极大的方便了人们的生活、社交和工作&#xff0c;各种各样的app为人们提供了各种便利的服务&#xff0c;真正让移动互联网服务大众。许多行业看到在app上爆发出的巨大潜能&#xff0c;都纷纷主动进行app制作来迎接移动互联网浪潮。 开发app一个很重的问题…

如何开发app软件?程序员揭秘你还没听过的1种方法

如何开发app软件&#xff1f;app软件开发需要多少钱&#xff1f;现在手机app这么火&#xff0c;很多企业都想开发一个app软件开拓市场。但是在开发app的时候往往会感觉力不从心&#xff0c;因为app开发门槛比较高&#xff0c;大家对app软件开发需要的技术、成本并不了解。 现…

商城APP软件开发要素有哪些

商城APP软件体系是当今电商行业经常会运用的商城体系&#xff0c;既支撑企业对企业的运营形式&#xff0c;也支撑卖家对消费者的运营形式&#xff0c;便是现在常见的B2C&#xff0c;B2B的电商形式APP&#xff0c;跟着现在的互联网的遍及现在这些电商都是成为了电商商家开展的不…

app软件怎么开发 盘点3种app制作方式

现在智能手机给大家的生活带来了很多便利&#xff0c;利用手机&#xff0c;随时随地看新闻、手机打车提前预定、网上订餐不用等、商家服务提前预约、学习内容随时看、购物更是随时买。各式各样的手机App&#xff0c;让生活方式方式了重大改变。 1、组建团队开发APP 自己有团队…

app应用软件开发流程是怎样的?

从入职这行业到创业已有 7 载&#xff0c;对 APP 产品开发的流程已经再熟悉不过了&#xff0c;现在把这几年积累的一些经验和大家分享一下&#xff0c;一个产品是怎么从想法一步一步落地为具体成品的&#xff0c;这个过程中会经历一些怎样的必要流程呢&#xff0c;下面大致说一…

三阶魔方傻瓜指南

三阶魔方20分钟完全自救指南——包学包会 前言 寒假宅在家里&#xff0c;闲来无事&#xff0c;偶得一个三阶魔方&#xff0c;便从网上找公式摸索。发现了很多版本&#xff0c;但是大多局部最优&#xff0c;缺少易于小白理解的全局最优方法。所以做次总结&#xff0c;集各家所…

C/C++FPS实战CSGO矩阵方框透视骨骼自瞄实战教程

C/CFPS实战CSGO矩阵方框透视骨骼自瞄实战教程

基于yolo5制作的csgo,ai自瞄

研究了几天&#xff0c;终于肝出来了&#xff0c;基本功能完美运行&#xff0c;晚点在训练一个专用模型。

警惕AI,我搭建了一个“枪枪爆头”的视觉AI自瞄程序,却引发了一场“山雨欲来”

前言 前段时间在网上看到《警惕AI外挂&#xff01;我写了一个枪枪爆头的视觉AI&#xff0c;又亲手“杀死”了它》 这个视频&#xff0c;引起了我极大的兴趣。 视频中提到&#xff0c;在国外有人给使命召唤做了个AI程序来实现自动瞄准功能。它跟传统外挂不一样&#xff0c;该程…

理解FPS游戏中的矩阵方框透视自瞄

​ 初识矩阵 其实矩阵是线性代数里面的说法 矩阵&#xff08;Matrix&#xff09;是一个按照长方阵列排列的复数或实数集合 [1] &#xff0c;最早来自于方程组的系数及常数所构成的方阵。这一概念由19世纪英国数学家凯利首先提出。 类似&#xff1a; 矩阵的乘法&#xff1a; …

手把手教你如何0基础编写基于AI机器视觉的--王者荣耀百里守约的自瞄程序,我家村头的傻X都能学会的教程!

先看看我在B站的效果视频 王者荣耀-百里守约-AI辅助自瞄研究 摘要&#xff1a;YoLo目标检测框架 PaddleDetection机器学习框架 不介绍上面的框架了&#xff0c;直入主题&#xff1a; 以下教程基于Windows10系统环境&#xff0c;Win7应该不太行。 考虑到大多数学生比较穷&a…

yolov5实现机器视觉ai自瞄,本人跑代码训练时总结下来的坑(参数设置,服务器使用,自动打标签,训练速度,显存使用率...)

想到啥些啥&#xff0c;都是些我遇到的&#xff0c;很坑&#xff0c;但偏偏又有点蠢的问题。 路过进来的朋友可以 ctrlF 搜一下有没有自己苦恼的问题。 1&#xff0c;训练的模型使用越小&#xff08;最小是yolov5n&#xff09;&#xff0c;帧数越高&#xff0c;自瞄间隔越短。…

[原创]FPS网络游戏自动瞄准自瞄外挂漏洞分析以及实现

0x0. 前言 来到论坛已经有一段时间了,目前大三学生,乐于分享知识,看到论坛招聘讲师的帖子,于是想发点文字,分享知识,不知道能不能申请精华。 新年刚刚过去,不知道大家亲戚走没走完,新的一年里,祝大家财源广进,身体健康,万事如意,一切都会好起来的! 0x1. 了解FPS…

基于yolov7的FPS游戏(csgo,cf,cfhd)自瞄开发

1.训练yolo识别人物导出pt文件 链接: yolov7训练自己的数据集-gpu版 2.使用win32进行屏幕截图和鼠标移动 3.使用导出的pt文件进行推理(pt文件将在完善后发出) 屏幕截图获取屏幕->检测目标的坐标->取中心点->计算距离获取最近的敌人坐标->移动鼠标到中心点 数据集…

穿越火线(CF) AI 自瞄 代码 权重 数据集 亲测可用(结尾有资源)

初衷 本人热衷玩CF&#xff0c;同时为一名程序员&#xff0c;近期听说AI霸占FPS游戏&#xff0c;本着学习的态度&#xff0c;特来测试 不喜欢看过程的小伙伴直接看最下面 模型 采用yolov5模型架构 对过程感兴趣的小伙伴下文自行学习 https://zhuanlan.zhihu.com/p/17212138…

通过YOLOV5实现:王者荣耀百里守约自瞄

前期提要&#xff1a; 本文章仅供技术讨论使用。 关于如何通过YOLOV5去检测到王者中的敌方人物&#xff0c;在网上有很多相关的文章和教学视频我在这里就不过多的阐述&#xff0c;本篇文章主要讲的是在实现中比较难处理的一些技术点&#xff1a;如何获取高刷新率的手机屏幕、…

Ai实现FPS游戏自动瞄准 yolov5fps自瞄

大家好 我是毕加锁 (锁!) 今天来分享一个Yolov5 FPS跟枪的源码解析和原理讲解。代码比较粗糙 各位有什么优化的方式可以留言指出&#xff0c;可以一起交流学习。 需要了解的东西和可能会遇到的问题1.xy坐标点与当前鼠标的xy坐标点距离计算 2.获取窗口句柄&#xff0c;本文使…

[CPU+目标检测] openvino实现Robomaster自瞄

这篇文章为大连理工大学Robomaster凌Bug战队的李乐恒同学成果&#xff01; 他在CPU上利用openvino这样的深度学习算法实现了Robomaster的自瞄&#xff0c;大大提高了robomaster自瞄的上界&#xff0c;且达到了良好的检测效果。所有代码全部开源&#xff0c; github主页如下&am…

基于C++的车辆装甲板检测自瞄系统

资源下载地址&#xff1a;https://download.csdn.net/download/sheziqiong/85773209 资源下载地址&#xff1a;https://download.csdn.net/download/sheziqiong/85773209 装甲板检测 由于在比赛过程中操作手是第一视角&#xff0c;很难用手动瞄准。通过装甲板检测就是自瞄系统…