Qt -使用OpenCV得到SDF
博客主页:【夜泉_ly】
本文专栏:【暂无】
欢迎点赞👍收藏⭐关注❤️
目录
- cv::Mat
- distanceTransform
- 获得SDF
本文的目标,
是简单学习并使用OpenCV的相关函数,
并获得QImage的SDF(Signed Distance Field 有向距离场)
至于我要用QImage的SDF来做什么,嗯,以后再说。
cv::Mat
这个可以理解为OpenCV的QImage,嗯。
简单看看就行。
首先,Mat是可以存QImage并显示的。
其次,Mat是可以手搓的。
我们先手搓一个白底的黑色正方形,看看效果:
void Widget::on_pushButton_clicked()
{cv::Mat testMat(201, 201, CV_8UC1);for(int i = 0; i <= 200; i++){for(int j = 0; j <= 200; j++){// testMat[i][j] = 0; 这不行呢if((90 < i && i < 110) && (90 < j && j < 110)) {testMat.at<uchar>(i, j) = 0; // 0 - 黑} else {testMat.at<uchar>(i, j) = 255; // 255 - 白}}}cv::imshow("testMat", testMat);
}
CV_8UC1
,表示 8 位单通道,即灰度图,这个之后会用。
先试试把QImage转为灰度图:
cv::Mat image_to_CV_8UC1(const QImage& image){int w = image.width(),h = image.height();cv::Mat mat(w, h, CV_8UC1);for(int i = 0; i < w; i++){for(int j = 0; j < h; j++){ // 注: 只能设为全0,或全1mat.at<uchar>(i, j) = (image.pixelColor(i, j).alpha() == 0) ? 0 : 255;}}if(mat.empty()) return mat;cv::imshow("image", mat);return mat;
}
跑一下,发现图像被逆时针转了九十度。
cv::Mat 的这个构造,传的分别是 ( 行, 列, 类型)
QImage 的 width 是宽, height 是高,刚好反了:
改了顺序过后就对了:
cv::Mat image_to_CV_8UC1(const QImage& image){int r = image.height(), c = image.width();cv::Mat mat(r, c, CV_8UC1);for(int i = 0; i < r; i++){for(int j = 0; j < c; j++){ // 注意下面 image 是 (j, i)mat.at<uchar>(i, j) = (image.pixelColor(j, i).alpha() == 0) ? 0 : 255;}}if(mat.empty()) return mat;cv::imshow("image", mat);return mat;
}
再来试试翻转,我们需要把0变非0,把非0变0,
这个用条件判断加赋值有点慢,
不过,OpenCV 有现成的函数 bitwise_not :
void Widget::on_pushButton_2_clicked(bool checked)
{cv::Mat mat_front = image_to_CV_8UC1(_image);cv::Mat mat_back;cv::bitwise_not(mat_front, mat_back);if(checked) cv::imshow("image", mat_front);else cv::imshow("image", mat_back);
}
distanceTransform
这个可以用来计算每个非零像素点到最近的零像素点的距离。
嗯,有点抽象,不过刚刚我们学会了手绘 Mat,
那我们可以先做个实验,看看这个距离到底是什么:
void Widget::on_pushButton_3_clicked()
{cv::Mat src(21, 21, CV_8UC1);for(int r = 0; r <= 20; r++){for(int c = 0; c <= 20; c++){if((5 < r && r < 15) && (5 < c && c < 15)) {src.at<uchar>(r, c) = 0; // 0 - 黑} else {src.at<uchar>(r, c) = 255; // 255 - 白}}}cv::Mat dst;cv::distanceTransform(src, dst, cv::DIST_L2, 3);QString ret;for(int r = 0; r <= 20; r++){for(int c = 0; c <= 20; c++){float f = dst.at<float>(r, c);ret += QString::number(f).rightJustified(9, ' ');} ret += "\n";}cv::imshow("dst", dst);std::cout << ret.toStdString();
}
哦,关于参数,
第一个是传入的 Mat,类型好像只能是 CV_8UC1,值只能是0或255。
第二个是得到的 Mat,只能用 at<float>
去取到它的值,这个值就是非0到最近0的距离。
第三个是距离的类型,这里用的 cv::DIST_L2,即欧几里得距离。
第四个是掩码,嗯,意义不明,取三就行。
打印结果如下:
8.21576 7.80147 7.38718 6.97289 6.55859 6.1443 5.73001 5.73001 5.73001 5.73001 5.73001 5.73001 5.73001 5.73001 5.73001 6.1443 6.55859 6.97289 7.38718 7.80147 8.215767.80147 6.84647 6.43217 6.01788 5.60359 5.1893 4.77501 4.77501 4.77501 4.77501 4.77501 4.77501 4.77501 4.77501 4.77501 5.1893 5.60359 6.01788 6.43217 6.84647 7.801477.38718 6.43217 5.47717 5.06288 4.64859 4.2343 3.82001 3.82001 3.82001 3.82001 3.82001 3.82001 3.82001 3.82001 3.82001 4.2343 4.64859 5.06288 5.47717 6.43217 7.387186.97289 6.01788 5.06288 4.10788 3.69359 3.2793 2.86501 2.86501 2.86501 2.86501 2.86501 2.86501 2.86501 2.86501 2.86501 3.2793 3.69359 4.10788 5.06288 6.01788 6.972896.55859 5.60359 4.64859 3.69359 2.73859 2.3243 1.91 1.91 1.91 1.91 1.91 1.91 1.91 1.91 1.91 2.3243 2.73859 3.69359 4.64859 5.60359 6.558596.1443 5.1893 4.2343 3.2793 2.3243 1.36929 0.955002 0.955002 0.955002 0.955002 0.955002 0.955002 0.955002 0.955002 0.955002 1.36929 2.3243 3.2793 4.2343 5.1893 6.14435.73001 4.77501 3.82001 2.86501 1.91 0.955002 0 0 0 0 0 0 0 0 0 0.955002 1.91 2.86501 3.82001 4.77501 5.730015.73001 4.77501 3.82001 2.86501 1.91 0.955002 0 0 0 0 0 0 0 0 0 0.955002 1.91 2.86501 3.82001 4.77501 5.730015.73001 4.77501 3.82001 2.86501 1.91 0.955002 0 0 0 0 0 0 0 0 0 0.955002 1.91 2.86501 3.82001 4.77501 5.730015.73001 4.77501 3.82001 2.86501 1.91 0.955002 0 0 0 0 0 0 0 0 0 0.955002 1.91 2.86501 3.82001 4.77501 5.730015.73001 4.77501 3.82001 2.86501 1.91 0.955002 0 0 0 0 0 0 0 0 0 0.955002 1.91 2.86501 3.82001 4.77501 5.730015.73001 4.77501 3.82001 2.86501 1.91 0.955002 0 0 0 0 0 0 0 0 0 0.955002 1.91 2.86501 3.82001 4.77501 5.730015.73001 4.77501 3.82001 2.86501 1.91 0.955002 0 0 0 0 0 0 0 0 0 0.955002 1.91 2.86501 3.82001 4.77501 5.730015.73001 4.77501 3.82001 2.86501 1.91 0.955002 0 0 0 0 0 0 0 0 0 0.955002 1.91 2.86501 3.82001 4.77501 5.730015.73001 4.77501 3.82001 2.86501 1.91 0.955002 0 0 0 0 0 0 0 0 0 0.955002 1.91 2.86501 3.82001 4.77501 5.730016.1443 5.1893 4.2343 3.2793 2.3243 1.36929 0.955002 0.955002 0.955002 0.955002 0.955002 0.955002 0.955002 0.955002 0.955002 1.36929 2.3243 3.2793 4.2343 5.1893 6.14436.55859 5.60359 4.64859 3.69359 2.73859 2.3243 1.91 1.91 1.91 1.91 1.91 1.91 1.91 1.91 1.91 2.3243 2.73859 3.69359 4.64859 5.60359 6.558596.97289 6.01788 5.06288 4.10788 3.69359 3.2793 2.86501 2.86501 2.86501 2.86501 2.86501 2.86501 2.86501 2.86501 2.86501 3.2793 3.69359 4.10788 5.06288 6.01788 6.972897.38718 6.43217 5.47717 5.06288 4.64859 4.2343 3.82001 3.82001 3.82001 3.82001 3.82001 3.82001 3.82001 3.82001 3.82001 4.2343 4.64859 5.06288 5.47717 6.43217 7.387187.80147 6.84647 6.43217 6.01788 5.60359 5.1893 4.77501 4.77501 4.77501 4.77501 4.77501 4.77501 4.77501 4.77501 4.77501 5.1893 5.60359 6.01788 6.43217 6.84647 7.801478.21576 7.80147 7.38718 6.97289 6.55859 6.1443 5.73001 5.73001 5.73001 5.73001 5.73001 5.73001 5.73001 5.73001 5.73001 6.1443 6.55859 6.97289 7.38718 7.80147 8.21576
额,似乎有偏差?感觉明明该是整数的点却是小数。
不过偏差不大,能用就行。
试试把掩码改为5,听说这个精确一些:
8.4 7.7969 7.1938 6.5907 6.3938 6.1969 6 6 6 6 6 6 6 6 6 6.1969 6.3938 6.5907 7.1938 7.7969 8.47.7969 7 6.3969 5.7938 5.3938 5.1969 5 5 5 5 5 5 5 5 5 5.1969 5.3938 5.7938 6.3969 7 7.79697.1938 6.3969 5.6 4.9969 4.3938 4.1969 4 4 4 4 4 4 4 4 4 4.1969 4.3938 4.9969 5.6 6.3969 7.19386.5907 5.7938 4.9969 4.2 3.5969 3.1969 3 3 3 3 3 3 3 3 3 3.1969 3.5969 4.2 4.9969 5.7938 6.59076.3938 5.3938 4.3938 3.5969 2.8 2.1969 2 2 2 2 2 2 2 2 2 2.1969 2.8 3.5969 4.3938 5.3938 6.39386.1969 5.1969 4.1969 3.1969 2.1969 1.4 1 1 1 1 1 1 1 1 1 1.4 2.1969 3.1969 4.1969 5.1969 6.19696 5 4 3 2 1 0 0 0 0 0 0 0 0 0 1 2 3 4 5 66 5 4 3 2 1 0 0 0 0 0 0 0 0 0 1 2 3 4 5 66 5 4 3 2 1 0 0 0 0 0 0 0 0 0 1 2 3 4 5 66 5 4 3 2 1 0 0 0 0 0 0 0 0 0 1 2 3 4 5 66 5 4 3 2 1 0 0 0 0 0 0 0 0 0 1 2 3 4 5 66 5 4 3 2 1 0 0 0 0 0 0 0 0 0 1 2 3 4 5 66 5 4 3 2 1 0 0 0 0 0 0 0 0 0 1 2 3 4 5 66 5 4 3 2 1 0 0 0 0 0 0 0 0 0 1 2 3 4 5 66 5 4 3 2 1 0 0 0 0 0 0 0 0 0 1 2 3 4 5 66.1969 5.1969 4.1969 3.1969 2.1969 1.4 1 1 1 1 1 1 1 1 1 1.4 2.1969 3.1969 4.1969 5.1969 6.19696.3938 5.3938 4.3938 3.5969 2.8 2.1969 2 2 2 2 2 2 2 2 2 2.1969 2.8 3.5969 4.3938 5.3938 6.39386.5907 5.7938 4.9969 4.2 3.5969 3.1969 3 3 3 3 3 3 3 3 3 3.1969 3.5969 4.2 4.9969 5.7938 6.59077.1938 6.3969 5.6 4.9969 4.3938 4.1969 4 4 4 4 4 4 4 4 4 4.1969 4.3938 4.9969 5.6 6.3969 7.19387.7969 7 6.3969 5.7938 5.3938 5.1969 5 5 5 5 5 5 5 5 5 5.1969 5.3938 5.7938 6.3969 7 7.79698.4 7.7969 7.1938 6.5907 6.3938 6.1969 6 6 6 6 6 6 6 6 6 6.1969 6.3938 6.5907 7.1938 7.7969 8.4
不过精确当然也有代价,比如运算速度肯定不如 3。
我们把数据改大,测测效率:
#include <QElapsedTimer>
void mask_3_VS_5(const cv::Mat& src, int mask)
{QElapsedTimer timer;timer.start();for (int i = 0; i < 10; i++) {cv::Mat dst;cv::distanceTransform(src, dst, cv::DIST_L2, mask);}qint64 elapsed = timer.nsecsElapsed();qDebug() << "mask = " << mask << ", Elapsed time:" << elapsed / 1000000.0 << "ms";
}void Widget::on_pushButton_4_clicked()
{cv::Mat src(10000, 10000, CV_8UC1);for(int r = 0; r < 10000; r++) for(int c = 0; c < 10000; c++)if((rand() % 100) < 50) src.at<uchar>(r, c) = 0;else src.at<uchar>(r, c) = 255;mask_3_VS_5(src, 5);mask_3_VS_5(src, 3);
}
10000 x 10000
的图,跑 10 次, 打印结果如下:
嗯,截图为证:
精度高的运行速度还更快!竟然还有这种好事😋。
获得SDF
我们已经得到了非0点到最近0点距离。
但SDF要求有正有负,即把图分为2份,一个外,一个内:
我们拿到一个QImage,把它转为 Mat。
其中,alpha,即透明度为 0,即纯黑的我们称为 内,其他的称为 外。
额,算了,换个说法,黑色就是障碍物,其他就是背景。
障碍物内的值为 负,外的值为 正。
那么我们拿着两个一减就得到了SDF:
// image 中, alpha为 0 的表示背景
bool image_to_SDF(const QImage& image, cv::Mat* SDF)
{int r = image.height(), c = image.width();cv::Mat mat_background(r, c, CV_8UC1);for(int i = 0; i < r; i++){for(int j = 0; j < c; j++){mat_background.at<uchar>(i, j) = (image.pixelColor(j, i).alpha() == 0) ? 0 : 255;}}if(mat_background.empty()){qDebug() << "传了个空的image计算SDF";return false;} else {qDebug() << "准备计算sdf, 地图大小: rows = " << r << ", cols = " << c;}cv::Mat mat_background_dst; // 这里面为 0 的是障碍物, 为正的是背景cv::distanceTransform(mat_background, mat_background_dst, cv::DIST_L2, 5);cv::Mat mat_front(r, c, CV_8UC1); // 这里面为 0 的是障碍物cv::Mat mat_front_dst; // 这里面为 0 的是背景, 为正的是障碍物cv::bitwise_not(mat_background, mat_front);cv::distanceTransform(mat_front, mat_front_dst, cv::DIST_L2, 5);*SDF = mat_background_dst - mat_front_dst;return true;
}
再简单测试一下:
void Widget::on_pushButton_5_clicked()
{QImage image(21, 21, QImage::Format_ARGB32);for(int i = 0; i <= 20; i++){for(int j = 0; j <= 20; j++){if((5 < i && i < 15) && (5 < j && j < 15)) {image.setPixelColor(i, j, QColor(0, 0, 0, 0)); // 透明} else {image.setPixelColor(i, j, QColor(0, 0, 0, 255));}}}cv::Mat sdf;image_to_SDF(image, &sdf);QString ret;for(int r = 0; r < sdf.rows; r++){for(int c = 0; c < sdf.cols; c++){float f = sdf.at<float>(r, c);ret += QString::number(f).rightJustified(9, ' ');} ret += "\n";}cv::imshow("sdf", sdf);std::cout << ret.toStdString();
}
输出结果:
不错,和预期的一致。
然后我们再测测性能,用 10000 x 10000 的 image 跑它10次:
void Widget::on_pushButton_6_clicked()
{QImage image(10000, 10000, QImage::Format_ARGB32);for(int i = 0; i < 10000; i++){for(int j = 0; j < 10000; j++){int cur = rand() % 500 + 1;image.setPixelColor(i, j, QColor(0, 0, 0, cur < 255 ? cur : 0));}}QElapsedTimer timer;timer.start();qDebug() << "begin:" << timer.nsecsElapsed() / 1000000.0 << "ms";for (int i = 0; i < 10; i++) {cv::Mat sdf;image_to_SDF(image, &sdf);qDebug() << "第" << i << "次计算完成, time : " << timer.nsecsElapsed() / 1000000.0 << "ms";}qDebug() << "end:" << timer.nsecsElapsed() / 1000000.0 << "ms";
}
有点慢,但看了下,主要慢在我们每次都构造了个 cv::Mat sdf,
这里得判断 10000 x 10000次 image 是不是透明的。
那么优化方案就很明显了,我们别每次重新构造 cv::Mat 了,
我们在创建、修改 QImage时,顺便带一个 cv::Mat,
算 SDF 时,直接使用这个 cv::Mat 就行。
bool get_SDF(const cv::Mat& background, cv::Mat* SDF)
{if(background.empty()){qDebug() << "传了个空的background计算SDF";return false;} else {qDebug() << "准备计算sdf, 地图大小: rows = " << background.rows << ", cols = " << background.cols;}cv::Mat background_dst; // 这里面为 0 的是障碍物, 为正的是背景cv::distanceTransform(background, background_dst, cv::DIST_L2, 5);cv::Mat front(background.rows, background.cols, CV_8UC1); // 这里面为 0 的是障碍物cv::Mat front_dst; // 这里面为 0 的是背景, 为正的是障碍物cv::bitwise_not(background, front);cv::distanceTransform(front, front_dst, cv::DIST_L2, 5);*SDF = background_dst - front_dst;return true;
}void Widget::on_pushButton_7_clicked()
{QImage background_image(10000, 10000, QImage::Format_ARGB32);cv::Mat background_mat(10000, 10000, CV_8UC1);for(int r = 0; r < 10000; r++){for(int c = 0; c < 10000; c++){int alpha = (rand() % 2 == 0) ? 0 : (rand() % 255 + 1); // 差不多 50% 是 0background_image.setPixelColor(c, r, QColor(0, 0, 0, alpha));background_mat.at<uchar>(r, c) = (alpha == 0) ? 0 : 255;}}QElapsedTimer timer;timer.start();qDebug() << "begin:" << timer.nsecsElapsed() / 1000000.0 << "ms";for (int i = 0; i < 10; i++) {cv::Mat sdf;get_SDF(background_mat, &sdf);qDebug() << "第" << i << "次计算完成, time : " << timer.nsecsElapsed() / 1000000.0 << "ms";}qDebug() << "end:" << timer.nsecsElapsed() / 1000000.0 << "ms";
}
嘿嘿,非常不错,效率高多了。
至于加载,那不是我们关心的,毕竟每个游戏登录时都会让你等半天。
嗯,不过我们可以看看创建一个 10000 x 10000 的 image要多久,以及带上一个 cv::Mat又要多久:
void Widget::on_pushButton_8_clicked()
{QElapsedTimer timer;timer.start();qDebug() << "begin:" << timer.nsecsElapsed() / 1000000.0 << "ms";for(int i = 0; i < 1; i++){QImage background_image(10000, 10000, QImage::Format_ARGB32);for(int r = 0; r < 10000; r++){for(int c = 0; c < 10000; c++){int alpha = (rand() % 2 == 0) ? 0 : (rand() % 255 + 1); // 差不多 50% 是 0background_image.setPixelColor(c, r, QColor(0, 0, 0, alpha));}}}qDebug() << "end:" << timer.nsecsElapsed() / 1000000.0 << "ms";timer.restart();qDebug() << "begin:" << timer.nsecsElapsed() / 1000000.0 << "ms";for(int i = 0; i < 1; i++){QImage background_image(10000, 10000, QImage::Format_ARGB32);cv::Mat background_mat(10000, 10000, CV_8UC1);for(int r = 0; r < 10000; r++){for(int c = 0; c < 10000; c++){int alpha = (rand() % 2 == 0) ? 0 : (rand() % 255 + 1); // 差不多 50% 是 0background_image.setPixelColor(c, r, QColor(0, 0, 0, alpha));background_mat.at<uchar>(r, c) = (alpha == 0) ? 0 : 255;}}}qDebug() << "end:" << timer.nsecsElapsed() / 1000000.0 << "ms";
}
可以看到差不了多久,说明 cv::Mat 的创建还是很快的。
希望本篇文章对你有所帮助!并激发你进一步探索编程的兴趣!
本人仅是个C语言初学者,如果你有任何疑问或建议,欢迎随时留言讨论!让我们一起学习,共同进步!
相关文章:

Qt -使用OpenCV得到SDF
博客主页:【夜泉_ly】 本文专栏:【暂无】 欢迎点赞👍收藏⭐关注❤️ 目录 cv::MatdistanceTransform获得SDF 本文的目标, 是简单学习并使用OpenCV的相关函数, 并获得QImage的SDF(Signed Distance Field 有向距离场) 至…...
Python 中Vector类的格式化实现,重点拆解其超球面坐标系的设计精髓
📌 高维向量的格式化革新 在Vector类第5版中,格式化系统迎来重要升级: 坐标系转型:从Vector2d的极坐标(p’后缀)升级为超球面坐标(h’后缀),支持n维空间维度突破&#…...

DDR5 ECC详细原理介绍与基于协议讲解
本文篇幅较长,涉及背景原理介绍方便大家理解其运作方式 以及 基于DDR5协议具体展开介绍。 背景原理介绍 上图参考:DDR 内存中的 ECC 写入操作时,On-die ECC的工作过程如下: SoC将需要写入到Memory中的数据发送给控制器控制器将需要写入的数据直接发送给DRAM芯片在DDR5 DR…...
Linux系统之gettext详解
gettext 是一个用于国际化(i18n)和本地化(l10n)的工具集,旨在帮助开发者创建多语言支持的应用程序。它主要通过提供一系列工具和库来简化文本翻译过程。 gettext 工作流程 标记源代码:在源代码中用 _() 函…...
基于Qt封装数据库基本增删改查操作,支持多线程,并实现SQLite数据库单例访问
抽出来的,直接用就行 头文件CPP文件使用示例 头文件 #ifndef DATABASECOMMON_H #define DATABASECOMMON_H/** 单例封装SQLite通用操作,支持多线程调用;可扩展兼容其他数据库,照着SysRunDatabase写,并且重载openDataba…...

EC800X QuecDuino开发板介绍
支持的模组列表 EG800KEC800MEC800GEC800E 功能列表 基本概述 EC800X QuecDuino EVB 搭载移远 EC800 系列模组。支持模组型号为: EC800M 系列、EC800K 系列、EG800K 系列、EC800E 系列等。 渲染图 开发板的主要组件、接口布局见下图 资料下载 EC800X-QuecDui…...
ARM P15协处理器指令详解:架构、编程与应用实践
ARM P15协处理器指令详解:架构、编程与应用实践 引言 在ARM处理器架构中,协处理器(Coprocessor)系统是扩展处理器功能的关键机制,其中CP15(即协处理器15)作为系统控制协处理器,承担着内存管理、缓存控制、系统配置等核心功能。本…...

PHP轻量级聊天室源码(源码下载)
最新版本:v2.1.2 (2024.08更新) 运行环境:PHP5.6(无需MySQL) 核心特性:手机电脑自适应、TXT数据存储、50条历史消息 适用场景:小型社区/企业内网/教育培训即时通讯 一、核心功能亮点(SEO关键词布…...
MySQL数据表添加字段(三种方式)
基本概念解析 数据表由行和列组成,专业术语中: 字段(Field):表的纵向列结构记录(Record):表的横向行数据 字段添加方法详解 MySQL支持三种字段添加位置,语法格式均通…...

leetcode hot100刷题日记——33.二叉树的层序遍历
解题总结二维vector的初始化方法 题目描述情况1:不确定行数和列数情况2:已知行数和列数情况3:已知行数但不知道列数情况4:已知列数但不知道行数 题目描述 解答:用队列 思路都差不多,我觉得对于我自己来说&a…...
2、PyTorch基础教程:从张量到神经网络训练
1、PyTorch基础 PyTorch 是一个开源的深度学习框架,以其灵活性和动态计算图而广受欢迎。 PyTorch 主要有以下几个基础概念:张量(Tensor)、自动求导(Autograd)、神经网络模块(nn.Module…...

《数据结构初阶》【番外篇:快速排序的前世今生】
【番外篇:快速排序的前世今生】目录 前言:---------------起源---------------一、诞生:二、突破:三、核心: ---------------发展---------------1. 早期版本:简单但不稳定1960 年:初始版本 2. …...

【笔记】基于 MSYS2(MINGW64)的 Poetry 虚拟环境创建指南
#工作记录 基于 MSYS2(MINGW64)的 Poetry 虚拟环境创建指南 一、背景说明 在基于 MSYS2(MINGW64)的环境中,使用 Poetry 创建虚拟环境是一种高效且灵活的方式来管理 Python 项目依赖。本指南将详细介绍如何在 PyChar…...

PINNs案例——二维磁场计算
基于物理信息的神经网络是一种解决偏微分方程计算问题的全新方法… 有关PINN基础详见:PINNs案例——中心热源温度场预测问题的torch代码 今日分享代码案例:二维带电流源磁场计算 该案例参考学习论文:[1]张宇娇,孙宏达࿰…...
Hive SQL 中 BY 系列关键字全解析:从排序、分发到分组的核心用法
一、排序与分发相关 BY 关键字 1. ORDER BY:全局统一排序 作用:对查询结果进行全局排序,确保最终结果集完全有序(仅允许单个 Reducer 处理数据)。 语法: SELECT * FROM table_name ORDER BY column1 [A…...
数据类型检测有哪些方式?
typeof 其中数组 对象 null都会判断为Object,其他正确 typeof 2 // number typeof true //bolean typeof str //string typeof [] //Object typeof function (){} // function typeof {} //object typeof undefined //undefined typeof null // nullinstanceof 判断…...

算法打开13天
41.前 K 个高频元素 (力扣347题) 给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。 示例 1: 输入: nums [1,1,1,2,2,3], k 2 输出: [1,2]示例 2: 输入: nums [1], k 1 输出: …...
Freeqwq 世界首个免费无限制 分布式 AI 算力平台 https://qwq.aigpu.cn/
官网:Free QWQ - 免费分布式 AI 算力平台 基于来自全国各地 50 台家用电脑的 3090、4080、4090 显卡分布式算力,我们为开发者提供完全免费、无限制的 QwQ 32B 大语言模型 API。无需注册,无需充值,立即获取 API Key 开始使用。 …...

广告拦截器:全方位拦截,畅享无广告体验
在数字时代,广告无处不在。无论是浏览网页、使用社交媒体,还是观看视频,广告的频繁弹出常常打断我们的体验,让人不胜其烦。更令人担忧的是,一些广告可能包含恶意软件,威胁我们的设备安全和个人隐私。AdGuar…...
.net Avalonia应用程序生命周期
.NET Avalonia 应用程序生命周期全解析 在 .NET 开发领域,Avalonia 作为一个跨平台的 UI 框架,为开发者提供了强大的功能和灵活性。了解 Avalonia 应用程序的生命周期,对于构建高效、稳定的应用至关重要。本文将深入探讨 Avalonia 应用程序生…...

主数据编码体系全景解析:从基础到高级的编码策略全指南
在数字化转型的浪潮中,主数据管理(MDM)已成为企业数字化转型的基石。而主数据编码作为MDM的核心环节,其设计质量直接关系到数据管理的效率、系统的可扩展性以及业务决策的准确性。本文将系统性地探讨主数据编码的七大核心策略&…...

Selenium操作指南(全)
🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 大家好,今天带大家一起系统的学习下模拟浏览器运行库Selenium,它是一个用于Web自动化测试及爬虫应用的重要工具。 Selenium测试直接运行在…...
Go语言中的数据类型转换
Go 语言中只有强制类型转换,没有隐式类型转换。 1. 数值类型之间的相互转换 1.1. 整型和整型之间的转换 package main import "fmt"func main() {var a int8 20var b int16 40fmt.Println(int16(a) b)// 60 }1.2. 浮点型和浮点型之间的转换 packag…...
35、请求处理-【源码分析】-自定义参数绑定原理
35、请求处理-【源码分析】-自定义参数绑定原理 自定义参数绑定原理主要涉及Spring Boot如何将HTTP请求中的参数自动绑定到控制器方法的自定义对象参数上。以下是详细的解析: ### 1. 参数解析器的选择 - **HandlerMethodArgumentResolverComposite**: - …...

智绅科技——科技赋能健康养老,构建智慧晚年新生态
当老龄化浪潮与数字技术深度碰撞,智绅科技以 “科技赋能健康,智慧守护晚年” 为核心理念,锚定数字健康与养老服务赛道,通过人工智能、物联网、大数据等技术集成,为亚健康群体与中老年人群构建 “监测 - 预防 - 辅助 - …...

STM32通过KEIL pack包轻松移植LVGL,并学会使用GUI guider
先展示最终实现的功能效果如下: 1.目的与意义 之前在学习STM32移植LVGL图形库的时候,搜到的很多教程都是在官网下载LVGL的文件包,然后一个个文件包含进去,还要添加路径,还要给文件改名字,最后才能修改程序…...
day43 python Grad-CAM
目录 一、为什么需要 Grad-CAM? 二、Grad-CAM 的原理 三、Grad-CAM 的实现 1. 模块钩子(Module Hooks) 2. Grad-CAM 的实现代码 四、学习总结 在深度学习领域,神经网络模型常常被视为“黑盒”,因为其复杂的内部结…...
在 Ubuntu 上挂载其他硬盘的步骤
一、查看当前磁盘信息 打开终端,执行: lsblk 这个命令会列出所有的块设备(包括硬盘和分区)。比如输出可能如下: NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 1.8T 0 disk └─sda1 8:1 0 …...

SQL的查询优化
1. 查询优化器 1.1. SQL语句执行需要经历的环节 解析阶段:语法分析和语义检查,确保语句正确;优化阶段:通过优化器生成查询计划;执行阶段:由执行器根据查询计划实际执行操作。 1.2. 查询优化器 查询优化器…...

MCU如何从向量表到中断服务
目录 1、中断向量表 2、编写中断服务例程 中断处理的核心是中断向量表(IVT),它是一个存储中断服务例程(ISR)地址的内存结构。当中断发生时,MCU通过IVT找到对应的ISR地址并跳转执行。本文将深入探讨MCU&am…...