opencv图像去畸变

news2024/9/23 7:31:06

图像去畸变的思路

对于目标图像(无畸变图像)上的每个像素点,转换到normalize平面,再进行畸变变换,进行投影,得到这个像素点畸变后的位置,然后将这个位置的源图像(畸变图像)的像素值作为目标图像该点的像素值。

通常我们得到的原图是畸变后的图像(x_distort,y_distort),要计算畸变之前的真实图像(x,y),不是用逆运算,而是计算真实图像畸变后会投影在哪,对应过去。先把原图像设置为一个空的图像,把一个个像素畸变投影过去,找到和畸变后图像像素点的对应关系

鱼眼相机模型

世界坐标系内的点转换到相机坐标系

X_c = RX_w + t

转到归一化平面

 

对归一化平面内的点,进行畸变变换

 

畸变模型

径向畸变:桶形或者鱼眼

切向畸变:镜片安装和成像平面不平行引起的。

undistort

 注意,OpenCV畸变系数矩阵的默认顺序 [k1, k2, p1, p2, k3]

#include <opencv2/opencv.hpp>
#include <chrono>
using namespace std;

int main(int argc, char **argv)
{
  // 内参
  double fx = 458.654, fy = 457.296, cx = 367.215, cy = 248.375;
  /**内参矩阵K
   * fx  0  cx
   * 0  fy  cy
   * 0   0   1
   */
  // 畸变参数
  double k1 = -0.28340811, k2 = 0.07395907, p1 = 0.00019359, p2 = 1.76187114e-05;

  cv::Mat image = cv::imread(argv[1], 0); // 图像是灰度图,CV_8UC1
  int rows = image.rows, cols = image.cols;
  cv::Mat image_undistort = cv::Mat(rows, cols, CV_8UC1); // 方法1去畸变以后的图
  cv::Mat image_undistort2 = cv::Mat(rows, cols, CV_8UC1);  // 方法2 OpenCV去畸变以后的图



  chrono::steady_clock::time_point t1 = chrono::steady_clock::now();
  //! 方法1. 自己写计算去畸变后图像的内容
  for (int v = 0; v < rows; v++)
  {
    for (int u = 0; u < cols; u++)
    {
      double x = (u - cx) / fx, y = (v - cy) / fy; //要求解的真实图,归一化平面上的坐标
      double r = sqrt(x * x + y * y);
      double x_distorted = x * (1 + k1 * r * r + k2 * r * r * r * r) + 2 * p1 * x * y + p2 * (r * r + 2 * x * x); //畸变后归一化坐标
      double y_distorted = y * (1 + k1 * r * r + k2 * r * r * r * r) + p1 * (r * r + 2 * y * y) + 2 * p2 * x * y;
      double u_distorted = fx * x_distorted + cx; //畸变后像素坐标,即原图
      double v_distorted = fy * y_distorted + cy;
      // 投影赋值
      if (u_distorted >= 0 && v_distorted >= 0 && u_distorted < cols && v_distorted < rows) //真实图畸变后仍然在图上的
      {
        image_undistort.at<uchar>(v, u) = image.at<uchar>((int)v_distorted, (int)u_distorted);
      }
      else
      {
        image_undistort.at<uchar>(v, u) = 0; //这里最好用插值法
      }
    }
  }
  chrono::steady_clock::time_point t2 = chrono::steady_clock::now();
  chrono::duration<double> time_used = chrono::duration_cast<chrono::duration<double>>(t2 - t1);
  cout << "time = " << time_used.count() << endl;






  //! 方法2. OpenCV自带的undistort函数,更快速
  cv::Mat K = cv::Mat::eye(3, 3, CV_32FC1); //内参矩阵
  K.at<float>(0, 0) = fx;
  K.at<float>(1, 1) = fy;
  K.at<float>(0, 2) = cx;
  K.at<float>(1, 2) = cy;
  cv::Mat distort_coeffs = cv::Mat::zeros(1, 5, CV_32FC1); //畸变系数矩阵 顺序是[k1, k2, p1, p2, k3]
  distort_coeffs.at<float>(0, 0) = k1;
  distort_coeffs.at<float>(0, 1) = k2;
  distort_coeffs.at<float>(0, 2) = p1;
  distort_coeffs.at<float>(0, 3) = p2;
  cout << "K = " << endl
       << K << endl;
  cout << "distort_coeffs = " << endl
       << distort_coeffs << endl;

  t1 = chrono::steady_clock::now();
  cv::undistort(image, image_undistort2, K, distort_coeffs); //去畸变
  t2 = chrono::steady_clock::now();
  time_used = chrono::duration_cast<chrono::duration<double>>(t2 - t1);
  cout << "time = " << time_used.count() << endl;

  // 展示去畸变后图像
  cv::imshow("distorted", image);
  cv::imshow("undistorted", image_undistort);
  cv::imshow("image_undistort2", image_undistort2);
  cv::waitKey(0);
  return 0;
}

undistortPoints

 

if (_distCoeffs) 
{
	// compensate tilt distortion
	cv::Vec3d vecUntilt = invMatTilt * cv::Vec3d(x, y, 1);
	double invProj = vecUntilt(2) ? 1. / vecUntilt(2) : 1;
	x0 = x = invProj * vecUntilt(0);
	y0 = y = invProj * vecUntilt(1);

	double error = 1.7976931348623158e+308;
	// compensate distortion iteratively

	//注1: 开始循环迭代去除畸变
	for (int j = 0;; j++)
	{
		//注2:一个是最大迭代次数条件
		if ((criteria.type & cv::TermCriteria::COUNT) && j >= criteria.maxCount)
			break;
		//注3:一个是迭代最大误差条件
		if ((criteria.type & cv::TermCriteria::EPS) && error < criteria.epsilon)
			break;
			
		//注4:在只有k1,k2,k3,p1,p2共5个畸变参数时,对应的k数组中只有k[0]~k[4]有值,其他都为0
		double r2 = x*x + y*y;
		double icdist = (1 + ((k[7] * r2 + k[6])*r2 + k[5])*r2) / (1 + ((k[4] * r2 + k[1])*r2 + k[0])*r2);
		double deltaX = 2 * k[2] * x*y + k[3] * (r2 + 2 * x*x) + k[8] * r2 + k[9] * r2*r2;
		double deltaY = k[2] * (r2 + 2 * y*y) + 2 * k[3] * x*y + k[10] * r2 + k[11] * r2*r2;
		//注5:形如式(8)的迭代
		x = (x0 - deltaX)*icdist;
		y = (y0 - deltaY)*icdist;

		if (criteria.type & cv::TermCriteria::EPS)
		{
			double r4, r6, a1, a2, a3, cdist, icdist2;
			double xd, yd, xd0, yd0;
			cv::Vec3d vecTilt;

			//注6:将第k次计算的无畸变点代入畸变模型,得到于当前有畸变的偏差
			r2 = x*x + y*y;
			r4 = r2*r2;
			r6 = r4*r2;
			a1 = 2 * x*y;
			a2 = r2 + 2 * x*x;
			a3 = r2 + 2 * y*y;
			cdist = 1 + k[0] * r2 + k[1] * r4 + k[4] * r6;
			icdist2 = 1. / (1 + k[5] * r2 + k[6] * r4 + k[7] * r6);
			xd0 = x*cdist*icdist2 + k[2] * a1 + k[3] * a2 + k[8] * r2 + k[9] * r4;
			yd0 = y*cdist*icdist2 + k[2] * a3 + k[3] * a1 + k[10] * r2 + k[11] * r4;

			vecTilt = matTilt*cv::Vec3d(xd0, yd0, 1);
			invProj = vecTilt(2) ? 1. / vecTilt(2) : 1;
			xd = invProj * vecTilt(0);
			yd = invProj * vecTilt(1);

			double x_proj = xd*fx + cx;
			double y_proj = yd*fy + cy;

			error = sqrt(pow(x_proj - u, 2) + pow(y_proj - v, 2));
		}
	}
}

void cvUndistortPointsInternal( const CvMat* _src, CvMat* _dst, const CvMat* _cameraMatrix,
                   const CvMat* _distCoeffs,
                   const CvMat* matR, const CvMat* matP, cv::TermCriteria criteria)
{
	// 判断迭代条件是否有效
    CV_Assert(criteria.isValid());
	// 定义中间变量--A相机内参数组,和matA共享内存;RR-矫正变换数组,和_RR共享内存
	// k-畸变系数数组
    double A[3][3], RR[3][3], k[14]={0,0,0,0,0,0,0,0,0,0,0,0,0,0};
    CvMat matA=cvMat(3, 3, CV_64F, A), _Dk;
    CvMat _RR=cvMat(3, 3, CV_64F, RR);
    cv::Matx33d invMatTilt = cv::Matx33d::eye();
    cv::Matx33d matTilt = cv::Matx33d::eye();
	
	// 检查输入变量是否有效
    CV_Assert( CV_IS_MAT(_src) && CV_IS_MAT(_dst) &&
        (_src->rows == 1 || _src->cols == 1) &&
        (_dst->rows == 1 || _dst->cols == 1) &&
        _src->cols + _src->rows - 1 == _dst->rows + _dst->cols - 1 &&
        (CV_MAT_TYPE(_src->type) == CV_32FC2 || CV_MAT_TYPE(_src->type) == CV_64FC2) &&
        (CV_MAT_TYPE(_dst->type) == CV_32FC2 || CV_MAT_TYPE(_dst->type) == CV_64FC2));
 
    CV_Assert( CV_IS_MAT(_cameraMatrix) &&
        _cameraMatrix->rows == 3 && _cameraMatrix->cols == 3 );
 
    cvConvert( _cameraMatrix, &matA );// _cameraMatrix <--> matA / A
 
	// 判断输入的畸变系数是否有效
    if( _distCoeffs )
    {
        CV_Assert( CV_IS_MAT(_distCoeffs) &&
            (_distCoeffs->rows == 1 || _distCoeffs->cols == 1) &&
            (_distCoeffs->rows*_distCoeffs->cols == 4 ||
             _distCoeffs->rows*_distCoeffs->cols == 5 ||
             _distCoeffs->rows*_distCoeffs->cols == 8 ||
             _distCoeffs->rows*_distCoeffs->cols == 12 ||
             _distCoeffs->rows*_distCoeffs->cols == 14));
 
        _Dk = cvMat( _distCoeffs->rows, _distCoeffs->cols,
            CV_MAKETYPE(CV_64F,CV_MAT_CN(_distCoeffs->type)), k);// _Dk和数组k共享内存指针
 
        cvConvert( _distCoeffs, &_Dk );
        if (k[12] != 0 || k[13] != 0)
        {
            cv::detail::computeTiltProjectionMatrix<double>(k[12], k[13], NULL, NULL, NULL, &invMatTilt);
            cv::detail::computeTiltProjectionMatrix<double>(k[12], k[13], &matTilt, NULL, NULL);
        }
    }
 
    if( matR )
    {
        CV_Assert( CV_IS_MAT(matR) && matR->rows == 3 && matR->cols == 3 );
        cvConvert( matR, &_RR );// matR和_RR共享内存指针
    }
    else
        cvSetIdentity(&_RR);
 
    if( matP )
    {
        double PP[3][3];
        CvMat _P3x3, _PP=cvMat(3, 3, CV_64F, PP);
        CV_Assert( CV_IS_MAT(matP) && matP->rows == 3 && (matP->cols == 3 || matP->cols == 4));
        cvConvert( cvGetCols(matP, &_P3x3, 0, 3), &_PP );// _PP和数组PP共享内存指针
        cvMatMul( &_PP, &_RR, &_RR );// _RR=_PP*_RR 放在一起计算比较高效
    }
 
    const CvPoint2D32f* srcf = (const CvPoint2D32f*)_src->data.ptr;
    const CvPoint2D64f* srcd = (const CvPoint2D64f*)_src->data.ptr;
    CvPoint2D32f* dstf = (CvPoint2D32f*)_dst->data.ptr;
    CvPoint2D64f* dstd = (CvPoint2D64f*)_dst->data.ptr;
    int stype = CV_MAT_TYPE(_src->type);
    int dtype = CV_MAT_TYPE(_dst->type);
    int sstep = _src->rows == 1 ? 1 : _src->step/CV_ELEM_SIZE(stype);
    int dstep = _dst->rows == 1 ? 1 : _dst->step/CV_ELEM_SIZE(dtype);
 
    double fx = A[0][0];
    double fy = A[1][1];
    double ifx = 1./fx;
    double ify = 1./fy;
    double cx = A[0][2];
    double cy = A[1][2];
 
    int n = _src->rows + _src->cols - 1;
	// 开始对所有点开始遍历
    for( int i = 0; i < n; i++ )
    {
        double x, y, x0 = 0, y0 = 0, u, v;
        if( stype == CV_32FC2 )
        {
            x = srcf[i*sstep].x;
            y = srcf[i*sstep].y;
        }
        else
        {
            x = srcd[i*sstep].x;
            y = srcd[i*sstep].y;
        }
        u = x; v = y;
        x = (x - cx)*ifx;//转换到归一化图像坐标系(含有畸变)
        y = (y - cy)*ify;
 
		//进行畸变矫正
        if( _distCoeffs ) {
            // compensate tilt distortion--该部分系数用来弥补沙氏镜头畸变??
			// 如果不懂也没管,因为普通镜头中没有这些畸变系数
            cv::Vec3d vecUntilt = invMatTilt * cv::Vec3d(x, y, 1);
            double invProj = vecUntilt(2) ? 1./vecUntilt(2) : 1;
            x0 = x = invProj * vecUntilt(0);
            y0 = y = invProj * vecUntilt(1);
 
            double error = std::numeric_limits<double>::max();// error设定为系统最大值
            // compensate distortion iteratively
			// 迭代去除镜头畸变
			// 迭代公式    x′= (x−2p1 xy−p2 (r^2 + 2x^2))∕( 1 + k1*r^2 + k2*r^4 + k3*r^6)
			//             y′= (y−2p2 xy−p1 (r^2 + 2y^2))∕( 1 + k1*r^2 + k2*r^4 + k3*r^6)
 
            for( int j = 0; ; j++ )
            {
                if ((criteria.type & cv::TermCriteria::COUNT) && j >= criteria.maxCount)// 迭代最大次数为5次
                    break;
                if ((criteria.type & cv::TermCriteria::EPS) && error < criteria.epsilon)// 迭代误差阈值为0.01
                    break;
                double r2 = x*x + y*y;
                double icdist = (1 + ((k[7]*r2 + k[6])*r2 + k[5])*r2)/(1 + ((k[4]*r2 + k[1])*r2 + k[0])*r2);
                double deltaX = 2*k[2]*x*y + k[3]*(r2 + 2*x*x)+ k[8]*r2+k[9]*r2*r2;
                double deltaY = k[2]*(r2 + 2*y*y) + 2*k[3]*x*y+ k[10]*r2+k[11]*r2*r2;
                x = (x0 - deltaX)*icdist;
                y = (y0 - deltaY)*icdist;
 
				// 对当前迭代的坐标加畸变,计算误差error用于判断迭代条件
                if(criteria.type & cv::TermCriteria::EPS)
                {
                    double r4, r6, a1, a2, a3, cdist, icdist2;
                    double xd, yd, xd0, yd0;
                    cv::Vec3d vecTilt;
 
                    r2 = x*x + y*y;
                    r4 = r2*r2;
                    r6 = r4*r2;
                    a1 = 2*x*y;
                    a2 = r2 + 2*x*x;
                    a3 = r2 + 2*y*y;
                    cdist = 1 + k[0]*r2 + k[1]*r4 + k[4]*r6;
                    icdist2 = 1./(1 + k[5]*r2 + k[6]*r4 + k[7]*r6);
                    xd0 = x*cdist*icdist2 + k[2]*a1 + k[3]*a2 + k[8]*r2+k[9]*r4;
                    yd0 = y*cdist*icdist2 + k[2]*a3 + k[3]*a1 + k[10]*r2+k[11]*r4;
 
                    vecTilt = matTilt*cv::Vec3d(xd0, yd0, 1);
                    invProj = vecTilt(2) ? 1./vecTilt(2) : 1;
                    xd = invProj * vecTilt(0);
                    yd = invProj * vecTilt(1);
 
                    double x_proj = xd*fx + cx;
                    double y_proj = yd*fy + cy;
 
                    error = sqrt( pow(x_proj - u, 2) + pow(y_proj - v, 2) );
                }
            }
        }
		// 将坐标从归一化图像坐标系转换到成像平面坐标系
        double xx = RR[0][0]*x + RR[0][1]*y + RR[0][2];
        double yy = RR[1][0]*x + RR[1][1]*y + RR[1][2];
        double ww = 1./(RR[2][0]*x + RR[2][1]*y + RR[2][2]);
        x = xx*ww;
        y = yy*ww;
 
        if( dtype == CV_32FC2 )
        {
            dstf[i*dstep].x = (float)x;
            dstf[i*dstep].y = (float)y;
        }
        else
        {
            dstd[i*dstep].x = x;
            dstd[i*dstep].y = y;
        }
    }
}

畸变矫正

通过calibrate Camera()得到的内参和畸变系数

图像的畸变矫正需要相机的内参和畸变系数, 在opencv中, 有以下两个函数可以实现:

  • initUndistortRectifyMap() + remap()函数
  • undistort()函数

推荐使用第一种方法:initUndistortRectifyMap只用运行一次,remap读取一次图像运行一次。

void initUndistortRectifyMap( InputArray cameraMatrix, InputArray distCoeffs,
InputArray R, InputArray newCameraMatrix, Size size, int m1type, OutputArray map1, OutputArray map2 );

参数说明:
cameraMatrix——输入的摄像头内参数矩阵(3X3矩阵)
distCoeffs——输入的摄像头畸变系数矩阵(5X1矩阵)
R——输入的第一和第二摄像头坐标系之间的旋转矩阵
newCameraMatrix——输入的校正后的3X3摄像机矩阵
size——摄像头采集的无失真图像尺寸
m1type——map1的数据类型,可以是CV_32FC1或CV_16SC2
map1——输出的X坐标重映射参数
map2——输出的Y坐标重映射参数
 

void remap(InputArray src, OutputArray dst, InputArray map1, InputArray map2, int interpolation, int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())

参数说明:
src——输入图像,即原图像,需要单通道8位或者浮点类型的图像
dst(c++)——输出图像,即目标图像,需和原图形一样的尺寸和类型
map1——它有两种可能表示的对象:(1)表示点(x,y)的第一个映射;(2)表示CV_16SC2,CV_32FC1等
map2——有两种可能表示的对象:(1)若map1表示点(x,y)时,这个参数不代表任何值;(2)表示 CV_16UC1,CV_32FC1类型的Y值
intermap2polation——插值方式,有四中插值方式:
(1)INTER_NEAREST——最近邻插值
(2)INTER_LINEAR——双线性插值(默认)
(3)INTER_CUBIC——双三样条插值(默认)
(4)INTER_LANCZOS4——lanczos插值(默认)

intborderMode——边界模式,默认BORDER_CONSTANT
borderValue——边界颜色,默认Scalar()黑色
 

projectPoints

先将世界坐标系内的点转换到相机坐标系、图像坐标系、像素坐标系。

然后再利用undistortPoint对这个点畸变矫正。

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

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

相关文章

Visual Studio 2022安装与编译简单c语言以及C#语言(番外)

文章目录1 软件下载网站2 下载与安装3 创建并学习C语言4 创建并学习C#语言1 软件下载网站 Visual Studio官网 2 下载与安装 1、下载社区版即可。 2、下载得到安装文件&#xff0c;右键以管理员方式运行安装文件。 3、点击继续。 4、等待下载完成。 5、这里学习C选择使用…

SpringBoot文件上传同时,接收复杂参数

目录 环境信息 问题描述 错误分析 解决方法 简单参数 总结 环境信息 Spring Boot&#xff1a;2.0.8.RELEASE Spring Boot内置的tomcat&#xff1a;tomcat-embed-core 8.5.37 问题描述 收到文件上传的开发工作&#xff0c;要求能适配各种场景&#xff0c;并且各场景的请求…

C语言——操作符详解(上)

C语言——操作符详解&#xff08;上&#xff09; 操作符的分类 C语言中的操作符主要分为算术操作符、移位操作符、位操作符、赋值操作符、单目操作符、关系操作符、逻辑操作符、条件操作符、逗号表达式、下标引用、函数调用和结构成员。我将分成三篇文章为大家详细介绍以上所…

[附源码]Python计算机毕业设计Django网约车智能接单规划小程序

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

[附源码]Python计算机毕业设计华夏商场红酒管理系统Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等…

AI绘画火爆,以昆仑万维AIGC为例,揭秘AI绘画背后的模型算法

AI绘画火爆&#xff0c;以昆仑万维AIGC为例&#xff0c;揭秘AI绘画背后的模型算法 一、前言 最近AI绘画让人工智能再次走进大众视野。在人工智能发展早起&#xff0c;一直认为人工智能能实现的功能非常有限。通常都是些死板的东西&#xff0c;像是下棋、问答之类的&#xff0…

mysql锁范围(一)表级锁变行级锁

文章目录行级锁1. 用两个连接connection登陆mysql2. 测试无索引情况1&#xff09;机器1开启事务&#xff0c;执行更新北京仓数据sql&#xff0c;不提交事务2&#xff09;机器2开启事务&#xff0c;先查询北京仓3&#xff09;机器2开始更新上海仓数据4&#xff09;机器1事务回滚…

【Spring Cloud】Nacos服务分级存储模型与负载均衡原理与实战

本期目录1. 服务分级模型介绍2. 服务分级模型的必要性3. 配置集群属性4. NacosRule负载均衡4.1 背景描述4.2 配置Nacos负载均衡策略4.3 根据权重负载均衡1. 服务分级模型介绍 为了提升整个系统的容灾性&#xff0c;Nacos 引入了地域 (Zone) 的概念&#xff0c;如上图中的北京、…

Reactor 和 Proactor 区别

Reactor 和 Proactor 区别 同步异步、阻塞非阻塞组合 同步 以read()函数为例&#xff0c;int n read(fd, buf. sz) 当采用同步的方式和阻塞io的方式时&#xff0c;buf就是从内核拷贝的数据&#xff0c;函数返回则可以马上知道 buf 中的数据。当采用同步的方式和非阻塞io的方式…

关于rabbitmq消息推送的小demo

目录 一.前言 1.1场景 1.2消息交换机三种形式 二.建设demo工程 2.1 依赖 2.2yml文件指定rabbitmq连接信息 2.3直连型消息链接 一.前言 1.1场景 在我们实际开发中到一个特定的时候是比如工作流到某个状态时, 我们会向某某单位发送消息, 这时就会用到我们的消息推送---ra…

javaee之Mybatis2

一、保存操作 在做这个方法之前&#xff0c;我们先把之前做的那个MybatisTest里面的每一个方法做成一个Test方法&#xff0c;也就是标注Test这个注解 这样便于我们测试接下来的每一个方法。仔细分析一下上面的代码&#xff0c;会发现&#xff0c;可重复性的地方太多。比如我们…

两台linux服务器rsync自动备份文件

检查rsycn是否安装 检查方法&#xff1a;rpm -qa rsync 出现rsync 包名就是安装了 安装rsycn rsync的安装可以使用yum直接安装&#xff1a;yum install rsync rsycn的服务端/文件接收端配置 1、先创建备份目录 mkdir /data/xsbak2、服务端需要开启rsyncd服务&#xff0c;添加…

接口测试(九)—— Git代码托管、jenkins 的持续集成

目录 一、持续集成 二、git 1、简介和安装 2、Gitee 2.1 git 和 gitee 管理代码工作原理 2.2 PyCharm 配置 Gitee 插件 3、PyCharm 与 Gitee 相关操作 3.1 将 Gitee的项目 Checkout到 Pycharm中 3.2 推送 PyCharm 新项目到 Gitee远程仓库 3.3 将 Pycharm代码 push到 …

React基础知识(组件实例三大核心属性state、props、refs)(二)

系列文章目录 第一章&#xff1a;React基础知识&#xff08;React基本使用、JSX语法、React模块化与组件化&#xff09;&#xff08;一&#xff09; 文章目录系列文章目录一、State1.1. state基本使用1.2 state的简写形式二、Props2.1 props的基本使用2.2 props属性值限制2.3 …

精品基于SSM的小学生课程资源网络云平台

《基于SSM的小学生课程资源网络云平台》该项目含有源码、论文等资料、配套开发软件、软件安装教程、项目发布教程等 使用技术&#xff1a; 开发语言&#xff1a;Java 框架&#xff1a;ssm 技术&#xff1a;JSP JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据…

redux

文章目录redux是什么redux使用情况redux工作原理redux安装redux的3个核心概念Action——形成动作Reducers——操作状态store——存储状态APIredux的响应式redux实现加减案例不使用ActionCreators实现使用ActionCreators实现异步Actionreact-redux使用容器组件创建修改App.jsx在…

干电池升压IC3.3V的电源芯片fs2111/FS2112

干电池升压3.3V的电源芯片FS2111/FS2112 FS2111/FS2112适用于一节干电池升压到3.3V&#xff0c;两节干电池升压3.3V的升压电路&#xff0c;FS2111/FS2112干电池升压IC。 FS2111/FS2112 干电池1.5V和两节干电池3V升压到3.3V的测试数据 两节干电池输出500MA测试&#xff1a; F…

CubeMax添加Rtthread操作系统 组件STM32F103

CubeMax添加Rtthread操作系统 组件STM32F103 本篇主要介绍&#xff0c;怎么使用STM32CubeMx工具&#xff0c;添加RT-Thread操作系统组件&#xff0c;码代码的IDE是keil。快速移植RT-Thread实时操作系统&#xff0c;所用的IDE可自行官网下载最新版。 CubeMax官网下载链接 RTthre…

Ajax(六)

1. XMLHttpRequest的基本使用——URL编码与解码 1.1 什么是URL编码 1.2 如何对URL进行编码与解码 <body><script>var str 黑马程序员//对str编码var str2 encodeURI(str)console.log(str2)//一个汉字对应三个百分号&#xff0c;反解码从console里头复制console.…

js解决单线程之路 - worker的使用分析

写在前面 今天写一个关于实现多线程的东西&#xff0c;都知道js是一个单线程的语言&#xff0c;所谓的单线程就是一次只能做一件事&#xff0c;多线程就是一次可以做很多件事&#xff0c;当然目前我们的电脑等设备很少会有单线程了&#xff0c;比如我们的电脑一般都是标的6核12…