一次实践:给自己的手机摄像头进行相机标定

文章目录

  • 1. 问题引入
  • 2. 准备工作
    • 2.1 标定场
    • 2.2 相机拍摄
  • 3. 基本原理
    • 3.1 成像原理
    • 3.2 畸变校正
  • 4. 标定解算
    • 4.1 代码实现
    • 4.2 详细解析
      • 4.2.1 解算实现
      • 4.2.2 提取点位
    • 4.3 解算结果
  • 5. 问题补充

1. 问题引入

不得不说,现在的计算机视觉技术已经发展到足够成熟的阶段了,还记得笔者刚工作的时候,相机标定还是个很神秘的技术,只有少数专业人员能够做,网上也找不到什么相关的资料。但是现在相机标定已经是一个非常普遍的技术了,也有不少的资料的可以参考,因此笔者突发奇想,既然那些大部头的相机可以标定,那么我们使用的手机摄像头一定也可以标定。因此,笔者就记录一下给自己手机摄像头的具体实践,算是弥补下当年没有学习到该技术的遗憾,毕竟要学习一项技术最好的办法就是亲自实践一下。

2. 准备工作

2.1 标定场

笔者见过不少正规的标定场,有的标定场很大,有很多带有标志物的竖条,还带有载动相机设备的轨道。不过目前比较流行且成本最低的办法就是使用棋盘格标定板了,也就是所谓的张正友标定法。

那么棋盘格标定板哪里来呢?打印到纸上倒是一个办法,不过可能有两个问题,一个是打印后每个格子的尺寸需要换算一下,由像素换成米制单位,这可能不是一个整数;另一个就是得找一面墙来贴上去,要贴的光滑平整还是挺难的。因此笔者没有选择这个办法,最后还是通过网上购物找的标定板。由于是给手机摄像头传感器尺寸都不是太大,标定板也不用选择太大,笔者最终选用的标定板尺寸如下所示:

图1:标定板尺寸

每个格子是5毫米,一共12X9个格子,整体尺寸还是比较小巧的,大概就一个手掌心大小。材质是玻璃基板,成本大概是50元左右。这个尺寸笔者实际体验还是有点偏小的,不过再大成本就上来了,建议有财力的同学可以适当选择大一点。

2.2 相机拍摄

接下来就是用手机摄像头对棋盘格标定板进行拍摄了。理论上进行标定解算只需要6组控制点就可以了,但是因为识别的控制点都是有误差的,需要多组点位来进行求解以提升精度。只拍摄一张照片获得的控制点也不太够,通常还需要获取多张照片上的控制点,避免局部最优的问题,提高解算过程的可靠性。如果可以的话,要使用多个视角、多个不同距离的标定板照片,同时最好保证标定板覆盖整个图像平面的不同区域,这样可以更好地估计畸变和其他参数。

在这里笔者拍摄了6张棋盘格标定板的图片,分别是前、后、左、右、上、下6个不同的位置和视角,如下所示:

图2:拍摄的棋盘格图片

可以看到拍摄的标定板区域都太靠中间了,不过也是没办法,使用的标定板尺寸确实有点偏小。拍摄的时候一旦靠的很近,手机拍照程序就会自动切换成近景拍摄。笔者不太确定切换成近景拍摄之后会不会修改相机的参数,所以都没有靠的很近。但是太远了拍照又有点糊,只能使用目前这样的效果。

现在很多手机拍照的功能会自动修正照片,比如滤镜,广角矫正等等,这些功能都尽量关了或者不使用。另外,拍照过程不要进行调焦,具体来说相机上会有0.6x、1x、2x、3x这样的参数,这代表变焦倍率,使用原始倍率(1x)进行标定即可。自动对焦功能当然也要关闭,保持镜头和焦平面的位置不变。

还有个问题是保持手机不动移动标定板来拍摄照片,还是保持标定板不动移动手机来拍摄照片?应该来说,两者原理上都可以实现,但是标定板不动,相机移动更常见一点,因为实现起来更见简单。笔者就是将棋盘格标定板通过双面胶粘在墙上实现的,也算是组成了一个成本最低的微型标定场了。

其实笔者也试过将标定板放在桌面上来拍摄,不过在室内拍摄很容易在照片上有影子,还是固定在墙上比较好一点。而且最好放采光比较好的墙面上,在白天日照充足的时候进行拍摄,以便获得最好的拍摄效果。

3. 基本原理

3.1 成像原理

相机标定虽然解算的是内参,但是其实连外参也解算了,因为相机标定解算使用的相机成像原理,这个过程中内参和外参会一起参与解算。在不考虑畸变的情况下,相机的成像原理可用下式(1)来表示:

s [ u v 1 ] = K [ R ∣ t ] [ X w Y w Z w 1 ] (1) s \begin{bmatrix} u\\ v\\ 1\\ \end{bmatrix} = K \begin{bmatrix} R|t\\ \end{bmatrix} \begin{bmatrix} X_w\\ Y_w\\ Z_w\\ 1\\ \end{bmatrix} \tag{1} s uv1 =K[Rt] XwYwZw1 (1)

在这个式子中:

  • [ X w Y w Z w ] T {\begin{bmatrix}X_w & Y_w & Z_w\\\end{bmatrix}}^T [XwYwZw]T表示世界空间中的三维点,也称为物方点。
  • [ u v ] T {\begin{bmatrix}u & v\\\end{bmatrix}}^T [uv]T表示图像平面上的像素坐标,也称为像点。
  • [ R ∣ t ] \begin{bmatrix}R|t\\\end{bmatrix} [Rt]是相机的外参矩阵。具体来说,就是旋转变换和平移变换的组合, R R R就是3X3的旋转矩阵, t t t则是一个3列维向量。由于旋转变换可以用欧拉角来表示,因而也可以表示成3维向量。3个旋转量,3个平移量,这就是相机的6个外参的由来。
  • K K K是相机的内参矩阵,通常表示为下式(2):
    K = [ f x 0 c x 0 f y c y 0 0 1 ] (2) K = \begin{bmatrix} f_x & 0 & c_x\\ 0 & f_y & c_y\\ 0 & 0 & 1\\ \end{bmatrix} \tag{2} K= fx000fy0cxcy1 (2)
    • f x f_x fx f y f_y fy分别是水平方向和垂直方向的焦距,单位为像素。
    • c x c_x cx c y c_y cy是像主点(即成像平面的光轴交点)坐标,单位为像素。
  • s s s是比例因子,这个参数是为了实现齐次坐标的转换,将其次三维坐标需要转为二维坐标。

以笔者的见识来说,上述相机成像原理其实与其他学科的一些知识有类似的地方:

  1. 计算机图形学。图形渲染中的几何变换,包含模型(model)变换、视图(view)变换和投影(projection)变换 ,合起来就是通常所说的MVP矩阵。模型变换包括旋转变换和平移变换,视图变换又是模型变换的逆变换,对应的就是式(1)的外参矩阵 [ R ∣ t ] \begin{bmatrix}R|t\\\end{bmatrix} [Rt]。不过投影矩阵有所不同,式(1)的内参矩阵 K K K是将点从相机坐标系转换为图像坐标系,图形渲染中的投影矩阵则是将点从将点从相机坐标系转换为裁剪坐标系。

  2. 摄影测量学。在摄影测量学中,这一套成像原理的公式被总结为共线方程,除了表示的形式不同,最显著的不同是内参只有三个:焦距和像主点二维坐标。这个公式个人认为并不太直观,但是比较容易进行平差计算。

如果有以上两者经验的读者,可以对照着进行理解,虽然它们看起来有点差异,但是笔者确定它们的原理都是一样的,都是基于空间的几何变换,只不过是应对于不同情况有不同的描述。

3.2 畸变校正

以上成像原理没有考虑到畸变的影响。为什么会产生畸变呢?很简单,相机镜头不是完美的平面光学系统,光线在传输时发生复杂的弯曲,这会导致图像中的直线在图像边缘发生扭曲。常见的畸变有径向畸变和切向畸变。

畸变校正看起来很玄乎,其实说穿了也非常简单,我们只需要理解一点,畸变校采用的有理函数的模型。所谓有理函数的模型,就是将校正前的位置x与校正后的位置y使用一个高阶多项式(形如 y = a x 3 + b x 2 + c x + d y=ax^3+bx^2+cx+d y=ax3+bx2+cx+d)来进行表示,没有什么物理上的原理,就是纯采用数学方式进行拟合,最后得到了每个高阶项的系数(a,b,c,d)。

鉴于畸变校正会增加对标定解算的复杂度,这里就不进行进一步论述了。对于初学者来说,理解成像原理的公式(1)更为关键一点。

4. 标定解算

4.1 代码实现

使用上述介绍的基本原理就可以进行标定解算了,不过解算方法比较复杂,我们还是结合具体的实现来解释,代码如下所示,这里主要使用了OpenCV库:

#include <filesystem>
#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>

#ifdef _WIN32
#include <Windows.h>
#endif

using namespace cv;
using namespace std;

int main() {
#ifdef _WIN32
  SetConsoleOutputCP(65001);
#endif

  vector<std::filesystem::path> imgPaths = {
      "C:/Work/CalibrateCamera/Data/front.jpg",
      "C:/Work/CalibrateCamera/Data/left.jpg",
      "C:/Work/CalibrateCamera/Data/right.jpg",
      "C:/Work/CalibrateCamera/Data/up.jpg",
      "C:/Work/CalibrateCamera/Data/down.jpg",
      "C:/Work/CalibrateCamera/Data/back.jpg"};
  size_t imageNum = imgPaths.size();

  // 定义棋盘格尺寸 (内角点数)
  int boardWidth = 11;  // 列数
  int boardHeight = 8;  // 行数
  cv::Size boardSize(boardWidth, boardHeight);

  double cellSize = 0.005;

  Size imageSize(3072, 4096);  // 图像尺寸

  // 准备标定所需的物方点和像方点
  vector<vector<Point3f>> objectPoints(imageNum);  // 多张图像的3D物方点
  vector<vector<Point2f>> imagePoints(imageNum);   // 多张图像的2D像方点

  for (size_t ii = 0; ii < imageNum; ++ii) {
    // 加载棋盘格图像
    cv::Mat image = cv::imread(imgPaths[ii].string().c_str());
    if (image.empty()) {
      std::cerr << "Error: Could not load image!" << std::endl;
      return -1;
    }

    // 存储角点坐标
    std::vector<cv::Point2f> corners;

    // 转换图像为灰度
    cv::Mat grayImage;
    cv::cvtColor(image, grayImage, cv::COLOR_BGR2GRAY);

    // 寻找棋盘格角点
    // cv::CALIB_CB_ADAPTIVE_THRESH | cv::CALIB_CB_NORMALIZE_IMAGE
    bool found = cv::findChessboardCorners(grayImage, boardSize, corners,
                                           cv::CALIB_CB_FAST_CHECK);

    // 如果找到角点,进行进一步处理
    if (found) {
      std::cout << "Chessboard corners found!" << std::endl;

      // 增加角点的精度
      cv::cornerSubPix(
          grayImage, corners, cv::Size(11, 11), cv::Size(-1, -1),
          cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::MAX_ITER,
                           30, 0.001));

      // 绘制角点
      std::string cornerImgPath = imgPaths[ii].parent_path().generic_string() +
                                  "/corner/" + imgPaths[ii].stem().string() +
                                  "_corner" + imgPaths[ii].extension().string();
      cv::drawChessboardCorners(image, boardSize, corners, found);
      cv::imwrite(cornerImgPath.c_str(), image);

      cout << corners.size() << endl;
      imagePoints[ii].resize(corners.size());

      for (size_t ci = 0; ci < corners.size(); ++ci) {
        imagePoints[ii][ci] = corners[ci];
      }

      objectPoints[ii].resize(corners.size());
      for (int hi = 0; hi < boardHeight; ++hi) {
        for (int wi = 0; wi < boardWidth; ++wi) {
          int ci = hi * boardWidth + wi;
          objectPoints[ii][ci].x = cellSize * wi;
          objectPoints[ii][ci].y = cellSize * hi;
          objectPoints[ii][ci].z = 0;
        }
      }
    } else {
      std::cerr << "Chessboard corners not found!" << std::endl;
    }
  }

  // 内参矩阵和畸变系数
  Mat cameraMatrix = Mat::eye(3, 3, CV_64F);  // 初始化为单位矩阵
  Mat distCoeffs = Mat::zeros(8, 1, CV_64F);  // 初始化为零

  // 外参的旋转和位移向量
  vector<Mat> rvecs, tvecs;

  // 执行标定
  double reprojectionError =
      calibrateCamera(objectPoints, imagePoints, imageSize, cameraMatrix,
                      distCoeffs, rvecs, tvecs);

  cout << u8"重投影误差:" << reprojectionError << endl;
  cout << u8"内参矩阵:" << cameraMatrix << endl;
  cout << u8"畸变系数:" << distCoeffs << endl;

  return 0;
}

4.2 详细解析

4.2.1 解算实现

代码实现的步骤很简单,就是通过函数findChessboardCorners提取棋盘格图片的角点,将其传入calibrateCamera函数中,就得到了最终的解算成果,也就是内参矩阵。这其中的关键就在于calibrateCamera这个函数,我们可以看一下它的函数原型:

CV_EXPORTS_W double calibrateCamera( InputArrayOfArrays objectPoints,
                                     InputArrayOfArrays imagePoints, Size imageSize,
                                     InputOutputArray cameraMatrix, InputOutputArray distCoeffs,
                                     OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs,
                                     int flags = 0, TermCriteria criteria = TermCriteria(
                                        TermCriteria::COUNT + TermCriteria::EPS, 30, DBL_EPSILON) );

其参数详解如下:

  • objectPoints:3D空间中的物方点坐标集合,也就是公式(1)中的 [ X w Y w Z w ] T {\begin{bmatrix}X_w & Y_w & Z_w\\\end{bmatrix}}^T [XwYwZw]T。由于是多张图片的多组点的集合,所以它的类型实际是std::vector<std::vector<cv::Point3f>>
  • imagePoints:图像中的像素坐标集合,对应公式(1)中的 [ u v ] T {\begin{bmatrix}u & v\\\end{bmatrix}}^T [uv]T,类型同样应该也是双重数组std::vector<std::vector<cv::Point2f>>
  • imageSize:输入图像的尺寸(宽度和高度),单位为像素。
  • cameraMatrix:输出的摄像机内参矩阵,也就是公式(1)中的 K K K,为3X3矩阵。
  • distCoeffs:输出的摄像机的畸变系数,通常为1X5或1X8的向量,包含径向和切向畸变系数。
  • rvecs:输出的旋转向量集合,可以转换成公式(1)中的 R R R。每个旋转向量对应一个图像,所以类型是std::vector<cv::Mat>
  • tvecs:输出的平移向量集合,对应公式(1)中的 t t t。每个平移向量对应一个图像,类型也是std::vector<cv::Mat>
  • 返回值:标定的重投影误差,用于衡量标定结果的精确度。误差越小,标定结果越准确。

通过对calibrateCamera函数的解析,相信读者就很容易明白为什么笔者要先讲公式(1)的成像原理。这个解算参数的输入输出都是根据公式(1)来的,不过另一个问题来了,输入的物方点和像方点是怎么来的呢?

4.2.2 提取点位

答案很简单,就是棋盘格上的角点。棋盘格由黑白相间的格子组成,所以它的角点是很容易提取的;另外一方面,棋盘格也是规整的,只要每个格子的尺寸都是一样,就很容易知道物方坐标。理论上,只要对图像提取角点,然后剔除掉非棋盘的角点就可以作为相机标定的像点了。不过,OpenCV提供了更进一步的接口findChessboardCorners,直接输入棋盘格的内角点个数,就可以自动检测出像点。如下图所示在一张图片上笔者提取的像点:

图3: 提取棋盘格的角点作为像点

正如上图所示,findChessboardCorners提取的是内角点,例如12X9的棋盘格,提取的内角点是11X8个,并且结果是按照从左到右,从上往下进行排序的。为什么要这么排序呢?因为很容易帮我们算出物方点。在相机标定这个应用中,相机的外参是不重要的,因此我们可以就以棋盘格标定板的左上角作为世界坐标系的原点,第1个点的坐标是(0,0,0),第2个点的坐标是(0.005,0,0),第3个点坐标是(0.010,0,0)…第12个点坐标是(0,0.005,0),第13个点坐标是(0.005,0.005,0)…就这么依次类推得到所有角点对应的世界空间坐标系坐标。

另外一点要提醒读者的是,findChessboardCorners这里我配置的是参数是cv::CALIB_CB_FAST_CHECK,是一种快速算法,cv::CALIB_CB_ADAPTIVE_THRESH和cv::CALIB_CB_NORMALIZE_IMAGE会对图像作预处理,能够增加提取棋盘格角点的稳健性。但是我实际使用发现程序卡住了,不知道是效率很低还是OpenCV的问题,就没有使用这两个选项。

4.3 解算结果

最终,笔者的结算结果如下所示:

重投影误差:0.166339
内参矩阵:[2885.695162446343, 0, 1535.720945173723;
 0, 2885.371543143629, 2053.122840953737;
 0, 0, 1]
畸变系数:[0.181362004467736;
 -3.970106972775221;
 0.0005157812878172198;
 0.0004644406171824815;
 23.559069196518]

解算的结果重投影误差是0.166339,表示每个物体点在重新投影到图像上时与实际检测到的角点位置的误差为0.166339像素。通常来说,这样的误差已经算是非常小,表明标定结果较为精确。

不过笔者还考虑一个问题,误差为0.166339像素,那么具体是多少米呢?以前做测绘软件的时候,平差的结果也是以像素为单位,总会有客户对我发起灵魂拷问:那具体是多少米呢?这次笔者也关注了一下这个问题,个人认为在相机标定这样的应用场景,确实无法直接使用物理单位表示精度,因为这个算法的结果就在于重投影到图像上的像素差为量度,这一点与相机外参的定向的误差量度有所不同。

对照内参矩阵,可得解算的焦距是 f x = 2885.695 f_x=2885.695 fx=2885.695 f y = 2885.372 f_y=2885.372 fy=2885.372,单位也是像素。那么这个焦距换算成物理单位是多少米呢?根据笔者查找的资料显示,焦距在像素和毫米之间的转换公式如下所示:
焦距(毫米) = 焦距(像素) × 传感器尺寸(毫米) 图像分辨率(像素) 焦距(毫米)= \frac{焦距(像素)×传感器尺寸(毫米)}{图像分辨率(像素)} 焦距(毫米)=图像分辨率(像素)焦距(像素)×传感器尺寸(毫米)

也就是说与相机传感器尺寸有关,不过关于传感器尺寸的描述有点蛋疼,比如网上显示我手机摄像头的传感器是1/1.49英寸,这通常表示传感器的对角线长度。可以根据对角线长度加上宽高比(例如4:3还是16:9)算出相机传感器的物理尺寸,进而知道具体物理单位的焦距值大小。不过传感器的对角线长度标称值和真实物理尺寸之间,会因为行业惯例和历史标准有所差异,所以算出来的也不一定正确,最好还是联系官方来确定。不过,标定出像素单位的焦距已经足够后续满足后续的使用场景了,笔者这里也就是寻根究底一下。

5. 问题补充

最后,补充一些没搞定或者暂时没理解的问题吧:

  1. 关于成像原理列出的公式(1)的内参矩阵部分,其实笔者也没弄清楚为什么将焦距分成X方向上的 f x f_x fx和y方向上的 f y f_y fy,有些资料上的内参矩阵并不是这么列的,《摄影测量学》教材上列出的共线方程更是只有一个焦距值 f f f
  2. 笔者记得似乎有个操作“相机重标定”,可以将使用固定焦距 f f f,调整像主点到图像中心,以及消除畸变的重投影,可以简化后续的空间计算,使得计算更为便捷。时间关系就留待后续研究了。
  3. 本文笔者并没有具体解释解算的算法原理,因为这不是一两句话就能说清楚的,在测绘学中有个过程有个专门的名词叫做平差;或者叫做状态估计、最大似然估计、非线性优化等等,至少我们需要知道最小二乘法原理才能继续论述这个,就留待后续的文章中进行讨论吧。

列出一些文章以供参考:

  1. 相机标定:从入门到实战
  2. 相机系列——相机标定简述
  3. 相机标定之张正友标定法数学原理详解
  4. 计算机视觉----相机标定

本文源代码和数据地址

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

用于视觉的MetaFormer基线模型

摘要 https://arxiv.org/pdf/2210.13452 摘要——MetaFormer&#xff0c;即Transformer的抽象架构&#xff0c;已被发现在实现竞争性能中发挥着重要作用。在本文中&#xff0c;我们再次通过将研究重点从令牌混合器&#xff08;token mixer&#xff09;设计转移开&#xff0c;来…

leetcode每日一题day19(24.9.29)——买票需要的时间

思路&#xff1a;对于位置k在队伍最末尾时&#xff0c;位置k前的人对于某一个人需要等的时间为 ​​​​​​​min(tickets[k],tickets[i]) 对于当前的人前方的的人可以使用上述策略&#xff0c;但对于后方由于当前的人有更高的优先&#xff0c;而导致情况有所不…

51单片机应用开发---keil 创建一个新工程并用Protues 8仿真(以点亮LED为例)

实现目标 1、掌握keil V5软件 创建一个新工程&#xff1b; 2、具体目标&#xff1a;1.会新建一个工程&#xff1b;2.编程实现点亮开发板的LED1. 一、新建工程步骤 1.1 在桌面上新建一个名字为 LED的文件夹 1.2 双击打开Keil uVision5 软件&#xff0c;点击 Project —>…

目标检测技术的发展:从R-CNN、YOLO到DETR、DINO

“深度人工智能”是成都深度智谷科技旗下的人工智能教育机构订阅号&#xff0c;主要分享人工智能的基础知识、技术发展、学习经验等。此外&#xff0c;订阅号还为大家提供了人工智能的培训学习服务和人工智能证书的报考服务&#xff0c;欢迎大家前来咨询&#xff0c;实现自己的…

修改Kali Linux的镜像网站

由于官方的镜像可能会出现连接不上的问题导致无法安装我们所需要的包&#xff0c;所以需要切换镜像站为国内的&#xff0c;以下是一些国内常用的Kali Linux镜像网站&#xff0c;它们提供了与Kali Linux官方网站相同的软件包和资源&#xff0c;但访问速度更快&#xff1a; #官方…

【CKA】二、节点管理-设置节点不可用

2、节点管理-设置节点不可用 1. 考题内容&#xff1a; 2. 答题思路&#xff1a; 先设置节点不可用&#xff0c;然后驱逐节点上的pod 这道题就两条命令&#xff0c;直接背熟就行。 也可以查看帮助 kubectl cordon -h kubectl drain -h 参数详情&#xff1a; –delete-empty…

User-Agent在WebMagic爬虫中的重要性

对于需要从网站上抓取数据的开发者来说&#xff0c;WebMagic是一个强大的工具。它是一个简单灵活的Java爬虫框架&#xff0c;用于抓取网页数据。在爬虫技术中&#xff0c;User-Agent&#xff08;用户代理&#xff09;是一个关键的HTTP请求头&#xff0c;它告诉服务器关于客户端…

LeetCode 面试经典150题 50.Pow(x,n)

题目&#xff1a;实现 pow(x, n) &#xff0c;即计算 x 的整数 n 次幂函数&#xff08;即&#xff0c; &#xff09;。 思路&#xff1a; 代码&#xff1a; class Solution {public double myPow(double x, int n) {double ans 1;long N n;if (N < 0) {N -N;x 1 / x;}…

YOLOv11改进策略【损失函数篇】| Shape-IoU:考虑边界框形状和尺度的更精确度量

一、本文介绍 本文记录的是改进YOLOv11的损失函数&#xff0c;将其替换成Shape-IoU。现有边界框回归方法通常考虑真实GT&#xff08;Ground Truth&#xff09;框与预测框之间的几何关系&#xff0c;通过边界框的相对位置和形状计算损失&#xff0c;但忽略了边界框本身的形状和…

机器学习模型评估

前言 承接上篇讲述了机器学习有哪些常见的模型算法&#xff0c;以及适用的场景&#xff0c;本篇将继续讲述如何评估模型。模型评估的目的是选出泛化能力强的模型。泛化能力强表示模型能很好地适用于未知的样本&#xff0c;模型的错误率低、精度高。本文将从评估方法和评估指标…

汽车3d动画渲染选择哪个?选择最佳云渲染解决方案

面临汽车3D动画渲染挑战&#xff1f;选择正确的云渲染服务至关重要。探索最佳解决方案&#xff0c;优化渲染效率&#xff0c;快速呈现逼真动画。 汽车3d动画渲染选择哪个&#xff1f; 对于汽车3D动画渲染&#xff0c;选择哪个渲染器取决于你的项目需求、预算和期望的效果。Ble…

计算机毕业设计 基于协同过滤算法的个性化音乐推荐系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

<<机器学习实战>>10-11节笔记:生成器与线性回归手动实现

10生成器与python实现 如果是曲线规律的数据集&#xff0c;则需要把模型变复杂。如果是噪音较大&#xff0c;则需要做特征工程。 随机种子的知识点补充&#xff1a; 根据不同库中的随机过程&#xff0c;需要用对应的随机种子&#xff1a; 比如 llist(range(5)) random.shuf…

Ubuntu下安装向日葵:闪退

下载 https://sunlogin.oray.com/download 初次安装 $ sudo dpkg -i SunloginClient_15.2.0.63064_amd64.deb 正在选中未选择的软件包 sunloginclient。 (正在读取数据库 ... 系统当前共安装有 234281 个文件和目录。) 准备解压 SunloginClient_15.2.0.63064_amd64.deb ..…

助农小程序|助农扶贫系统|基于java的助农扶贫系统小程序设计与实现(源码+数据库+文档)

助农扶贫系统小程序 目录 基于java的助农扶贫系统小程序设计与实现 一、前言 二、系统功能设计 三、系统实现 5.1.1 农户管理 5.1.2 用户管理 5.1.3 订单统计 5.2.1 商品信息管理 5.3.1 商品信息 5.3.2 订单信息 5.3.3 商品评价 5.3.4 商品退货 四、数据库设计 1、…

帝都程序猿十二时辰

前言 2019年度国产剧《长安十二时辰》火了&#xff0c;其口碑榜首、节奏紧凑、贴合原著、电影质感&#xff0c;都是这部剧的亮点。而最令人震撼的还是剧中对大唐盛世的还原&#xff0c;长安街坊的市容市貌、长安百姓的生活日常、长安风情的美轮美奂……而关于十二时辰的话题也接…

ubuntu18.04 Anconda安装及使用

1、安装Anaconda 1)下载&#xff1a; 下载链接&#xff1a;https://www.anaconda.com/download#downloads 点击图中Free Download&#xff0c;登录并下在 下载对应版本 2&#xff09;安装 sudo bash Anaconda3-2024.06-1-Linux-x86_64.sh输入后&#xff0c;直接回车安装。 出…

大数据-156 Apache Druid 案例实战 Scala Kafka 订单统计

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

【SQL】筛选字符串与正则表达式

目录 语法 需求 示例 分析 代码 语法 SELECT column1, column2, ... FROM table_name WHERE condition; WHERE 子句用于指定过滤条件&#xff0c;以限制从数据库表中检索的数据。当你执行一个查询时&#xff0c;WHERE 子句允许你筛选出满足特定条件的记录。如果记录满…

UE学习篇ContentExample解读------Blueprints Advanced-下

文章目录 总览描述批次阅览2.1 Timeline animation2.2 Actor tracking2.3 Button Trigger using a blueprint interface2.4 Opening door with trigger2.5 Child Blueprints 概念总结致谢&#xff1a; 总览描述 打开关卡后&#xff0c;引入眼帘的就是针对关卡的总体性文字描述&…