GCNv2_SLAM-CPU详细安装教程(ubuntu18.04)
GCNv2_SLAM-CPU详细安装教程-ubuntu18.04
- 前言
- 一、安装第三方库
- 1.安装Pangolin
- 2.安装OpenCV
- 3.安装Eigen
- 4.安装Pytorch(c++)
- 二、安装以及运行GCNv2_SLAM
- 1.安装编译GCNv2_SLAM
- 2.RGBD模式模式运行演示案例
- 总结
前言
paper:https://arxiv.org/pdf/1902.11046.pdf
githup::https://github.com/jiexiong2016/GCNv2_SLAM?tab=readme-ov-file
最近在ubuntu18.04上配置GCNv2_SLAM运行环境时踩了很多坑,在这期间查阅了很多资料和博客,于是想对安装过程进行总结,方便自己反复查阅以及分享经验避免大家重复踩坑。
博主是用docker在ubuntu18.04容器中安装的GCNv2_SLAM,已经打包成docker的镜像文件分享给大家。
因为博主的显卡安装不了低版本的cuda,对应低版本的pytorch只能使用cpu,因此暂时讲解cpu版本的安装教程
# 查看ubuntu版本号
lsb_release -a

安装前的准备:安装cmake、git 、gcc 和g++
# 更新apt库,更新软件列表
sudo apt-get update
apt-get源修改参考
# 安装git,用于从Github上克隆项目到本地
sudo apt-get install git
# 安装cmake,用于程序的编译
sudo apt-get install cmake
# 安装gcc和g++,安装c和c++编译器
sudo apt-get install gcc g++
一、安装第三方库
# 建立一个GCNv2_SLAM的文件夹,建议将所有的第三方库以及GCNv2_SLAM源码都放入其中
mkdir GCNv2_SLAM
可能需要安装百度云:
# 安装百度云,xxx.deb是自己下载的版本
sudo dpkg -i baidunetdisk_4.17.7_amd64.deb
1.安装Pangolin
Pangolin是对OpenGL进行封装的轻量级的OpenGL输入/输出和视频显示的库。
1.安装依赖项
sudo apt-get install libgl1-mesa-dev
sudo apt-get install libglew-dev
sudo apt-get install libboost-dev libboost-thread-dev libboost-filesystem-dev
sudo apt-get install libpython2.7-dev
2.安装 Pangolin
通过链接或通过git下载Pangolin源码(不推荐,问题很多)
# 需要科学上网
git clone --recursive https://github.com/stevenlovegrove/Pangolin.git
强烈推荐Pangolin 0.6(稳定版) 提取码:45bo
# 解压并重命名为Pangolin
unzip Pangolin-0.6.zip && mv Pangolin-0.6 Pangolin
# 开始编译和安装
cd Pangolin
mkdir build && cd build
cmake -DCPP11_NO_BOOST=1 ..
sudo make install
3.验证安装是否完成
# 验证
cd ../examples/HelloPangolin
mkdir build && cd build
cmake ..
make
./HelloPangolin
若安装成功,则会弹出以下窗口:

2.安装OpenCV
可以参考该链接
1.安装依赖项
# 解决:Unmet dependencies.Try'apt--fix-broken install'with no packages(or specify a solution)
sudo apt --fix-broken install
sudo apt-get update
sudo apt-get upgradesudo apt-get install build-essential libgtk2.0-dev libavcodec-dev libavformat-dev libjpeg.dev
sudo apt-get install libtiff5.dev libswscale-dev# 解决:add-apt-repository: command not found
sudo apt-get install software-properties-commonsudo apt-get update
sudo add-apt-repository "deb http://security.ubuntu.com/ubuntu xenial-security main"
sudo apt-get update
sudo apt-get install libjasper1 libjasper-dev
2.安装 OpenCV3.4.5
OpenCV3.4.5源码 提取码:m27t (可在Github仓库右侧的Releases里找大于2.4.3版本的OpenCV)
# 解压并重命名为opencv
tar -xvf opencv-3.4.5.tar.gz && mv opencv-3.4.5 opencv
# 开始编译和安装
cd opencv
mkdir build && cd build
cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local ..
# 4线程数量,根据电脑性能选择合适的数字
make -j4
sudo make install
3.验证安装是否完成
# 查询OpenCV版本
pkg-config --modversion opencv
# 查询OpenCV库
pkg-config --cflags opencv
# 查询头文件目录
pkg-config --libs opencv

# 验证
cd opencv/samples/cpp/example_cmake
cmake .
make
./opencv_example
若安装成功,则会弹出以下窗口:

3.安装Eigen
1.安装 Eigen3.3.7
建议源码安装可以下载任意大于3.1.0.版本对应的文件。
# 解压并重命名为eigen
tar -xvf eigen-3.3.7.tar.gz && mv eigen-3.3.7 eigen
# 开始编译和安装
cd eigen
mkdir build && cd build
cmake ..
make
sudo make install# 在很多程序中include时经常使用#include <Eigen/Dense>而不是使用#include <eigen3/Eigen/Dense>
# 因此安装后需要将头文件从 /usr/local/include/eigen3/ 复制到 /usr/local/include
# 后续小节会有C++测试代码说明
sudo cp -r /usr/local/include/eigen3/Eigen /usr/local/include
2.测试eigen库安装完成
在home目录下新建一个test.cp 文件用以测试。
# 建立 test 测试文件
touch test_eigen.cpp
# 用gedit打开此测试文件,添加C++代码用于测试
gedit test_eigen.cpp
# 编译后生成一个test_eigen的可执行文件
g++ test_eigen.cpp -o test_eigen
# 在test_eigen可执行文件目录下执行以下命令,证明eigen库安装完成
./test_eigen
在test_eigen.cpp文件中添加的C++测试代码。
#include <iostream>
//需要将头文件从 /usr/local/include/eigen3/ 复制到 /usr/local/include
#include <Eigen/Dense>
//using Eigen::MatrixXd;
using namespace Eigen;
using namespace Eigen::internal;
using namespace Eigen::Architecture;
using namespace std;
int main()
{cout<<"*******************1D-object****************"<<endl;Vector4d v1;v1<< 1,2,3,4;cout<<"v1=\n"<<v1<<endl;VectorXd v2(3);v2<<1,2,3;cout<<"v2=\n"<<v2<<endl;Array4i v3;v3<<1,2,3,4;cout<<"v3=\n"<<v3<<endl;ArrayXf v4(3);v4<<1,2,3;cout<<"v4=\n"<<v4<<endl;
}

4.安装Pytorch(c++)
1.选择 Pytorch的版本:进入Pytorch的githup官网地址,按照下图步骤查询所需安装的pytorch版本。
博主试过1.12.0高版本的在以后执行GCNv2_SLAM出现错误,无解降低了pytorch版本。

2.Pytorch源码编译Libtorch
# 博主选择安装1.4.0版本
git clone --recursive -b v1.4.0 https://github.com/pytorch/pytorch
cd pytorch && mkdir build && cd build
# 构建 LibTorch 库, 建议python3而不是原始命令的python执行
# 因为2版本的python可能会报错
python3 ../tools/build_libtorch.py
编译成功

3.编译Libtorch过程中可能出现的错误
博主尝试过不同版本的pytorch执行算法,下列问题都是曾经在安装不同版本pytorch时候出现和解决过的,但最后用的1.4.0版本需要解决的错误并没有那么多。
- 假如git下载中途断掉,解决方案:
# 进入目录pytorch cd pytorch # 用于初始化和更新子模块。 git submodule update --init --recursive - 正在使用的Python版本(2.x版本)不支持。

解决方案:使用更高的python版本。python3 ../tools/build_libtorch.py - 找不到名为 setuptools 的模块。

解决方案:安装 setuptools 模块。# 在 Ubuntu 上安装 Python 3 版本的包管理器 pip sudo apt install python3-pip # 安装 setuptools 模块 pip3 install setuptools - 没有安装符合要求的CMake版本。

解决方案:更新CMake(参考教程)。 - 找不到名为 typing_extensions yaml dataclasses 等模块。

解决方案:安装 typing_extensions yaml dataclasses 等模块。pip3 install typing_extensions pip3 install pyyaml pip3 install dataclasses # 需要删除build重新进行编译 cd .. && sudo rm -rf build && mkdir build && cd build # 构建 LibTorch 库 python3 ../tools/build_libtorch.py - c++: internal compiler error: Killed (program cc1plus)编译器试图使用过多内存或系统资源时,导致操作系统终止了编译器进程。


解决方案1 (低性能机器不建议) :使用临时交换分区来解决,docker的ubuntu18.04容器的解决参考。
解决方案2 (低性能机器强烈建议) :还有一个方法是减少线程数量,需要修改pytorch源码pytorch/tools/setup_helpers/cmake.py:# 创建一个大小为 30GB 的交换文件 /swapfile ,根据需要调整 bs 和 count 参数来创建不同大小的交换文件 sudo dd if=/dev/zero of=/swapfile bs=30M count=1024 # 更改上交换文件 /swapfile 的权限 sudo chmod 600 /swapfile # mkswap 命令将指定的文件 /swapfile 标记为交换分区,并设置相应的文件系统标识 sudo mkswap /swapfile # swapon 命令将指定的文件 /swapfile 作为交换空间启用,并将其添加到系统的交换空间列表中 sudo swapon /swapfile # 重新打开黑框,需要删除build重新进行编译 sudo rm -rf build && mkdir build && cd build # 构建 LibTorch 库 python3 ../tools/build_libtorch.py # swapoff 命令将指定的交换空间文件或设备从系统中移除,并停止使用它作为虚拟内存的一部分 sudo swapoff /swapfile # 删除 /swapfile 交换分区 sudo rm /swapfile

# 修改线程数目max_jobs,博主指定了12个 # max_jobs 必须是string类型 max_jobs = '12'
二、安装以及运行GCNv2_SLAM
1.安装编译GCNv2_SLAM
# 通过git下载GCNv2_SLAM源码,需要科学上网
git clone https://github.com/jiexiong2016/GCNv2_SLAM.git
cd GCNv2_SLAM
# 赋予shell文件运行权限
chmod +x build.sh
# 需要科学上网
# 博主根据个人电脑性修改build.sh里的torch位置,即你下载pytorch的路径
./build.sh

编译CUP版本需要几个地方:
-
修改GCNv2_SLAM/src/GCNextractor.cc中的相关代码:
//第一处原代码: const char *net_fn = getenv("GCN_PATH"); net_fn = (net_fn == nullptr) ? "gcn2.pt" : net_fn; module = torch::jit::load(net_fn); //修改为: torch::DeviceType device_type; device_type = torch::kCPU; torch::Device device(device_type); const char *net_fn = getenv("GCN_PATH"); net_fn = (net_fn == nullptr) ? "gcn2.pt" : net_fn; module = torch::jit::load(net_fn,device);//第二处原代码: device_type = torch::kCUDA; //修改为: device_type = torch::kCPU;
-
修改GCNv2_SLAM/GCN2下gcn2_320x240.pt、gcn2_640x480.pt和gcn2_tiny_320x240.pt中的内容:

更改以gcn2_320x240.pt为例,使用zip解压:unzip gcn2_320x240.pt && sudo rm -rf gcn2_320x240.pt解压完成后进入到解压文件的code目录下打开gcn.py将cuda:0修改成cpu:

修改完成后使用zip压缩:zip -r gcn2_320x240.pt gcn
编译成功:

常见的错误
-
这个错误会被密密麻麻的错误信息覆盖导致找不到,建议首先排查,可以在一开始报错的时候就用Ctrl+V中断:
(博主在此前pytorch1.12.0版本是出现的错误),现在是pytorch1.4.0

解决措施:只需要在GCNv2_SLAM/CMakeLists.txt文件修改添加:set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 修改:set_property(TARGET rgbd_gcn PROPERTY CXX_STANDARD 11) set_property(TARGET rgbd_gcn PROPERTY CXX_STANDARD 14)

-
std::shared_ptr是pytorch1.0.1版本使用的变量类型,现在博主使用的是pytorch1.4.0版本:

解决措施:修改/GCNv2_SLAM/include/GCNextractor.h中的相关代码://原代码 std::shared_ptr<torch::jit::script::Module> module; //更改为 torch::jit::script::Module module;
-
module已经不是指针:

解决措施:修改GCNv2_SLAM/src/GCNextractor.cc中的相关代码://原代码 auto output = module->forward(inputs).toTuple(); //更改为 auto output = module.forward(inputs).toTuple();
-
因为pytorch1.3以前默认true,后续版本默认false,需要修改:

解决措施:以gcn2_320x240.pt为例,进入解压进入code目录下打开gcn.py修改内容,具有修改步骤此前内容已经阐述不再复述://原代码 _32 = torch.squeeze(torch.grid_sampler(input, grid, 0, 0)) //修改为 _32 = torch.squeeze(torch.grid_sampler(input, grid, 0, 0, True))
2.RGBD模式模式运行演示案例
TUM 数据集
数据下载链接,下载如下数据集

在GCNv2_SLAM工程下新建datasets/TUM,将数据集下载到其中。
# 新建datasets/TUM数据集文件夹
mkdir -p datasets/TUM
# 下载数据集到datasets/TUM文件夹内
# 解压数据集
cd datasets/TUM && tar -xvf rgbd_dataset_freiburg1_desk.tgz
需额外下载associate.py添加到数据文件夹下,注意:只能在Python2 环境下运行。

# associate.py需要numoy包
sudo apt-get install python-pip
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple numpy# 在数据文件夹里执行命令
python associate.py rgb.txt depth.txt > associate.txt
作用是使RGD和depth的数据做一个对齐,一 一对应。

执行以下命令显示效果
cd GCN2
GCN_PATH=gcn2_320x240.pt ./rgbd_gcn ../Vocabulary/GCNvoc.bin TUM3_small.yaml /root/GCNv2_SLAM/GCNv2_SLAM/datasets/TUM/rgbd_dataset_freiburg1_desk /root/GCNv2_SLAM//GCNv2_SLAM/datasets/TUM/rgbd_dataset_freiburg1_desk/associate.txt

总结
尽可能简单、详细的介绍GCNv2_SLAM(CPU)的安装流程以及解决了安装过程中可能存在的问题。后续会根据自己学到的知识结合个人理解讲解GCNv2_SLAM的原理和代码。
相关文章:
GCNv2_SLAM-CPU详细安装教程(ubuntu18.04)
GCNv2_SLAM-CPU详细安装教程-ubuntu18.04 前言一、安装第三方库1.安装Pangolin2.安装OpenCV3.安装Eigen4.安装Pytorch(c) 二、安装以及运行GCNv2_SLAM1.安装编译GCNv2_SLAM2.RGBD模式模式运行演示案例 总结 前言 paper:https://arxiv.org/pdf/1902.11046.pdf githup::https://…...
使用gitee自动备份文件
需求 舍友磁盘前两天gg了,里面的论文没有本地备份,最后费劲巴拉的在坚果云上找到了很早前的版本。我说可以上传到github,建一个私人仓库就行了,安全性应该有保证,毕竟不是啥学术大亨,不会有人偷你论文。但是…...
智慧城市新篇章:数字孪生的力量与未来
随着信息技术的迅猛发展和数字化浪潮的推进,智慧城市作为现代城市发展的新模式,正在逐步改变我们的生活方式和社会结构。在智慧城市的构建中,数字孪生技术以其独特的优势,为城市的规划、管理、服务等方面带来了革命性的变革。本文…...
python讲解(2)
目录 一.变量与赋值 二.字符串类型 引号: 三引号: 字符串拼接 三.len函数 四.注释 注释的方法 一.# 二.文档字符串 注释的要求 群体注释 五.python的报错 六.bool类型 一.变量与赋值 python中的变量是不需要声明的,直接定义即…...
安卓安装Magisk面具以及激活EdXposed
模拟器:雷电模拟器 安卓版本: Android9 文中工具下载链接合集:https://pan.baidu.com/s/1c1X3XFlO2WZhqWx0oE11bA?pwdr08s 前提准备 模拟器需要开启system可写入和root权限 一、安装Magisk 1. 安装magisk 将magisk安装包拖入模拟器 点击:…...
C到C++的敲门砖-1
文章目录 关键字命名空间输入和输出缺省参数函数重载 关键字 相较于C语言32个关键字: autodoubleintstructbreakelselongswitchcaseenumregistertypedefcharexternreturnunionconstfloatshortunsignedcontinueforsignedvoiddefaultgotosizeofvolatiledoifwhilesta…...
Qt文件以及文件夹相关类(QDir、QFile、QFileInfo)的使用
关于Qt相关文件读写操作以及文件夹的一些知识,之前也写过一些博客: Qt关于路径的处理(绝对路径、相对路径、路径拼接、工作目录、运行目录)_qt 相对路径-CSDN博客 C/Qt 读写文件_qt c 读取文本文件-CSDN博客 C/Qt读写ini文件_…...
ChatGPT编程实现简易聊天工具
ChatGPT编程实现简易聊天工具 今天借助[[小蜜蜂]][https://zglg.work]网站的ChatGPT练习socket编程,实现一个简易聊天工具软件。 环境:Pycharm 2021 系统:Mac OS 向ChatGPT输入如下内容: ChatGPT收到后,根据返回结…...
C#-用于Excel处理的程序集
在.NET开发中,处理Excel文件是一项常见的任务,而有一些优秀的Excel处理包可以帮助开发人员轻松地进行Excel文件的读写、操作和生成。本文介绍了NPOI、EPPlus和Spire.XLS这三个常用的.NET Excel处理包,分别详细介绍了它们的特点、示例代码以及…...
HTTPS基础
目录 HTTPS简介 HTTP与HTTPS的区别 CA证书 案例 服务器生成私钥与证书 查看证书和私钥存放路径 Cockpit(图像化服务管理工具) HTTPS简介 超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息。HTTP协议以明文方式发送内容,不提供任何方式的数据加密&…...
【Flink SQL】Flink SQL 基础概念(四):SQL 的时间属性
《Flink SQL 基础概念》系列,共包含以下 5 篇文章: Flink SQL 基础概念(一):SQL & Table 运行环境、基本概念及常用 APIFlink SQL 基础概念(二):数据类型Flink SQL 基础概念&am…...
文字弹性跳动CSS3代码
文字弹性跳动CSS3代码,源码由HTMLCSSJS组成,记事本打开源码文件可以进行内容文字之类的修改,双击html文件可以本地运行效果,也可以上传到服务器里面,重定向这个界面 下载地址 文字弹性跳动CSS3代码...
前端小白的学习之路(事件流)
提示:事件捕获,事件冒泡,事件委托 目录 事件模型(DOM事件流) 1.事件是什么 2.事件流 1).事件流的三个阶段 3.参考代码 二、事件委托 1.概念 2.使用案例 3.阻止冒泡行为 事件模型(DOM事件流) 1.事件是什么 1). 事件是HTML和Javascr…...
电脑文件误删除如何恢复?分享三个简单数据恢复方法
在日常使用电脑的过程中,文件误删除的情况时有发生。无论是由于操作失误还是病毒感染,丢失的文件都可能对我们的工作和学习造成极大的影响。因此,掌握文件恢复的方法显得尤为重要。下面围绕“电脑文件误删除如何恢复”这一主题,给…...
MySQL实战:监控
监控指标 性能类指标 名称说明QPS数据库每秒处理的请求数量TPS数据库每秒处理的事务数量并发数数据库实例当前并行处理的会话数量连接数连接到数据库会话的数量缓存命中率Innodb的缓存命中率 功能类指标 名称说明可用性数据库是否正常对外提供服务阻塞当前是否有阻塞的会话…...
MySQL自增主键自动生成的主键重置
需求描述: 从主键1开始,insert操作自增了五个,库里五条数主键是1、2、3、4、5; 然后把主键是3、4、5的三条数据给删了,再继续insert,主键就是6了 因为这里表会把最大的数即5记住,下次自增即为…...
reverse_iterator实现
对于实现reverse_iterator,我们可以学栈和队列的实现过程,利用适配器,实现如下; #pragma oncetemplate<class Iterator,class Ref,class Ptr> class reverse_Iterator { public://构造函数:reverse_Iterator(Iterator it):…...
C++:什么情况下函数应该声明为纯虚函数
在C中,函数应该在以下情况下声明为纯虚函数: 抽象基类:当你希望定义一个基类,该基类不能被实例化,只能作为其他类的基类时,你应该在基类中声明至少一个纯虚函数。这样的基类被称为抽象基类。纯虚函数通过在…...
【全面了解自然语言处理三大特征提取器】RNN(LSTM)、transformer(注意力机制)、CNN
目录 一 、RNN1.RNN单个cell的结构2.RNN工作原理3.RNN优缺点 二、LSTM1.LSTM单个cell的结构2. LSTM工作原理 三、transformer1 Encoder(1)position encoding(2)multi-head-attention(3)add&norm 残差链…...
区块链推广海外市场怎么做,CloudNEO服务商免费为您定制个性化营销方案
随着区块链技术的不断发展和应用场景的扩大,区块链项目希望能够进入海外市场并取得成功已成为越来越多公司的目标之一。然而,要在海外市场推广区块链项目,需要采取有效的营销策略和措施。作为您的区块链项目营销服务商,CloudNEO将…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...
反射获取方法和属性
Java反射获取方法 在Java中,反射(Reflection)是一种强大的机制,允许程序在运行时访问和操作类的内部属性和方法。通过反射,可以动态地创建对象、调用方法、改变属性值,这在很多Java框架中如Spring和Hiberna…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...
【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...
