The sixth week of programming learning (4.3-4.6/4.14-4.16)

Table of contents

The first day 4.3 Monday Regression, DBH, empirical model

Day 2 4.4 Tuesday Regression Indicators

Day 3 4.5 Wednesday Pillow library, point cloud format, analytical and numerical solutions

The fourth day, 4.6 Thursday, two point cloud overlay methods

The fifth day 4.14 Friday QPainter, QT basic operation

The sixth day 4.15 Saturday QT line chart time axis

The seventh day, 4.16 Sunday, the abscissa changes Octree

Summary for this week:


The first day 4.3 Monday Regression, DBH, empirical model

Today, I mainly completed the scene use and distinction of the regression method, as well as the least square method and the complete least square method.

1. Main regression methods and their adaptation scenarios

  1. Linear regression: It is suitable for scenarios where there is a linear relationship between input variables and output variables, and is usually used to establish mathematical models, predict and explore the relationship between data. Among them, the least squares method and the complete least squares method are suitable for scenarios where there are measurement errors, and the value of the output variable can be predicted by fitting the data.

  2. Polynomial regression: It is suitable for scenarios where there is a nonlinear relationship between input variables and output variables, and more complex relationships can be fitted by adding high-order terms.

  3. Ridge regression: It is suitable for scenarios where there is multicollinearity or the number of variables is greater than the number of samples. The complexity of the model can be controlled by applying an L2 penalty term to the regression coefficients.

  4. Minimum angle regression: It is suitable for scenarios where there are a large number of correlated variables. By introducing an elastic network, variable selection and model optimization can be realized at the same time.

  5. Lasso regression and Elastic Net regression: suitable for scenarios that require feature selection and model simplification, which can be achieved by applying an L1 penalty term to the regression coefficient.

  6. Principal component regression: It is suitable for dealing with multicollinearity problems and high-dimensional data problems, and regression analysis can be performed after dimensionality reduction through principal component analysis.

  7. Bayesian regression: It is suitable for scenarios where the posterior distribution and uncertainty of regression coefficients need to be considered. Bayesian statistical inference can be used to obtain the probability distribution and confidence interval of regression coefficients.

  8. Decision tree regression: suitable for scenarios dealing with nonlinear relationships and interactions, divides the input space into multiple regions, and fits a local linear regression model in each region.

  9. Random forest regression: It is suitable for scenarios dealing with overfitting and high-dimensional data problems. By building multiple decision trees, random feature selection and self-service resampling techniques can be used to obtain a more robust regression model.

  10. Gradient Boosting Regression: Applicable to scenarios that require a more accurate regression model. By iteratively adding new regression trees, the prediction results of each regression tree are getting closer to the real value.

2. The learning sequence and suggested learning method of the regression method:

  1. Linear Regression: Linear regression is the basis of regression analysis and the basis of other regression algorithms. You can learn linear algebra and least squares first, and then learn the concept and implementation of linear regression.

  2. Polynomial Regression: Polynomial regression is an extension of linear regression that can handle nonlinear problems. It is recommended to learn polynomial regression after learning linear regression.

  3. Ridge Regression and Lasso Regression: These two regression methods are extensions of regularized linear regression to prevent overfitting. It is recommended to learn the basic concepts of linear regression and regularization first, and then learn the implementation methods of ridge regression and Lasso regression.

  4. Elastic Net Regression: Elastic Net Regression is a combination of Ridge Regression and Lasso Regression that can handle both linear and nonlinear relationships. It is recommended to master Ridge Regression and Lasso Regression first, and then learn Elastic Network Regression.

  5. Decision Tree Regression and Random Forest Regression: These two regression methods are decision tree-based algorithms that can handle nonlinear problems and high-dimensional data. It is recommended to learn the basic concepts and implementation methods of decision trees first, and then learn random forest regression.

  6. Support Vector Regression: Support Vector Regression is a nonlinear regression method that can handle complex data structures and high-dimensional data. It is recommended to learn the basic concepts and implementation methods of support vector machines first, and then learn support vector regression.

Suggested study methods include:

  1. Learn theory first, then practice. Master the mathematical foundation and theoretical knowledge of regression methods first, and then deepen your understanding by implementing code.

  2. Constantly adjust parameters and algorithms in practice. In the process of practice, you can try different parameters and algorithms to compare performance and effects, so as to continuously adjust and optimize.

  3. Study application scenarios and practical problems. Regression methods are widely used, and appropriate algorithms and parameters need to be selected according to specific problems and characteristics of data sets. Therefore, it is necessary to study regression methods in combination with practical application scenarios and problems.

3. Least Squares (OLS) and Complete Least Squares (CLS)

Both are methods for estimating the parameters of a linear regression model, but they are calculated differently.

The method of least squares estimates model parameters by minimizing the sum of squared residuals. Its basic idea is to find a set of model parameters that minimizes the residual sum of squares between the predicted value of the model and the true value. The method of least squares is a widely used method because of its simple calculation process, which is easy to understand and implement.

In contrast, the complete least squares method is a more complex method. CLS can handle models with multiple independent variables, as well as the presence of measurement errors. Its main idea is to project the data points onto the regression plane and find a regression plane that minimizes the sum of the distances between the projection of each data point on the plane and its actual value. CLS usually needs to use an iterative algorithm to calculate the optimal regression plane, which makes it more computationally expensive than OLS.

Therefore, OLS is a method suitable for simple linear regression, while CLS is suitable for more complex linear regression models, especially in the presence of multiple independent variables and measurement error. In practice, OLS remains one of the most commonly used linear regression methods due to its low computational cost.

4. The role of diameter at breast height in the calculation of forestry indicators

DBH is an index used to calculate forest volume and biomass. Vegetation coverage refers to the coverage of vegetation in a certain area, usually calculated using remote sensing image data and image processing algorithms

5. Empirical and physical models

The empirical model is to establish a mathematical model based on experience and data through observation, experiment and data analysis of the phenomenon to describe and predict the behavior and changes of the phenomenon. It is usually based on statistical analysis and machine learning techniques, which can effectively predict data and have strong interpretability. Empirical models are often applied to the modeling of natural, social, and economic phenomena, such as predicting stock prices, climate change trends, etc.

The physical model describes the structure and motion of the system by modeling the physical laws of the physical system to predict the behavior and changes of the system. It is usually based on physical principles, quantifies physical quantities, and uses mathematical equations to describe changes in physical quantities such as system motion, energy, and force, with high accuracy and reliability. Physical models are often used to study the motion, mechanics, thermodynamics, and electromagnetism of physical systems, such as computer simulations and weather forecasts.

Day 2 4.4 Tuesday Regression Indicators

 Today, we mainly study the characteristics and calculation methods of each regression index:

1. Coefficient of determination

The coefficient of determination (Coefficient of Determination, R^2) is a commonly used indicator to measure the degree of fitting of a statistical model to the data. It represents the proportion of the variation in the dependent variable that can be explained by the independent variable, and its value ranges from 0 to 1. R^2 is defined as follows:

R^2 = 1 - SSE/SST

Among them, SSE means the sum of squares of the residual error (Sum of Squares for Error), which is the sum of the model fitting errors, and SST means the sum of squares (Sum of Squares Total), which is the sum of the squares of the difference between the actual observation and the mean of the observation. . The closer R^2 is to 1, the better the model fits the data, that is, the model can explain a larger part of the variability of observations; the closer R^2 is to 0, the worse the model fits the data, that is The model cannot explain most of the variability in the observations.

The advantage of R^2 is that it is not affected by the sample size and the number of independent variables, and it is intuitive and easy to interpret. Therefore, R^2 is often used to evaluate the prediction accuracy and interpretation effect of regression models. However, it should be noted that R^2 does not fully represent the quality of the model, because it only considers the effect of the model on data fitting, and does not consider other factors, such as the interpretability and generalization ability of the model. Therefore, when evaluating the model, in addition to R^2, other indicators and practical applications need to be considered comprehensively.

from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
import numpy as np
from sklearn.metrics import r2_score

# (1)导入数据
X, y = load_diabetes().data, load_diabetes().target

# (2)分割数据
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

# (3)训练
LR = LinearRegression()
performance = LR.fit(X_train, y_train)

# (4)预测(本例分必要)
y_pred_train = LR.predict(X_train)  # 在测试集合上预测
y_pred_test = LR.predict(X_test)  # 在测试集合上预测

# (5) 评估模型
print("训练集合上R^2 = {:.3f}".format(performance.score(X_train, y_train)))
print("测试集合上R^2 = {:.3f} ".format(performance.score(X_test, y_test)))

print("训练集合上R^2 = {:.3f}".format(r2_score(y_train, y_pred_train)))
print("测试集合上R^2 = {:.3f} ".format(r2_score(y_test, y_pred_test)))

np.set_printoptions(precision=3, suppress=True)
print('w0 = {0:.3f}'.format(LR.intercept_))
print('W = {}'.format(LR.coef_))

2. Root mean square error

Root Mean Squared Error (RMSE) is a commonly used metric to measure the difference between the numerical solution and the analytical solution to the original problem. RMSE is the square root of MSE, which can be understood as the standard deviation of forecast error, and its calculation formula is as follows:

RMSE = sqrt(1/n * sum((y_true - y_pred)^2))

Among them, y_true represents the analytical solution of the original problem, y_pred represents the numerical solution, and n is the number of samples.

Compared with other evaluation indicators, RMSE has the following advantages:

  1. It is sensitive to outliers and can reflect the distribution of prediction errors.

  2. Compared with indicators such as mean absolute error (MAE) and mean square error (MSE), RMSE has a stronger penalty for samples with large prediction errors, so it pays more attention to samples with large errors.

  3. Compared with indicators such as MSE and MAE, the calculation result of RMSE is easier to understand, and its unit is the same as that of the original data.

In practical applications, RMSE is often used to measure the prediction accuracy of regression models. Generally speaking, the smaller the RMSE, the better the prediction effect of the model.

3. Mean Absolute Error

 4. Mean Square Error (MSE) Root Mean Square Error (RMSE)

5.MAE: mean absolute error; MAPE: mean absolute percentage error

code:

import numpy as np
from sklearn import metrics


# MAPE和SMAPE需要自己实现
def mape(y_true, y_pred):
    return np.mean(np.abs((y_pred - y_true) / y_true)) * 100


def smape(y_true, y_pred):
    return 2.0 * np.mean(np.abs(y_pred - y_true) / (np.abs(y_pred) + np.abs(y_true))) * 100


y_true = np.array([1.0, 5.0, 4.0, 3.0, 2.0, 5.0, -3.0])
y_pred = np.array([1.0, 4.5, 3.5, 5.0, 8.0, 4.5, 1.0])

# MSE
print(metrics.mean_squared_error(y_true, y_pred))  # 8.107142857142858
# RMSE
print(np.sqrt(metrics.mean_squared_error(y_true, y_pred)))  # 2.847304489713536
# MAE
print(metrics.mean_absolute_error(y_true, y_pred))  # 1.9285714285714286
# MAPE
print(mape(y_true, y_pred))  # 76.07142857142858,即76%
# SMAPE
print(smape(y_true, y_pred))  # 57.76942355889724,即58%

Day 3 4.5 Wednesday Pillow library, point cloud format, analytical and numerical solutions

Today's main harvest is as follows:

1. Today I learned that the data of fls belongs to the original data of the scanner and needs to be exported by software such as usage, so data processing usually uses las, e57, pcd and other formats

2. Analytical and Numerical Solutions

Analytical solution refers to the exact solution of an equation or function obtained by mathematical methods. This solution usually depends on the combination and application of formulas, operators, functions, variables and initial conditions. The solution obtained by solving is an expression that gives the exact value of the function over the domain of definition.

Numerical solution refers to the approximate solution of an equation or function obtained through a computer program using numerical methods. Numerical solutions are usually obtained by discretization (discretization of the problem into a set of numerical problems) and iterative solution (step by step approaching the exact solution through repeated calculations). The solution obtained by solving is a list or array of values ​​representing discrete approximations to the solution.

In practical problems, the analytical solutions of some equations or functions are difficult or even impossible to find, and numerical solutions become particularly important at this time. Often, the accuracy of the numerical solution can be controlled by appropriate algorithms and tuning parameters. However, compared to analytical solutions, the accuracy of numerical solutions may be affected by factors such as round-off errors and errors in the algorithm itself.

3. Pillow library

Pillow is a very popular image processing library in Python, which can be used to process various image formats, such as opening, saving, cropping, rotating, scaling, etc. Pillow is developed based on Python Imaging Library (PIL), but it is easier to use and more compatible than PIL.

Here are some common Pillow operations:

  1. 1 Open and save the image:
from PIL import Image

# 打开图像
image = Image.open("image.jpg")

# 保存图像
image.save("new_image.png")
  1. 2 Crop the image:
# 裁剪图像
cropped = image.crop((100, 100, 200, 200))

# 保存裁剪后的图像
cropped.save("cropped_image.jpg")
  1. 3 Rotate the image:
# 旋转图像
rotated = image.rotate(90)

# 保存旋转后的图像
rotated.save("rotated_image.jpg")
  1. 4 Scale the image:
# 缩放图像
resized = image.resize((300, 300))

# 保存缩放后的图像
resized.save("resized_image.jpg")
  1. 5 Adjust image brightness and contrast:
# 调整图像亮度和对比度
from PIL import ImageEnhance

enhancer = ImageEnhance.Brightness(image)
brightened_image = enhancer.enhance(1.5)  # 亮度增强1.5倍
brightened_image.save("brightened_image.jpg")

enhancer = ImageEnhance.Contrast(image)
contrasted_image = enhancer.enhance(1.5)  # 对比度增强1.5倍
contrasted_image.save("contrasted_image.jpg")

These are just some of the functions provided by Pillow, Pillow has many other functions, such as adding watermarks, adjusting color balance, converting image formats and so on.

The fourth day, 4.6 Thursday, two point cloud overlay methods

 Today, I mainly studied two methods of point cloud overlay and the reading of a paper:

1. It is very difficult to classify active and passive remote sensing with deep learning, which involves too many knowledge points, and it is okay to understand, but in-depth learning takes a lot of time, so it must be carefully considered. If you choose to study for a Ph.D., there is no doubt about deep learning You must study hard, but if you only consider graduation, I think it is better to find other simple directions

2. Two point cloud overlay methods

1. Superposition of bounding boxes

(1) Assuming that there are two sets of point clouds A and B, calculate the OBB bounding box of A
(2) Find the point cloud B in the OBB bounding box of point cloud A, so as to obtain the overlapping area of ​​point cloud B relative to A
(3) Conversely, calculate the overlapping area of ​​point cloud A relative to B
(4) In summary, a set of point clouds of overlapping areas can be obtained, which can be used for subsequent analysis.

// 提取点云重叠区域.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <pcl/features/moment_of_inertia_estimation.h>
#include <vector>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>

#include<pcl/filters/crop_box.h>
#include <pcl/common/transforms.h>
#include<pcl/common/common.h>
#include<ctime>
//获取第二个点云相对第一个点云的重叠区域
void getOverlappedCloud(const pcl::PointCloud<pcl::PointXYZ>& cloud, const pcl::PointCloud<pcl::PointXYZ>& cloud2, pcl::PointCloud<pcl::PointXYZ>& overlapped_cloud2)
{
    pcl::MomentOfInertiaEstimation <pcl::PointXYZ> feature_extractor;
    feature_extractor.setInputCloud(cloud.makeShared());
    feature_extractor.compute();
    pcl::PointXYZ min_point_AABB;
    pcl::PointXYZ max_point_AABB;
    pcl::PointXYZ min_point_OBB;
    pcl::PointXYZ max_point_OBB;
    pcl::PointXYZ position_OBB;
    Eigen::Matrix3f rotational_matrix_OBB;
    feature_extractor.getAABB(min_point_AABB, max_point_AABB);
    feature_extractor.getOBB(min_point_OBB, max_point_OBB, position_OBB, rotational_matrix_OBB);
    //将点云旋转,使得第一个点云的OBB=AABB,以此获得第一个点云OBB区域内的第二个点云
    Eigen::Matrix4f tf = Eigen::Matrix4f::Identity();
    tf << (rotational_matrix_OBB.inverse());
    pcl::PointCloud<pcl::PointXYZ>cloud_tf, cloud2_tf;
    pcl::transformPointCloud(cloud, cloud_tf, tf);
    pcl::transformPointCloud(cloud2, cloud2_tf, tf);
    Eigen::Vector4f pmin, pmax;

    pcl::getMinMax3D(cloud_tf, pmin, pmax);
    pcl::PointCloud<pcl::PointXYZ> tf_overlapped_cloud2;
    std::vector<int> indices;
    //第二个点云旋转之后的点云重叠区域
    pcl::getPointsInBox(cloud2_tf, pmin, pmax, indices);
    pcl::copyPointCloud(cloud2_tf, indices, tf_overlapped_cloud2);
    //再变换到原始坐标系中
    pcl::transformPointCloud(tf_overlapped_cloud2, overlapped_cloud2, tf.inverse());

    std::cout << tf << std::endl;
    std::cout << rotational_matrix_OBB << std::endl;
}
int main(int argc, char** argv)
{
    auto start = std::clock();
    pcl::PointCloud<pcl::PointXYZ>cloud, overlapped_cloud;
    pcl::io::loadPCDFile("dem_transformed.pcd", cloud);

    pcl::PointCloud<pcl::PointXYZ>cloud2, overlapped_cloud2;
    pcl::io::loadPCDFile("feidimian.pcd", cloud2);

    getOverlappedCloud(cloud, cloud2, overlapped_cloud2);
    getOverlappedCloud(cloud2, cloud, overlapped_cloud);

    pcl::io::savePCDFile<pcl::PointXYZ>("重叠点云.pcd", overlapped_cloud);
    pcl::io::savePCDFile<pcl::PointXYZ>("重叠点云2.pcd", overlapped_cloud2);
    auto end = std::clock();
    std::cerr << "耗时" << std::difftime(end, start) << "ms" << std::endl;
    std::cout << "Hello World!\n";
}


2. Octree method

(1) Assuming that there are two sets of point clouds A and B, build an octree for A
(2) Traverse all points in B, and query whether there is a point cloud in the corresponding voxel of A (that is, whether AB exists in the same voxel at the same time) point cloud), if it exists, then the point is the overlapping point cloud of point cloud B
(3) Similarly, the overlapping point cloud in A is obtained

// 提取点云重叠区域2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include<pcl/io/pcd_io.h>
#include<pcl/point_cloud.h>
#include<pcl/point_types.h>
#include<pcl/octree/octree.h>
#include<ctime>
using point = pcl::PointXYZ;
using cloud = pcl::PointCloud<point>;
void getOverlappedCloud(const pcl::PointCloud<pcl::PointXYZ>& cloud1, const pcl::PointCloud<pcl::PointXYZ>& cloud2, pcl::PointCloud<pcl::PointXYZ>& overlapped_cloud2)
{
    double radius = 3.0;
    pcl::octree::OctreePointCloudSearch<point> octree(radius);
    octree.setInputCloud(cloud1.makeShared());
    octree.addPointsFromInputCloud();

    //pcl::PointCloud<point> overlapped_2;
    for (size_t i = 0; i < cloud2.size(); ++i)
    {
        std::vector<int> indices;
        octree.voxelSearch(cloud2.points[i], indices);
        pcl::PointCloud<point> cloud_out;
        if (indices.size())
        {
            overlapped_cloud2.push_back(cloud2.points[i]);
        }

    }
}
int main()
{
    auto start = std::clock();
    pcl::PointCloud<point> cloud1, cloud2;
    pcl::io::loadPCDFile<point>("SHCSCloud.pcd", cloud1);
    pcl::io::loadPCDFile<point>("SHCSCloud副本.pcd", cloud2);
    cloud overlapped1, overlapped2;
    getOverlappedCloud(cloud1, cloud2, overlapped2);
    getOverlappedCloud(cloud2, cloud1, overlapped1);

    pcl::io::savePCDFile("重叠1.pcd", overlapped1);
    pcl::io::savePCDFile("重叠2.pcd", overlapped2);
    auto end = std::clock();
    std::cerr << "耗时" << std::difftime(end, start) << "ms" << std::endl;
    std::cout << "Hello World!\n";
}


The fifth day 4.14 Friday    QPainter , QT basic operation

Today, I will learn about QT again, mainly drawing line charts and mouse operations

For details, see the project file untitled5 (Qpaint mouse operation)

1. Painter.begin(&pixmap), painter.end(); code understanding

QPainter is a class used to draw graphics in the Qt framework. It provides a series of drawing functions, which can draw various shapes, characters, patterns, etc. on the image. To draw graphics on the image, you must first create a QPainter object, and then use it to perform drawing operations.

In this code, first create a QPainter object named painter , and then use the begin() function to associate it with the pixmap object. This means that in subsequent drawing operations, all drawing function calls will be performed on the pixmap image.

It should be noted that in order to ensure the correct drawing operation, the drawing operation of QPainter must be ended in time after the drawing is completed , and related resources must be released. In this code, use the end() function to end the drawing operation of the painter object to release the resources it occupies.

2. The coordinates of the current object prePoint=event->pos()

The coordinates of the current object refer to the position described in the coordinate system of the current object. For example, in a window (QWidget) object, (0,0) is usually the upper left corner of the window. The pos() function in the mouse event returns the position of the mouse pointer relative to the current object coordinate system. Therefore, in the mousePressEvent() function of the Myplot object, prePoint is set to the position of the mouse pointer relative to the coordinate system of the Myplot object.

3. The relationship between QPainter painter(this) and QPainter painter

Both QPainter painter(this) and QPainter painter create a QPainter object, but their functions are slightly different.

The QPainter object created by QPainter painter(this) will be drawn on the current control, which is the control referred to by this . For example, if this is a QWidget object, then this code will create a QPainter object for drawing operations on the QWidget control. This usage is usually used for custom drawing on the control, such as drawing a custom picture on a QLabel control and so on.

The QPainter object created by the QPainter painter does not specify the drawing target, and needs to be specified using the begin() function during the drawing operation . For example, a QPixmap object can be passed in the begin() function for drawing operations on the image. This usage is usually used to create an image in memory and perform drawing operations in it, such as generating a captcha image and so on.

It should be noted that no matter which usage is used, it is necessary to use the end() function to end the drawing operation of QPainter after the drawing operation is completed , so as to release related resources.

4. Header file protection

#ifndef MYPLOT_H
#define MYPLOT_H

These two sentences are the standard way of writing header files, called "Header Guard", which is used to prevent the same header file from being included multiple times. When a header file is included multiple times, header guards ensure that it is only compiled once. When compiling for the first time, the preprocessor will define MYPLOT_H, and when the header file is included later, since MYPLOT_H has already been defined, the preprocessor will ignore the contents of the entire file, thereby avoiding repeated definitions.

At the same time, this code is also a preprocessor directive, which is used to tell the compiler which files to include.

#ifndef is a conditional compilation preprocessor directive, meaning "if undefined". If the identifier is defined, skip the following code, otherwise compile the code until the #endif directive. This directive is usually used in header files to avoid including the same header file multiple times.

#define is another preprocessor directive used to define a macro. In this case, the macro is MYPLOT_H . If the MYPLOT_H macro is not defined, define it, then compile the following code, and finally add #endif at the end of the file to end the area of ​​conditional compilation instructions. The purpose of this is to prevent multiple inclusions of the same file.

Five, the meaning of Q_OBJECT

Q_OBJECT is a macro provided by the Qt framework, which is used to declare that a class needs to use Qt's meta-object system (Meta-Object System) to support the signal (Signal) and slot (Slot) mechanism.

Before using the signal and slot mechanism, you need to add the Q_OBJECT macro to the class declaration. When the macro is parsed, Qt will automatically create an internal meta-object system, which can dynamically query the information of the class through the reflection mechanism at runtime, so as to realize the connection of signals and slots, event processing and other mechanisms.

It should be noted that if a class contains the Q_OBJECT macro, the source file of the class must be processed by the MOC (Meta-Object Compiler) preprocessor to generate code for the meta-object system. These codes will be compiled into a .moc file with the same name as the source file, and compiled by the compiler together with other object files into the final executable program during the linking phase.

Q_OBJECT is a macro, which tells the compiler that this class is a Qt meta-object class (Meta-Object Class), which needs to be processed by the MOC preprocessor.

In the class defined by Q_OBJECT macro, you can use the functions provided by Qt's meta-object system (Meta-Object System), such as signal and slot mechanism, dynamic properties, reflection, etc.

In the class defined by the Q_OBJECT macro, the MOC preprocessor will automatically generate some additional C++ code, including some strings, functions, variables and codes, etc., which are used to implement the functions provided by Qt's meta-object system.

In order for the MOC preprocessor to correctly process the Q_OBJECT macro in the header file, you need to add the following statement to the project's .pro file:

makefileCopy code

QT += core

This tells the qmake tool to link against the Qt core library, which contains the MOC preprocessor. When compiling, the MOC preprocessor is automatically invoked to generate additional C++ code.

Six, class Myplot: public QWidget meaning

This code defines a class named Myplot, which is a derived class of the QWidget class. That is to say, the Myplot class inherits all member functions and member variables of the QWidget class, and can be extended on this basis. QWidget is the base class of all visual controls in QT, so the Myplot class can be used to create a new QT control that can be displayed and interacted with in a window.

7. The meaning of Myplot(QWidget *parent = 0)

In C++, functions can have parameters. The parameter here is a QWidget pointer, which is a pointer named "parent", pointing to a QWidget object. The default value of this pointer is 0, which means that if the user does not pass a pointer to the parent QWidget object, the default value of 0 will be used.

The role of this parameter is to pass the parent window to the Myplot constructor. This is useful for displaying Myplot widgets in subwindows. If no parent pointer is passed, the Myplot widget will be displayed on the screen instead of in the parent window.

        7.1 The difference with QWidget* parent = nullptr

Both QWidget *parent = 0 and QWidget *parent = nullptr indicate that the parent pointer is set to a null pointer, that is, there is no parent object. However, after the introduction of nullptr in the C++11 standard, it is recommended to use QWidget *parent = nullptr, because nullptr more clearly represents a null pointer, and avoids the possibility of misinterpreting 0 as an integer value.

        7.2 Should the parameter of every constructor be this?

Not every constructor should have this parameter. This parameter is usually used to implement the parent-child relationship, that is, when creating an object, you need to tell the object who its parent object is. If this object is a subclass of a widget (QWidget), then this parameter is usually passed to the constructor of QWidget to implement the parent-child relationship between widgets.

In earlier versions of Qt, this parameter is usually set to 0 (that is, NULL), indicating that this object has no parent object. But in the C++11 standard, it is recommended to use nullptr instead of 0, because nullptr has clearer semantics and can prevent some potential errors.

8. The difference between public , protected or private

In C++, member variables of a class can be defined as public , protected or private . The meaning of these keywords is as follows:

  • public : Members can be accessed by code outside the class.
  • protected : Members can be accessed by code outside the class, but only through the class inheritance system.
  • private : Members can only be accessed by code within the class.

Generally speaking, we should define member variables as private as much as possible, and use public or protected only when necessary . This is because defining a variable as public will cause it to be exposed outside the class, which may affect the encapsulation of data and the maintainability of the class.

Typically, data members are defined as private and public member functions (getters and setters) are provided to access and modify the data members. This method is called encapsulation, which can effectively protect data, but also make the code more maintainable and easy to read. For some members that need to be accessed outside the class, they can be defined as public or protected , but the access control of the members and the encapsulation of the class need to be considered.

Some tricks:

  • Prefer using member functions to access and modify data members rather than directly manipulating member variables.
  • Defining member variables as const or static can improve code security and maintainability.
  • For some members that need to be accessed in subclasses, they can be defined as protected .
  • For some members that do not need to be accessed outside the subclass or class, they should be defined as private .

The access control and definition method of member variables need to be determined according to the specific situation.

九、 Widget::Widget(QWidget* parent) : QWidget(parent) , ui(new Ui::Widget)

This is the implementation of the constructor of the Widget class. It accepts a parameter parent of QWidget* type, and calls the constructor of the base class QWidget and passes in parent to initialize the Widget object.

Second, it creates a new Ui::Widget object and stores it in the member variable ui. Ui::Widget is an automatically generated class that contains pointers to all widgets in the UI file. We can use it to access various components in the UI file, such as buttons, labels, etc.

10. Problem summary:

1. #include "ui_Text1.h" should be placed in the same file as Ui::Text1Class ui;

2. After the ui file is modified, it automatically becomes the code before modification again:

Edit .ui directly in VS and open it with a compiler to directly change .ui

3. Unresolvable external symbol "__declspec(dllimport) public: __cdecl QApplication::QApplication(int &,char * *,int)" (__imp_??0QApplication@@QEAA@AEAHPEAPEADH@Z), which is referenced in the function main Symbol QtApplication1 E:\yan1\exercise\C_C++\QT\QtApplication1\QtApplication1\source.obj 1

Reason: The template is not connected, just add it in the VS plug-in

4. Pro to sln, open it with a vs plug-in, and then save it as sln

Exception thrown at 5.0x00007FFD4263ED6A (Qt5Cored.dll) (in DynamicPlot.exe): 0xC0

Reason: QLineSeries* series = new QLineSeries(); to be changed to series = new QLineSeries();

QLineSeries* must be declared in .h, if it is in cpp, a wild pointer will appear

In addition to main.cpp

The sixth day 4.15 Saturday QT line chart time axis

Today I mainly learned the line chart in QT, the drawing of the time axis,

1. Use QHBoxLayout to add layout controls

In Qt, using layouts is a common way of managing the positioning and size of widgets in a window.

In this case, the QChartView widget is added to a QHBoxLayout layout, which is then set as the layout of the QMainWindow central widget. This allows the graph view to automatically resize and position when the window is resized or the layout changes.

Creating a layout and adding the graph view to it also provides more flexibility in case you later want to add other components to the main window. You can add these widgets to the same layout or to different layouts, and manage their position relative to the graph view by adjusting layout properties.

In general, using layouts allows for greater control over the positioning and size of widgets in a window, and provides flexibility, and is a best practice for Qt programming.

2. The relationship between timer startTimer and timerEvent

When a timer is started using the startTimer method, Qt will arrange a QTimerEvent event in the event loop, which will be triggered automatically when the timer expires. At this point, Qt will automatically call the timerEvent method of the object where the timer is located to handle the event. So, in your code, when you use startTimer(300), Qt schedules a timer and calls the timerEvent method when the timer expires, even if you don't call the method explicitly

3. Use code to represent the creation of QObject and QTimer timers, start, get ID, and end

1. Create a QObject timer:

//创建计时器对象

QTimer *timer = new QTimer(this);

//开始计时,设置时间间隔为1000毫秒

timer->start(1000);

//获取计时器ID

int timerId = startTimer(1000);



//在计时器触发的槽函数中结束计时器

void MyClass::onTimeout() {

    //结束计时器

    timer->stop();

    //结束QObject计时器

    killTimer(timerId);

}

2. Create a QTimer timer:

//创建计时器对象

QTimer *timer = new QTimer(this);

//设置时间间隔为1000毫秒

timer->setInterval(1000);

//开始计时

timer->start();

//获取计时器ID

int timerId = timer->timerId();



//在计时器触发的槽函数中结束计时器

void MyClass::onTimeout() {

    //结束计时器

    timer->stop();

    //结束QTimer计时器

    timer->deleteLater();

}

Note: The start of the QObject timer and the setting of the time interval are completed in the startTimer() function, and the QTimer timer is set through the setInterval() function to set the time interval, and then call the start() function to start timing. Also, QObject timers use the killTimer() function when they end, while QTimer timers use the deleteLater() function.

Fourth, the meaning of QT_CHARTS_USE_NAMESPACE

QT_CHARTS_USE_NAMESPACE is a macro defined in the Qt Charts module to access all classes in the Qt Charts namespace. It is used to avoid duplicating QtCharts:: before each class name of the Qt Charts module.

For example, you can simply include QT_CHARTS_USE_NAMESPACE in your code and use QChart directly instead of writing QtCharts::QChart every time you want to use the QChart class.

Note that it is important to only use this macro in source files, not header files, to avoid namespace collisions.

Five, the meaning of QT_BEGIN_NAMESPACE and QT_END_NAMESPACE

QT_BEGIN_NAMESPACE and QT_END_NAMESPACE are preprocessor directives provided by Qt for defining namespaces. These macros are used to ensure that all Qt classes, functions and macros are defined within the Qt namespace.

QT_CHARTS_USE_NAMESPACE is another preprocessor directive provided by Qt to include the Qt charts namespace in your code.

In the code you provided, QT_BEGIN_NAMESPACE and QT_END_NAMESPACE are used to define the Ui namespace, which contains the Widget class. This is to ensure that the Widget class is defined in the Qt namespace.

QT_CHARTS_USE_NAMESPACE is used to include the Qt charts namespace in the code.

Six, namespace Ui { class MainWindow; } meaning

This code snippet defines a namespace called "Ui" and within it a class called "Widget". Usually, this code is automatically generated by Qt's UI file, and is used to separate the interface defined in the UI file from the logic code of the application.

In the generated code, the "Ui::Widget" class is instantiated and used in the main class, while the actual UI elements and layout information are described and stored in the UI file. This design pattern can make it easier for developers to maintain and modify the UI interface, and it can also improve the readability and maintainability of the code.

Seven, dynamic_cast function

dynamic_cast is a C++ operator that can be used to perform runtime type information (RTTI) checks on an object pointer and safely convert between pointer types, including polymorphic types. In the code,

QValueAxis *axisY = dynamic_cast<QValueAxis*>(this->chart()->axisY()); 

It tries to convert the pointer returned by this->chart()->axisY() into a pointer of type QValueAxis.

If the pointer returned by this->chart()->axisY() can be safely converted to a QValueAxis*, then the conversion will succeed and the pointer will be assigned to axisY. Otherwise, if the pointer is not an instance of QValueAxis, dynamic_cast will return a null pointer.

In this particular code, it assumes the axisY object is of type QValueAxis (rather than a QCategoryAxis or other type of axis) and uses it to access and modify the range of values ​​displayed on the chart's Y axis.

Eight, setMouseTracking function

this->setMouseTracking(false);

This code sets the mouseTracking property of QGraphicsView to false. When the mouseTracking property is false, QGraphicsView will only trigger the mousePressEvent, mouseReleaseEvent, and mouseMoveEvent events when the mouse is pressed, released, or dragged. If the mouseTracking property is true, the mouseMoveEvent event will also be triggered when the mouse is moving.

Therefore, setting the mouseTracking property to false can reduce unnecessary event triggering and improve application performance. However, if you need to achieve the effect of mouse hovering, you need to set the mouseTracking property to true.

Nine, QPen pen (0x000001) function to set the color

//设置pen的颜色 
pen.setWidth(1); 
splineSeries->setPen(pen); 
//设置画笔颜色和大小

This code is used to set the brush property of the QSplineSeries object. QSplineSeries is a spline-based polyline chart. By interpolating the data, the polyline is made smoother. The pen property determines the color and width of the polyline.

Here, a QPen object is first created, 0x000001 is a hexadecimal color value, representing black (RGB value is (0, 0, 1)). Then set the width of the brush to 1 through the setWidth() method, and finally set the brush of the polyline to the QPen object through the setPen() method of the splineSeries object.

Note that when using QSplineSeries to draw a line chart, it is recommended to set the width of the QPen object to 1 to obtain the best drawing effect.

If you are using QLineSeries, do you not need to set it?

Yes, if you are using QLineSeries, you don't need to set QPen, because the default brush of QLineSeries is black and the line width is 2. But if you need to customize properties such as line color and width, you can also use the setPen method to achieve it.

10. Problem summary:

1. The newly added controls in Qdesigner may not be available, so you need to reopen the project with VS instead of reopening VS

2.chart->setTitle("real-time dynamic curve"); The function will report an error when compiling with VS, but not when using Qcreator

3.cmake needs full English path

4. QVTKOpenGLNativeWidget instead of QVTKWidget        

The seventh day, 4.16 Sunday, the abscissa changes Octree

Today I mainly learned the label change of the abscissa of the line chart, and the learning research of the octree:

1. qobject_cast function: convert label type

To set the label format of the vertical axis, you can use the setLabelFormat method of the QValueAxis class .

chart->axes(Qt::Vertical).back() returns a pointer of type QAbstractAxis , which is the base class of type QValueAxis . Therefore, you need to convert it to the QValueAxis type before using the setLabelFormat method.

QValueAxis* verticalAxis = qobject_cast<QValueAxis*>(chart->axes(Qt::Horizontal).back());
    if (verticalAxis) {
        verticalAxis->setLabelFormat("%d"); // 设置标签格式为小数点后两位
    }

In this example, we first use the qobject_cast function to convert the QAbstractAxis pointer to a QValueAxis pointer, and then assign it to the verticalAxis variable. If the conversion is successful, then we can use the setLabelFormat method to set the label format. The label format here is "%.2f" , which means to display two decimal places.

Note that if the QAbstractAxis pointer actually points to a different type of axis than QValueAxis , then the conversion operation will fail and the verticalAxis variable will be nullptr , so you should check before using the verticalAxis pointer.

Two, static_cast<int> method

To make m_coordItem->setText() output an integer, use the QString::number() method to convert the floating-point number to an integer. Specifically, you can convert curVal.x() and curVal.y() respectively to integers and insert them into strings.

//输出int
const QPoint curPos = event->pos();
QPointF curVal = this->chart()->mapToValue(QPointF(curPos));
int x = static_cast<int>(curVal.x());
int y = static_cast<int>(curVal.y());
QString coordStr = QString("X = %1, Y = %2").arg(x).arg(y);
m_coordItem->setText(coordStr);

//输出String
const QPoint curPos = event->pos();
QPointF curVal = this->chart()->mapToValue(QPointF(curPos));
QString coordStr = QString("X = %1, Y = %2").arg(curVal.x()).arg(curVal.y());
m_coordItem->setText(coordStr);

In this example, we first convert curVal.x() and curVal.y() to integers and assign them to the x and y variables respectively. Here static_cast<int>(curVal.x()) is used to convert the float to an integer. Then, we use the QString::arg() method to insert x and y into the string, which will be set as the text content of m_coordItem . In this way, m_coordItem->setText() will output integers.

三、now.addSecs(i * 60)

now.addSecs(i * 60) is the time obtained by adding i minutes to the current time now. where i*60 converts i minutes to seconds.

Four, the relationship between octree and quadtree

Both quadtrees and octrees are common tree data structures used to partition two-dimensional and three-dimensional spaces.

Quadtree is a tree structure that divides a two-dimensional space into four quadrants. Each node can have up to four child nodes, representing the upper left, upper right, lower left, and lower right quadrants. Typically, the root node of a quadtree represents the entire space, while the child nodes represent smaller regions of the space. Applications of quadtrees include image processing, geographic information systems, and computer games.

Octree is a tree structure that divides three-dimensional space into eight subspaces. Each node can have up to eight child nodes, representing the six directions of up, down, left, right, front, and back. The root node of the octree represents the entire space, while the child nodes represent smaller subspaces of the space. Applications of octrees include 3D graphics, computer-aided design, physical simulation, and computer vision.

Both quadtree and octree are effective tools for space division, which can greatly improve the efficiency of the algorithm. However, when dealing with non-uniform distribution data, they may appear unbalanced, which requires special algorithms to solve.

That is to say, quadtree application is two-dimensional, octree application is three-dimensional, quadtree is mainly used in two-dimensional space, and octree is mainly used in three-dimensional space. This is because in two-dimensional space, dividing the space into four quadrants is sufficient to describe most situations, while in three-dimensional space, it is necessary to divide the space into eight subspaces to fully describe various situations in the space. Of course, that's not to say that quadtrees can't be used in 3D, or octrees can't be used in 2D, but there may be some limitations to their efficiency.

5. How to use octree and its relationship with point cloud data

Q: Will the octree increase the running speed?

Using an octree can improve some running speed, but it depends on the specific application scenario and algorithm implementation.

Generally speaking, the use of octrees can speed up some algorithms that need to search, traverse or neighbor search for point cloud data. Octrees can partition point cloud data by spatial location for faster access. For example, in applications such as point cloud registration and 3D reconstruction, the use of octrees can improve the calculation speed of matching point pairs and reduce the algorithm complexity.

However, in some cases, the use of octrees may increase the runtime and space complexity. For example, if the point cloud data is very dense or the resolution setting of the octree is unreasonable, the number of nodes in the octree will increase sharply, thereby reducing the efficiency of the algorithm.

Therefore, whether to use octree needs to evaluate its effect according to specific application scenarios and algorithm implementation.

Q: How to fully grasp the octree algorithm

To fully master the octree algorithm, some pre-knowledge and certain skills are required.

First of all, you need to master the basic knowledge of C++ programming language and data structure, and understand the usage methods and principles of basic data structures such as pointers, arrays, linked lists, and trees.

Secondly, it is necessary to understand the basic concepts and formats of point cloud data, and be familiar with related algorithms and libraries for point cloud data processing, such as PCL, Open3D, etc.

Next, you need to understand the principle and implementation method of the octree, including the data structure of the octree, the definition and attributes of nodes, the access and traversal of nodes, etc.

Finally, actual programming exercises and project practice are required to deepen the understanding and mastery of the octree algorithm, such as the realization of point cloud registration, reconstruction, clustering and other algorithms.

Here are some specific suggestions to help you learn and master the octree algorithm:

  1. Learn the basic concepts and principles of octrees, including the definition and attributes of nodes, access and traversal of nodes, the data structure of octrees, etc.
  2. Based on the existing octree library or code implementation, understand the specific application scenarios and usage methods of octree, such as point cloud registration, reconstruction, clustering, etc.
  3. Practice projects, apply octree algorithm to solve practical problems, such as building scene maps, performing target detection, segmentation, etc.
  4. Refer to relevant literature and academic papers to gain an in-depth understanding of the theory and latest research results of the octree algorithm.

In short, to fully master the octree algorithm, you need to practice and learn continuously, improve your programming skills and theoretical level, and try to apply it to practical problems.

Q: Common application fields related to octree and point cloud

The applications related to octree and point cloud are very extensive. The following are some common application areas:

  1. 3D reconstruction and modeling: Octrees can be used to sample, filter and voxelize point clouds to generate denser and more accurate 3D reconstruction and modeling results. For example, in 3D reconstruction, an octree can be used to voxelize the input point cloud, generate a dense voxel grid, and apply a grid reconstruction algorithm to it for surface reconstruction.
  2. Point cloud registration: Octrees can be used to speed up point cloud registration algorithms. By voxelizing the input point cloud, the computational complexity of matching point pairs is reduced from O(n^2) to O(nlogn) or lower, improving the efficiency and accuracy of the algorithm. For example, in a registration algorithm, an octree can be used to generate feature descriptors to achieve fast and accurate matching.
  3. Target detection and recognition: Octrees can be used to segment and cluster point cloud data for target detection and recognition. For example, in driverless cars, octrees can be used to segment and cluster point cloud data to detect and recognize objects such as roads, vehicles, and pedestrians.
  4. Point cloud processing and analysis: Octrees can be used for filtering, segmentation, fitting, analysis and other operations on point cloud data. For example, in computer vision, an octree can be used to filter and segment the input point cloud, thereby improving the efficiency and accuracy of image processing and analysis.

In short, octrees and point clouds have a wide range of applications and can be applied to multiple fields and tasks, providing researchers and engineers with a lot of convenience and innovative possibilities.

Six, C++ code of octree

#include <iostream>
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <pcl/octree/octree.h>

int main(int argc, char** argv)
{
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);

    // Fill in the cloud data
    cloud->width    = 1000;
    cloud->height   = 1;
    cloud->is_dense = false;
    cloud->points.resize(cloud->width * cloud->height);

    for (size_t i = 0; i < cloud->points.size (); ++i)
    {
        cloud->points[i].x = 1024.0f * rand () / (RAND_MAX + 1.0f);
        cloud->points[i].y = 1024.0f * rand () / (RAND_MAX + 1.0f);
        cloud->points[i].z = 1024.0f * rand () / (RAND_MAX + 1.0f);
    }

    // Octree resolution - side length of octree voxels
    float resolution = 128.0f;

    // Instantiate octree-based point cloud change detection class
    pcl::octree::OctreePointCloudChangeDetector<pcl::PointXYZ> octree(resolution);

    // Add points to the octree
    octree.setInputCloud(cloud);
    octree.addPointsFromInputCloud();

    // Switch octree buffers: This resets octree but keeps previous tree structure in memory.
    octree.switchBuffers();

    // Generate output cloud with points that have differences between buffered and current octree state
    pcl::PointCloud<pcl::PointXYZ>::Ptr output(new pcl::PointCloud<pcl::PointXYZ>);
    octree.getPointIndicesFromNewVoxels(*output);

    std::cout << "Input cloud: " << cloud->size() << " points" << std::endl;
    std::cout << "Output cloud: " << output->size() << " points" << std::endl;

    return 0;
}

This example uses the pcl::octree::OctreePointCloudChangeDetector class in the PCL library to implement an octree. This class can be used to detect the difference between two point clouds, it contains two main steps: add point cloud data and get difference point cloud.

In this example, we first define a pointer cloud of pcl::PointCloud<pcl::PointXYZ> type , and fill the point cloud data with random numbers. Then, we define a resolution variable to set the resolution of the octree, which is the side length of each voxel in the octree. Next, we create a pcl::octree::OctreePointCloudChangeDetector object octree and set the resolution to resolution .

Then, we set the point cloud data as the input data of the octree object and add it to the octree using the addPointsFromInputCloud() method. Next, we call the switchBuffers() method to switch the buffers of the octree and store the current octree state in memory.

Finally, we get the difference point cloud data using the getPointIndicesFromNewVoxels() method and store it in a pointer of type pcl::PointCloud<pcl::PointXYZ> named output . Finally, we output the size of the input point cloud and the output point cloud.

It should be noted that this example uses the example of point cloud change detection, but octrees can also be used for other types of point cloud operations, such as clustering, reconstruction, etc. In addition, in practical applications, parameters such as resolution can be adjusted according to specific needs to obtain better results.

Seven, the Python code of the octree

import open3d as o3d

# 读取点云数据
pcd = o3d.io.read_point_cloud("pointcloud.ply")

# 构建八叉树
octree = o3d.geometry.Octree(max_depth=5)
octree.convert_from_point_cloud(pcd)

# 保存八叉树
o3d.io.write_octree("octree.ot", octree)

In the above code, first use the read_point_cloud function of the Open3D library to read the point cloud data, then construct an Octree object, and specify the maximum depth as 5. Next, call the convert_from_point_cloud method to convert the point cloud data into an octree, and save the result to the octree object. Finally, call the write_octree function to save the octree to a file.

It should be noted that the Octree class used in the above code is a CPU-based octree implementation in the Open3D library. If you need to use a GPU-based octree implementation, you can use the KDTreeFlann class of the Open3D library. In addition, when constructing an octree, the maximum depth parameter needs to be adjusted according to the actual situation to ensure the quality and efficiency of the octree.

8. Problem summary:

1. Get the built-in chart of QchartView

QChart* chart = ui->chartView->chart();

2. After adding the abscissa to display the time, it can run normally, but there is an error problem

Prompt window: ASSERT failer in QList<T>::at "index out of range"

chart->removeAxis(chart->axes(Qt::Horizontal).at(1));
//可能会是.at(1)的问题,list数组里没有1,最多就到0

Summary for this week:

Due to various reasons, a document will be shared in the past two weeks. In the future, there will be no accidents, and learning records will be made every week:

1. The concept of regression, physical model and empirical model

2. Various indicators of accuracy evaluation: such as root mean square error, coefficient of determination

3. Point cloud format, analytical solution and numerical solution

4. Two methods of point cloud superposition, the concept of octree

5. Learning of QPaint and Qchart and their differences and connections

6. Drawing of the time axis, change of the abscissa axis

Guess you like

Origin blog.csdn.net/z377989129/article/details/129963188