关键点简介
关键点也称为兴趣点,它是 2D 图像或 3D 点云或曲面模型上,可以通过检测标准来获取的具有稳定性、区别性的点集。从技术上来说,关键点的数量比原始点云或图像的数据量少很多,其与局部特征描述子结合组成关键点描述子。常用来构成原始数据的紧凑表示,具有代表性与描述性,从而加快后续识别、追踪等对数据的处理速度 。(也就是能够代表对象特征的子点集)
固而,关键点提取就成为了2D与3D信息处理中不可获缺的关键技术
关键点概念以及算法
NARF(Normal Aligned Radial Feature)关键点是为了从深度图像中识别物体而提出的,关键点探测的重要一步是减少特征提取时的搜索空间,对NARF关键点提取过程有以下要求:
- 提取的过程必须考虑边缘以及物体表面变化信息
- 即使换了不同的视角,关键点的位置必须稳定的可以被重复探测
-
关键点所在的位置必须有稳定的支持区域,可以计算描述子和估计唯一的法向量。
为了满足上述要求,可以通过以下探测步骤来进行关键点提取: -
遍历每个深度图像点,通过寻找在邻近区域有深度突变的位置进行边缘检测
- 遍历每个深度图像点,根据近邻区域的表面变化决定一测度表面变化的系数,以及变化的主方向;
- 根据第2步找到的主方向计算兴趣值,表征该方向与其他方向的不同,以及该处表面的变化情况,即该点有多稳定;
- 对兴趣值进行平滑过滤;
-
进行非极大值抑制找到最终的关键点,即为 NARF 关键点。
-
实践项目
narf_keypoint_extraction.cpp
typedef pcl::PointXYZ PointType;
// --------------------
// -----Parameters-----
// --------------------
float angular_resolution=0.5f; //应该时求解 局部描述子的时候,对应的beam的间隔角度 这个觉得了直方差的维度
float support_size=0.2f; //这个有两个作用,一个是 support_size/2 对应的算术特征点提取的局部参考空间大小,另外一个在求解局部描述子时,对应的patch的覆盖范围
pcl::RangeImage::CoordinateFrame coordinate_frame=pcl::RangeImage::CAMERA_FRAME; //深度世界坐标系的类型
bool setUnseenToMaxRange=false; //对于超出传感器范围的量采取的措施
void printUsage(const char *progName) {
std::cout << "\n\nUsage: " << progName << " [options] \n\n"
<< "Options:\n"
<< "-------------------------------------------\n"
<< "-r angular resolution in degrees (default " << angular_resolution << ")\n"
<< "-c coordinate frame (default " << (int) coordinate_frame << ")\n"
<< "-m Treat all unseen points as maximum range readings\n"
<< "-s support size for the interest points (diameter of the used sphere - "
<< "default " << support_size << ")\n"
<< "-h this help\n"
<< "\n\n";
}
// --------------
// -----Main-----
// --------------
int main(int argc,char **argv){
// --------------------------------------
// -----Parse Command Line Arguments-----
// --------------------------------------
// 显示help参数
if (pcl::console::find_argument(argc, argv, "-h") >= 0) {
printUsage(argv[0]);
return 0;
}
// 对待超出距离的点的处理方法,true就是最大化处理
if (pcl::console::find_argument(argc, argv, "-m") >= 0) {
setUnseenToMaxRange = true;
cout << "Setting unseen values in range image to maximum range readings.\n";
}
// 读取坐标轴的类型,这个可以查查api的定义
int tmp_coordinate_frame;
if (pcl::console::parse(argc, argv, "-c", tmp_coordinate_frame) >= 0) {
coordinate_frame = pcl::RangeImage::CoordinateFrame(tmp_coordinate_frame);
cout << "Using coordinate frame " << (int) coordinate_frame << ".\n";
}
//从命令参数里面获取angular_resolution
//从命令参数里面获取support_size
if (pcl::console::parse(argc, argv, "-s", support_size) >= 0)
cout << "Setting support size to " << support_size << ".\n";
if (pcl::console::parse(argc, argv, "-r", angular_resolution) >= 0)
cout << "Setting angular resolution to " << angular_resolution << "deg.\n";
//从角度变换到弧度
angular_resolution = pcl::deg2rad(angular_resolution);
// ------------------------------------------------------------------
// -----Read pcd file or create example point cloud if not given-----
// ------------------------------------------------------------------
// 下面需要完成以下内容,从文件中读取pcd点云
// 通过点云,转变成深度图
// 加载远景处理,如果没有的点,就直接最大化距离就可以了
pcl::PointCloud<PointType>::Ptr point_cloud_ptr(new pcl::PointCloud<PointType>);
pcl::PointCloud<PointType> &point_cloud=*point_cloud_ptr;
pcl::PointCloud<pcl::PointWithViewpoint> far_ranges;
Eigen::Affine3f scene_sensor_pose(Eigen::Affine3f::Identity());
//在命令行参数列表里面获取pcd文件名并加载
std::vector<int> pcd_filename_indices = pcl::console::parse_file_extension_argument(argc, argv, "pcd");
if (!pcd_filename_indices.empty()) {
std::string filename = argv[pcd_filename_indices[0]];
if (pcl::io::loadPCDFile(filename, point_cloud) == -1) {
cerr << "Was not able to open file \"" << filename << "\".\n";
printUsage(argv[0]);
return 0;
}
//从传感器位置里面获取场景传感器参数
scene_sensor_pose = Eigen::Affine3f(Eigen::Translation3f(point_cloud.sensor_origin_[0],
point_cloud.sensor_origin_[1],
point_cloud.sensor_origin_[2])) *
Eigen::Affine3f(point_cloud.sensor_orientation_);
//加载远景处理图,如果没有就不用了
std::string far_ranges_filename = pcl::getFilenameWithoutExtension(filename) + "_far_ranges.pcd";
if (pcl::io::loadPCDFile(far_ranges_filename.c_str(), far_ranges) == -1)
std::cout << "Far ranges file \"" << far_ranges_filename << "\" does not exists.\n";
}else{
setUnseenToMaxRange=true;
cout << "\nNo *.pcd file given => Generating example point cloud.\n\n";
for (float x=-0.5f;x0.5f;x+=0.01f){
for(float y=-0.5f;y0.5f;y+=0.01f){
PointType point;
point.x=x;
point.y=y;
point.z=2.0f-y;
point_cloud.points.push_back(point);
}
}
point_cloud.width=(int)point_cloud.points.size();
point_cloud.height=1;
}
// -----------------------------------------------
// -----Create RangeImage from the PointCloud-----
// -----------------------------------------------
float nosie_level=0.0;
//最小范围
float min_range=0.0f;
int border_size=1;
std::shared_ptr<pcl::RangeImage> range_image_ptr(new pcl::RangeImage);
pcl::RangeImage &range_image=*range_image_ptr;
// 将点云变成深度图
// border_size=1 边框尺度
// 噪声级别,如果想要更好的去除噪声,就去更高的值
range_image.createFromPointCloud(point_cloud,angular_resolution,
pcl::deg2rad(360.0f),pcl::deg2rad(180.0f),
scene_sensor_pose,
coordinate_frame,
nosie_level,
min_range,
border_size);
// 加载元景图
range_image.integrateFarRanges(far_ranges);
// 对于没有探测到的点的处理办法
if(setUnseenToMaxRange)
range_image.setUnseenToMaxRange();
// --------------------------------------------
// -----Open 3D viewer and add point cloud-----
// --------------------------------------------
pcl::visualization::PCLVisualizer viewer("3D Viewer");
viewer.setBackgroundColor(1, 1, 1);
//颜色处理
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointWithRange> range_image_color_handler(range_image_ptr,
255, 0, 0);
//深度图加载颜色处理
viewer.addPointCloud(range_image_ptr, range_image_color_handler, "range image");
//设定点大小的属性
viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "range image");
//增加显示坐标轴
viewer.addCoordinateSystem (1.0f, "global");
//PointCloudColorHandlerCustom<PointType> point_cloud_color_handler (point_cloud_ptr, 150, 150, 150);
//viewer.addPointCloud (point_cloud_ptr, point_cloud_color_handler, "original point cloud");
//初始化viewer参数
viewer.initCameraParameters();
//setViewerPose (viewer, range_image.getTransformationToWorldSystem ());
// --------------------------
// -----Show range image-----
// --------------------------
// 显示深度图
pcl::visualization::RangeImageVisualizer range_image_widget("Range image");
range_image_widget.showRangeImage(range_image);
// --------------------------------
// -----Extract NARF keypoints-----
// --------------------------------
//边框抽取器
pcl::RangeImageBorderExtractor range_image_border_extrator;
pcl::NarfKeypoint narf_keypoint_detector(&range_image_border_extrator);
narf_keypoint_detector.setRangeImage(&range_image);
//计算NARF可信度时的范围
narf_keypoint_detector.getParameters().support_size=support_size;
//narf_keypoint_detector.getParameters ().add_points_on_straight_edges = true;
//narf_keypoint_detector.getParameters ().distance_for_additional_points = 0.5;
// 关键点的索引
pcl::PointCloud<int> keypoint_indices;
//获取关键点 索引
narf_keypoint_detector.compute(keypoint_indices);
std::cout << "Found " << keypoint_indices.points.size() << " key points.\n";
// ----------------------------------------------
// -----Show keypoints in range image widget-----
// ----------------------------------------------
//for (size_t i=0; i<keypoint_indices.points.size (); ++i)
//range_image_widget.markPoint (keypoint_indices.points[i]%range_image.width,
//keypoint_indices.points[i]/range_image.width);
// -------------------------------------
// -----Show keypoints in 3D viewer-----
// -------------------------------------
//显示关键点
pcl::PointCloud<pcl::PointXYZ>::Ptr keypoints_ptr(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ> &keypoints=*keypoints_ptr;
keypoints.points.resize(keypoint_indices.points.size());
for(size_t i=0;i<keypoint_indices.points.size();i++)
{
keypoints.points[i].getVector3fMap() = range_image.points[keypoint_indices.points[i]].getVector3fMap();
}
// 颜色处理,与点大小
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> keypoints_color_handler(keypoints_ptr, 0, 255, 0);
viewer.addPointCloud<pcl::PointXYZ>(keypoints_ptr, keypoints_color_handler, "keypoints");
viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 5, "keypoints");
//--------------------
// -----Main loop-----
//--------------------
while (!viewer.wasStopped()) {
range_image_widget.spinOnce(); // process GUI events
viewer.spinOnce();
pcl_sleep(0.01);
}
}
Original: https://blog.csdn.net/weixin_38498629/article/details/123949026
Author: weixin_38498629
Title: PCL点云处理与关键点提取
相关阅读1
Title: NLP-Beginner 任务五:基于神经网络的语言模型+pytorch
NLP-Beginner 任务五:基于神经网络的语言模型+pytorch
- 传送门
- 一. 介绍
* - 1.1 任务简介
- 1.2 数据集
- 1.3 原数据解释
- 二. 特征提取——Word embedding(词嵌入)
- 三. 神经网络(LSTM+CRF)
* - 3.1 LSTM/GRU层
- 3.2 Loss
- 3.2.3 生成诗句
- 四. 代码及实现
* - 4.1 实验设置
- 4.2 结果展示
- 4.3 诗句展示
- 五. 总结
- 六. 自我推销
- 七. 下期预告
传送门
一. 介绍
1.1 任务简介
本次的 NLP(Natural Language Processing)任务是用LSTM、GRU来训练字符级的语言模型,通过语言模型来 生成诗句。
1.2 数据集
例子:
原数据(以其中两首诗为例):
巴山上峡重复重,阳台碧峭十二峰。荆王猎时逢暮雨,
夜卧高丘梦神女。轻红流烟湿艳姿,行云飞去明星稀。
目极魂断望不见,猿啼三声泪沾衣。
见尽数万里,不闻三声猿。但飞萧萧雨,中有亭亭魂。
千载楚襄恨,遗文宋玉言。至今青冥里,云结深闺门。
输入:每行最大字符数(包括标点符号):12;行数:6
输出诗句:投败兵家经。
登文新云幄,山藏鬼谷幽。
青来从所好,停策汉阴多。
云泻不见长,吴楚各依然。
千载楚襄恨,遗文宋玉言。
云雨从兹别,犹能骑马回。输入:
藏头关键字:春夏秋冬
输出诗句:春长碎,名客松醪酒。
夏寻雷震,鸣弦暂辍弹。
秋惨儿,山红水尚青。
冬指涛亦不尽,栖栖吴楚间。
1.3 原数据解释
真正的原数据在NLP-Beginner:poetryFromTang.txt。
本次数据为163首唐诗,注意到原始数据中有脏数据,如:一些诗句夹杂了一些英文字母,·由于错误较少,因此采用人工修改方式,把正确的诗句填入。最终得到163首完全正确的古诗。
修改后的数据在我的代码/数据集传送门。
二. 特征提取——Word embedding(词嵌入)
请参考NLP任务二,因为本次实战的语言为中文,没有比较好的预训练模型,因此采用随机初始化。
本次实战除了给定的各种序列类别之外,还要另外多加3个类别,分别是: < pad >,< start >,< end >,分别代表padding(即补位,使句子达到同一个长度),句子开头和句子结尾,总共C类标签。
注意: 本次实战由于数据量较少,采用batch大小为1,因此并无padding, 即padding的部分在本次实战中并不会真正参与到任何的计算。
三. 神经网络(LSTM+CRF)
本部分详细内容可以参考《神经网络与深度学习》 第6、15章
3.1 LSTM/GRU层
; 3.2 Loss
采用交叉熵函数(请参考NLP任务二)。
3.2.3 生成诗句
无论是生成随机诗句,还是藏头诗局,都需要先初始化一个向量。
对于生成随机诗句
- 随机初始化一个向量(长度为l h l_h l h )
- 输入到 LSTM / GRU 中,得到新的向量,这个向量长度为l h l_h l h
- 输入到全连接层,得到新的向量,长度为C C C ,代表下一个字在C种字符的得分
- 对该向量取最高分(最大值),对应第i个索引(index),就是下一个字i i i
- 如果下一个字是 "句号" 或者 "< end >",给该诗句画上句号,开始重复以上步骤生成下一句。
对于生成藏头诗句
- 选择一个在词嵌入字典中已有的字,获得其对应的特征向量(长度为l h l_h l h )
- 输入到 LSTM / GRU 中,得到新的向量,这个向量长度为l h l_h l h
- 输入到全连接层,得到新的向量,长度为C C C ,代表下一个字在C种字符的得分
- 对该向量取最高分(最大值),对应第i个索引(index),就是下一个字i i i
- 如果下一个字是 "句号" 或者 "< end >",给该诗句画上句号,开始重复以上步骤生成下一句。
四. 代码及实现
4.1 实验设置
- 样本量:poetryFromTang.txt
- 特征提取:Random Embedding
- 策略 : LSTM / GRU
- 学习率:4 * 10-3
- l h , l f l_h,\ l_f l h ,l f :50
- Batch 大小:1
- 训练轮数:150
4.2 结果展示
可以看到 GRU 比 LSTM 稍好一些
; 4.3 诗句展示
训练好模型后,可以利用模型生成古诗。(本次实战中,古诗生成采用带有LSTM的模型)
有两种生成方式:
- 不给定任何信息,生成古诗。
- 生成藏头诗。
这两种方式的初始化方式也有不同,即 LSTM 中的 h 0 , c 0 h_0,\ c_0 h 0 ,c 0 有不同的初始化方式,一种是 0向量初始化,另一种是随机初始化; 前者会生成固定的古诗,后者每次会生成不一样的古诗(但可通过选择随机种子使每次输出一样)。下面是一些例子:
可以看到有些诗句和唐诗原句一模一样,应该是训练集样本太少的缘故,有点过拟合了,数据集增大应该会消除这个问题。
五. 总结
所有NLP-Beginner 任务已经完结,整一个系列可以说是虎头蛇尾了orz,实际上任务五已经是我2021年4月份时候做的了,一直拖到了现在,很多细节也遗忘了,大家有问题的我尽力解答。前一段时间比较忙,部分私信来不及回,如果有问题,欢迎来问,我会尽力解答(不保证我也会orz)。
谢谢各位的阅读,欢迎各位对本文章指正或者进行讨论,希望可以帮助到大家!
六. 自我推销
七. 下期预告
后续可能会写一些和 图神经网络有关的博客(如果不忙的话orz),欢迎大家继续留意我的博客~
Original: https://blog.csdn.net/qq_42365109/article/details/121921018
Author: 0oTedo0
Title: NLP-Beginner 任务五:基于神经网络的语言模型+pytorch
相关阅读2
Title: 人工智能(AI)库TensorFlow 踩坑日记之二
这个库里面主要是一些常用的模型用tensorflow实现之后的代码。其中我用的是
models/tree/master/tutorials/image/cifar10 这个示例,上一篇也大致讲过了。
关于上次遇到问题是:
虽然训练了很多次,但是每次实际去用时都是相同的结果。这个问题主要原因是
在核心代码文件cifar10.py里
被我改成 batch_size =1
一开始我误以为这个batch要跟训练文件的.bin 文件里面的图片数量对应,其实不然。这个batch_size 是为了用
cifar10_input.py
创建一个图片跟标签的队列,每个队列128个元素,便于分布式处理。
由于改成1之后可能是影响是训练效果。导致整体的loss很高,所以识别率很差。有待进一步验证。
2018-03-11 修正
batch_size 作用就是一次性训练这么多次之后才开始做梯度下降,这样loss 的波动不会太大。
2018-06-19 补充
看完这篇文章之后终于对batch_size 有了一个更深刻的理解。就是越小的batch会导致局部的梯度波动大,难以收敛。
另外一个原因很可能是最致命的
上一篇讲到label的对应方式是
label 也是用string_input_producer 做了另外一条字符串队列
这其实是错误的,因为两条队列要完美保持一致,而且还不能加shuffle 参数 这个参数可以随机获取图片文件,以便训练模型效果更具备泛化能力。
shuffle=true 还是要加的。
label的获取方式就得另外想办法。
把 cifar10_input.py 方法 read_cifar10 改造如下:
其中 splitilenames ,diff 方法是我新增的,主要是为了把文件所在目录的路劲切出来
比如"H:\imagenet\fortest\n01440764" 切出来 "n01330764"。 这个方法是支持批量处理的。
之所以写的这么麻烦。是因为输入量是tensor,所以所有操作都必须按照tensorflow的api写。
diff方法(代码在下面) 是为了判定key 的分类名在所有分类里面的文件排序位置(数字0-1000以内)。用这个位置作为label。
这里 读者估计有一个疑问
"为啥不直接用分类名'n01330764'作为label标签去训练呢?"
这里也是迫于无奈,因为原始代码cifar10的后续功能有2个限制,1,label必须是int型,2label最大值不能大于分类总数。所以不能简单把"n"删除然后转成数字 1330764 。
否则会出各种错。修正这2个问题明显比我新增一个diff方法改动更大。
虽然不太优雅,各位看官轻拍。
2018-06-19 修正
后来这里取label的方法还是换成文件夹按字母排序后的位置作为label了。这样保险很多,而且性能也好一些。
好了,到止为止,train(训练过程)的代码就改完了,可以开始训练了。
cifar10_eval.py 这边需要改个地方。
通过参数传入 单图片的地址,用来放到生产环境执行识别程序。
先跑一下8.jpg 测试一下
得出来结果是0 之所以有这么多,是因为
cifar10_eval 原来的代码用了一部分跟训练代码一致的过程,其中训练代码中batchsize=128,导致虽然输入只有1张图,输出的结果还是有128个。有点多余,不过取其中一个作为结果就可以了。(这里可以在把batchsize改为1,只在运行时用1)
然后我用C# MVC写了一个页面。用来上传图片,然后输出中文结果。
主要核心代码是(C#):
主要是把图片改为 32*32 然后用Process 拉起python 去执行 cifar10_runsingle.py (这个是cifar10_eval.py 改造后的)。
然后用正则把 结果的数字切出来。
剩下就是把位置比如 0替换成"n01330764"
测试一下
Original: https://www.cnblogs.com/7rhythm/p/7270207.html
Author: 鬼柒
Title: 人工智能(AI)库TensorFlow 踩坑日记之二
相关阅读3
Title: nlp(贪心学院)——时序模型、HMM、隐变量模型、EM算法
任务225: 时序模型
随时间维度变化的
每时每刻有相关性(t时刻数据t+1时刻数据有相关性)
时序数据长度不确定
时序数据:如股票价格、语音、文本、温度
; 任务226: HMM的介绍
观测值、隐式变量
z是一个隐式的状态
可以是生成模型,从状态生成观测值
也可以是判别模型
任务227: HMM的应用例子
掷硬币案例
A硬币出现正面的概率是μ 1 \mu_{1}μ1
B硬币出现正面的概率是μ 2 \mu_{2}μ2
小明和我隔着一块不透明的布
小明有自己的顺序投A还是投B,我只能看到硬币最后是正面还是反面朝上
由此产生两个问题
(1)inference问题
(2)parameter estimation参数估计问题
(3)P(正反正...),计算边缘概率
能不能通过观测值估算出所有的参数,能不能通过参数知道抛硬币的次序
词性标注案例
; 任务228: HMM的参数
A——状态转移的概率,从一个状态变成另一个状态
B——生成的概率,某一个状态下看到某个观测值的概率
π \pi π——某个状态是句首单词状态的概率
估计参数
(1)(x已知,z已知)complete case——>mle估计参数
(2)(x已知,z未知)incomplete case——>EM算法
任务229: HMM中的Inference问题
已知观测值、已知参数的情况下讨论
第一种方法:使用枚举法
枚举z不同情况的组合
第二种方法:使用维特比算法
动态规划适合解决指数级别的复杂度,但可以通过存储中间的过程来去减轻计算量
维特比算法为什么适合HMM?
HMM有限制条件——隐式变量z i z_{i}z i 只会和前后z i z_{i}z i 有联系
从左到右,每一列填好,找出最后一列哪个结点分数最大,反向把整条路径找出来
; 任务230-232: HMM中的F B算法 

通过forward和backward算法,可以计算P(z k z_{k}z k ∣ |∣x x x)的概率,方便后续的参数估计
任务233: Data Representation
原先的特征存在的问题
(1)冗余
(2)噪音
(3)有些特征不需要
更低维的空间有更好的特征表示方法
; 任务234: Latent Variable Models(隐变量模型)
隐变量模型——>EM算法解决掉
HMM,GMM(kmeans是其特例)都是经典的隐变量模型
传统逻辑回归(x,y)先在多了一个变量z——隐变量
隐变量gender、eye color、hair color、pose生成图片
x的维度比z的维度更高
现有z后有x
z之间有相关性
x之间没有相关性
任务235: Complete vs Incomplete Case
参数估计时
; 任务236: MLE for Complete and Incomplete Case
任务237: EM Derivation
; 任务238: Remarks on EM
EM算法不能保证全局最优解,只能保证局部最优解
EM算法,严格递增(一定会converge收敛)
任务239: K-means
; 任务240: K-means Cost Function
任务241: MLE for GMM
没有∑ \sum ∑就是kmeans
; 任务244: HMM中的参数
任务245: Complete vs Incomplete Case
; 任务247: Incomplete Case
任务248: EM算法回顾
先求z的期望,再求ln这个式子
; 任务249: F B算法回顾
任务250: 估计PI 
; 任务251: 估计B
任务252: 估计A
这里讨论观测、状态变量都是离散的HMM的情况
Original: https://blog.csdn.net/Grateful_Dead424/article/details/124611770
Author: Grateful_Dead424
Title: nlp(贪心学院)——时序模型、HMM、隐变量模型、EM算法