1 cesium/source/core/cartesian3.js 经纬度转WGS84坐标代码:

直接去github看cesium的源码实现就行了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Cartesian3.fromRadians = function (
longitude,
latitude,
height,
ellipsoid, # default is WGS84
result
) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.number("longitude", longitude);
Check.typeOf.number("latitude", latitude);
//>>includeEnd('debug');

height = defaultValue(height, 0.0);
var radiiSquared = defined(ellipsoid)
? ellipsoid.radiiSquared
: wgs84RadiiSquared;

var cosLatitude = Math.cos(latitude);
scratchN.x = cosLatitude * Math.cos(longitude);
scratchN.y = cosLatitude * Math.sin(longitude);
scratchN.z = Math.sin(latitude);
scratchN = Cartesian3.normalize(scratchN, scratchN);

Cartesian3.multiplyComponents(radiiSquared, scratchN, scratchK);
var gamma = Math.sqrt(Cartesian3.dot(scratchN, scratchK));
scratchK = Cartesian3.divideByScalar(scratchK, gamma, scratchK);
scratchN = Cartesian3.multiplyByScalar(scratchN, height, scratchN);

if (!defined(result)) {
result = new Cartesian3();
}
return Cartesian3.add(scratchK, scratchN, result);
};

0 直接看实现

  • 1 手写依赖扫描版本
  • 2 spring boot 版本

    1 自定义注解 Annotation

    这里给出自定义注解的例子:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    package com.soul.weapon.algorithm.annotation;

    import java.lang.annotation.*;

    @Target({ElementType.TYPE, ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    public @interface WeaponAlgorithm {
    String algoName () default "";
    }

注意注解的申明是用@interface来声明的:
然后这里对每一个参数做简单说明:

  • @Target 说明注解的使用范围
    • TYPE: 用于描述类、接口(包括注解类型) 或enum声明
    • FILED: 用于描述域
  • @Retention 说明注解可以被保留到什么地方
    • 1 RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
    • 2 RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
    • 3 RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;

      这3个生命周期分别对应于:Java源文件(.java文件) —> .class文件 —> 内存中的字节码。
      明确生命周期长度 SOURCE < CLASS < RUNTIME ,所以前者能作用的地方后者一定也能作用。一般如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解,比如接下来要讲到的在运行过程过获得某种注解的所有的类;如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife 和 mapstruct 等),就用 CLASS注解;如果只是做一些检查性的操作,比如** @Override**和 @SuppressWarnings,则可选用 SOURCE 注解。

  • @Documentation: 用于生成javadoc
  • @Inherited: 说明被改注解注释的子类会继承该注解

2 简单工厂模式 简单甚至不简单的工厂Ref

简单说明一下,比如你有一个业务要求是,实现n种车的drive方法,那最普通的就如下:
分别实现其类然后调用,这未免有点难看:

1
2
3
4
5
6
7
8
9
10
public class Driver1 {

public static void main(String[] args) {
Car1 car = new Car1();
Car2 car = new Car2();
Car3 car = new Car3();
Car4 car = new Car4();
}

}

使用简单工厂模式就是说,来一个carFactory类,我所有车的实例的创建和使用都通过carFactory,然后传具体的参数就能实例化相应的类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Driver2 {
public Car car;
public static void createCar(String name) {
switch (name) {
case "car1":
car = new Car3();
break;
case "car2":
car = new Car3();
break;
case "car3":
car = new Car3();
break;
default:
car = null;
break;
}
LOG.info("Created car name is {}", name);
}
public drive() { car.drive();}
}

但是这里有一个问题,没有遵循开闭原则(开放扩展,关闭修改的原则),那么造成没有遵循的原因是什么?因为每一个实现的类的名称是我们手动写到代码里的,当这些相关的类的名称是我们通过代码可以获取的时候,我们就可以解决该问题了,于是我们使用注解

3 使用注解的反射来完善包含简单工厂模式的策略模式

  • 策略模式 将具体的算法封装到一个context类,context可以根据传入的参数自动调取相关的算法,简单工厂模式还是与之很像的
  • 解决了什么问题
    策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完全的都是相同的工作,只是实现不同,用户通过context以相同的方式调用所有的算法,减少了各种算法实现类与使用类之间的耦合
  • 和工厂模式的区别
    • 1 工厂模式,主要是将对象的创建,和使用进行解耦,而策略模式,主要将策略的定义创建,和使用进行解耦,主要是他们针对的对象不同,一个主体是实体类,另一个是策略类,我个人感觉核心思想都是一样的

3.1 示例分析:

接着上面#1的注解,一个策略模式的例子如下(个人理解,欢迎交流)

  • 1 定义一个接口,这样所有的算法类的调用都按照这个接口调用即可:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    public interface Algorithm {
    /**
    *
    * 使用指定的工厂里的函数来处理input
    * @param input
    * @return
    */
    String exAlgo(String input);
    }
  • 2 为这个接口实现一个算法:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @WeaponAlgorithm(algoName = "airMissilePipeTest")
    public class AirMissilePipeTest implements Algorithm {

    @Override
    public String exAlgo(String input) {
    Logger LOG = LoggerFactory.getLogger(AirMissilePipeTest.class);
    LOG.info("airMissilePipeTest algo executing!");
    return input;
    }
    }
  • 3 从用户层面考虑,为用户实现一个上下文类,这个上下文类帮助用户实例化对应的算法类,然后调用相关的算法类:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    public class AlgoFactoryContext {
    private static final Logger LOG = LoggerFactory.getLogger(AlgoFactoryContext.class);
    private static Map<String, Class> allStrategies;

    static {
    Reflections reflections = new Reflections("com.soul.weapon.algorithm.impl",
    new SubTypesScanner(),
    new TypeAnnotationsScanner(),
    new FieldAnnotationsScanner());
    Set<Class<?>> annotatedClasses =
    reflections.getTypesAnnotatedWith(WeaponAlgorithm.class);
    allStrategies = new ConcurrentHashMap<String, Class>();
    for (Class<?> classObject : annotatedClasses) {
    WeaponAlgorithm annotatedAlgo = (WeaponAlgorithm) classObject
    .getAnnotation(WeaponAlgorithm.class);
    allStrategies.put(annotatedAlgo.algoName(), classObject);
    }
    allStrategies = Collections.unmodifiableMap(allStrategies);
    }

    private Algorithm algoExecutor;

    public AlgoFactoryContext (String requiredAlgoName){
    if(allStrategies.containsKey(requiredAlgoName)) {
    LOG.info("algo name is {}", requiredAlgoName);
    try {
    algoExecutor = (Algorithm) allStrategies.get(requiredAlgoName).getDeclaredConstructor().newInstance();
    } catch (NoSuchMethodException | InstantiationException
    | InvocationTargetException | IllegalAccessException ex) {
    LOG.error("Instantiate algo Failed: ", ex);
    }
    } else {
    LOG.error("algo with name: {} not exist!", requiredAlgoName);
    }
    }

    public void execAlgo(String dataString) {
    algoExecutor.exAlgo(dataString);
    }
    }


  • 4 调用示例:
    1
    2
    AlgoFactoryContext ctx = new AlgoFactoryContext("airMissilePipeTest");
    ctx.execAlgo("info to process!");

4 当使用spritboot的自动装配来简化以免手动写reflections

  • 1 定义一个接口,这样所有的算法类的调用都按照这个接口调用即可:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    public interface Algorithm {
    /**
    *
    * 使用指定的工厂里的函数来处理input
    * @param input
    * @return
    */
    String exAlgo(String input);
    }
  • 2 为这个接口实现一个算法:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Service(value = "airMissilePipeTest")
    public class AirMissilePipeTest implements Algorithm {

    @Override
    public String exAlgo(String input) {
    Logger LOG = LoggerFactory.getLogger(AirMissilePipeTest.class);
    LOG.info("airMissilePipeTest algo executing!");
    return input;
    }
    }
  • 3 创建上下文类然后自动注入实现接口的那些类:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @Service
    public class AlgoFactoryContext {
    private static final Logger LOG = LoggerFactory.getLogger(AlgoFactoryContext.class);

    @Autowired(required = true)
    private Map<String, Algorithm> allStrategies;

    private Algorithm algoExecutor;

    public void execAlgo(String requiredAlgoName, String dataString) {
    if(allStrategies.containsKey(requiredAlgoName)) {
    LOG.info("algo name is {}", requiredAlgoName);
    algoExecutor = (Algorithm) allStrategies.get(requiredAlgoName);
    } else {
    LOG.error("algo with name: {} not exist!", requiredAlgoName);
    }
    algoExecutor.exAlgo(dataString);
    }
    }
  • 4 调用:这里需要注意,如果是单独new一个AlgoFactoryContext,并没有办法完成自动装配,猜测原因是,由于使用了sprintboot的autoWire,那么所有依赖都要保证有@component和@sevice/@resouce等去注解,这些注解保证了sprinboot会将这些依赖给管理起来,所以如果new,那么autoWire无法发挥作用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    @Slf4j
    @RestController
    @RequestMapping("/free/test")
    @RequiredArgsConstructor
    public class FreePipeTestController {

    @Autowired
    private AlgoFactoryContext ctx; // by autowire, so ctx will scan and get all
    // implement of the algorithm interface

    private final PipeTestService pipeTestService;

    @Api
    @GetMapping(value = "/{id}")
    public PipeTest getById(@PathVariable("id") String id)
    {
    // add test for the algo factory:
    ctx.execAlgo("airMissilePipeTest", "telegram from socket!");
    return pipeTestService.getById(id);
    }
    ...
    }

1 pmvs文章 Accurate, Dense, and Robust Multiview Stereopsis

1 提出 patch-based MVS algorithm

2 关键词理解

2.1 patch: 估计模型表面的一个tagent平面,更具体的: 3维的一个矩形,其中一边平行于相机拍的图片,范围是55或者77的一个矩形

2.2 Photometric Discrepancy Function 光学差异函数: 用来恢复光学差异小的那些patches

改过程假设照片中物体的lambertian光照环境,R(p)是reference image的意思

2-1

$$g(p) = \frac{1}{|V(p) / R(p)|} \sum_{I \in V(p) \ R(p)}^{} h(p, I, R(p))$$

h(p, I, R(p))是光学差异函数,需要选择出那些满足低于阈值 α 的I然后组成V*(p):

2-2

$$V^*(p) = { I | I \in V(p), h(p, I, R(p))aa \leq \alpha) }$$

替换掉2-1中的V(p)即可,相当于对图像做了一个过滤,得到:

2-3

$$g^*(p) = \frac{1}{| V(p)^* / R(p)|} \sum_{I \in V^*(p) \ R(p)}^{} h(p, I, R(p))$$

2.3 patch优化

每一个patch重建需要2步

  • 1 初始化c(p), n(p), V*(p)以及R(p)
  • 2 优化c(p)和n(p), 将c(p)固定在一个ray上,这样就将自由度由3减少到1,然后n(p)由欧拉角来决定即可,yaw和pitch即可,那么就成为一个无约束最优化问题,则采用共轭梯度法(conjugate gradient)来解即可优化c(p)和n(p)

2.4 image

基于patch去做表面展示的最大优点是很灵活,然而缺少patch与patch之间的链接信息,就不容易获取相邻的patch,解决办法如下:
对于每一张图片,划分成 ββ(β=2在文中)的cell,然后对于每一个patch,它在每个可见图片上都有映射过去的cell,然后上述过程做完以后,每张图片的每个cell都记录了该cell可见的那些个patch,我们记作$Q_i(x, y)$, 同样的,如果是在每个可见图片而且满足#2-3定义的光学差异进行上述步骤,那么就得到$Q_i^(x, y)$

3 patch重建过程

3.1 初始化feature match

3.1.1 采用高斯查分和harris角点来做特征提取

3.1.2 特正匹配和生成atch

  • 1 对于图片$I_i$有光心$O(I_i)$,它里面的每个特征点f,在其它图片中有很多其他对应的特征点$f’$,对于每一个$(f, f’)$点对,那些在他们极线上的特征点,我们组成一个集合F,我们把F中的特征点三角化可以得到对应的3d点,对于这个点的集合,按照其与$O(I_i)$的距离升序排序,一个一个点的尝试去生成patch,生成patch在,直到成功
  • 2 生成patch的过程是在2.3 patch优化阐释的,这里简单复述一下,生成patch就是优化patch的c(p)和o(p)然后使得光学差异函数最小,一旦一个cell生成了一个patch,那么改cell的其他特征点就直接删除不需要了
    生成patch的算法如下:
    PMVS 特征匹配

    3.2 扩展patch

    目的: 对于每一个image的cell $C_i(x, y)$, 至少建出来一个patch,过程如下:

3.2.1 找出相邻的cells来扩展

PMVS patch 扩展
对于一个p,其在它的第i张可见图片中,而且被$C_i(x, y)$格子记住的patch集合$Q_i(x, y)$中,然后相邻的cells如下获得:

3-1

$$C(p) = {C_i(x’, y’) | p in Q_i(x, y), |x - x’| + |y - y’| = 1}$$
C(p)中需要移除两种类型的cell

  • 1 移除已有patch的邻居cell,具体的: 若,$C_i(x’, y’)$ 包含了一个patch $p’$, 这个p’和p是邻居,那么这个$C_i(x’, y’)$则会被从C(p)移除,具体判断方式见下面的公式:

    3-2

    $$|(c(p) - c(p’))*n(p)| + |(c(p)-c(p’))*n(p’)| < 2\rho_1$$
    $\rho_1$是对应于参考图像R(p)在c(p)和c(p’)的深度时,图像移动$\beta_1$个pixels需要的距离
  • 2*(这一步可以不要,因为图片光学差异的图片过滤过程可以消除这个错误,主要是为了计算效率) 移除深度不连续的邻居cell,从已有的相机去看$C_i(x’, y’)$,得到的深度和从相机去看p的慎独过大,则放弃这种邻居cell,然而实际中构建surface之前,这些个p和$C_i(x’, y’)$可能真实对应的表面间的深度不连续很难判断,所以简单化,也就是若$Q_i^*(x’, y’)$的size大于0,也就证明不连续,也就是当这个$C_i(x’, y’)$这个格子,至少有一个patch是在满足了光学差异达到目标的情况下被他记录,那么它就不需要别的patch来帮他expand,我个人如此理解

3.3 滤除错误的patch

  • 1 过滤可见性不一致的patch,去除深度值不一致且一致性较低的点,意思是如果扩散的点云在其他图特征点的点云前面了,通过比较各自的一致性来剔除;如果扩散点云跑到后边去了,也比较一致性

    3-3

    $$|V^*(p)|(1-g^*(p)) < \sum_{p_i \in U(p)}{1-g^*(p)}$$
  • 2 对于每一个patch,用深度图计算出在$V^*(p)$中可以看到它的图像的个数,如果比$\gamma$要小,那么认为可见图片太少,滤除
  • 3 保证了一个弱正则化,对于每一个patch,收集它所有的可见图片的相邻cells中的所有patches,若这些patches中是邻居patch的比例小于0.25,那么也滤除(考虑到它的相邻patch都不是邻居说明这个patch空间上和邻居不太连续)

<未完待续>

0 相关脚本

1 docker cli 命令镜像管理

1.1 常见命令https://docs.docker.com/engine/reference/commandline/docker/

命令 说明
docker search myphp | grep admin 搜索镜像
docker pull mysql:latest
docker run –name myMysql -it -d -p 30000:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql bash -it: 进入终端(tty), -p 端口映射: 本机到容器, -d后台运行
docker ps -al 查看镜像
docker rm -f 4e14 停止并完全删除镜像
docker logs -f –tail 50 myMysql 查看日志

1.2 cli示例

这里给一个运行mysql的例子:(gitbash中运行)

1
2
3
4
docker run -d \
--rm --name myMysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-p 30000:3306 mysql --character-set-server=utf8mb4

2 docker yamlhttps://docs.docker.com/compose/gettingstarted/

2.1 这里给出redis和mysql的一个示例:

  • 1 mysql:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    version: '3.7'
    services:
    mysql:
    image: mysql:8.0.18
    restart: always
    container_name: mysql
    ports:
    - "${MYSQL_PORT:-3306}:3306"
    networks:
    - ingress
    environment:
    - TZ=Asia/Shanghai
    - MYSQL_DATABASE=fregata
    - MYSQL_USER=xin
    - MYSQL_PASSWORD=123
    - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-123456}
    command:
    --default-authentication-plugin=mysql_native_password
    --character-set-server=utf8
    --collation-server=utf8_general_ci
    --explicit_defaults_for_timestamp=true
    --lower_case_table_names=1
    --max_connections=1000
    --max_allowed_packet=128M;
    volumes:
    - ./volumes/data:/var/lib/mysql
    - ./volumes/initdb.d:/docker-entrypoint-initdb.d:ro

    networks:
    ingress:
    name: xin
  • 2 reids:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    version: "3.7"

    services:
    redis:
    image: redis:5.0.7
    build:
    context: ./build
    dockerfile: Dockerfile
    container_name: redis
    restart: always
    environment:
    - TZ=sia/Shanghai
    ports:
    - ${REDIS_PORT:-6379}:6379
    volumes:
    - ./volumes/data:/data
    networks:
    - ingress

    networks:
    ingress:
    external:
    name: xin
    起\挺\查看容器:
    1
    2
    3
    docker-compose up -d
    docker-compose down
    docker-compose ls

1 std::thread传入引用值需要使用std::ref

std::ref的说明: Constructs an object of the appropriate reference_wrapper type to hold a reference to elem.
其实主要是,如果要向thread传参的时候,该参数在线程内会被修改,需要用这个ref作为一个wrapper将对象包裹成为一个引用然后传入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void main() {

while(1){
...<省略>...
if (!initializedFlag) {
initializedFlag = true;
// viewThd = new std::thread(viewThread, camVecToDraw); // wrong way
viewThd = new std::thread(viewThread, std::ref(camVecToDraw)); // right way
}
...<省略>...

addCamToDrawVec(imgData.image, camVecToDraw);
camVecToDraw;
}

viewThd.join();
delete viewThd;
viewThd = nullptr;
}

void viewThread(std::vector<pangolin::OpenGlMatrix>& camVecToDraw) {
while(!shallQuit()) {
// render some cameras according to camVecToDraw
...<省略>...
}
}

1 ffmpeg 解帧

1
ffmpeg -i DJI_20210615164633_0003_W.MP4 -r 3 images/%4d.jpg

2 reinterpret_cast<> 理解以及典型应用:

对于其他的例如static_cast<>等的应用,参考:http://www.cplusplus.com/doc/tutorial/typecasting/

以下引用自: https://stackoverflow.com/questions/573294/when-to-use-reinterpret-cast

Here is a variant of Avi Ginsburg’s program which clearly illustrates the property of reinterpret_cast mentioned by Chris Luengo, flodin, and cmdLP: that the compiler treats the pointed-to memory location as if it were an object of the new type:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;

class A
{
public:
int i;
};

class B : public A
{
public:
virtual void f() {}
};

int main()
{
string s;
B b;
b.i = 0;
A* as = static_cast<A*>(&b);
A* ar = reinterpret_cast<A*>(&b);
B* c = reinterpret_cast<B*>(ar);

cout << "as->i = " << hex << setfill('0') << as->i << "\n";
cout << "ar->i = " << ar->i << "\n";
cout << "b.i = " << b.i << "\n";
cout << "c->i = " << c->i << "\n";
cout << "\n";
cout << "&(as->i) = " << &(as->i) << "\n";
cout << "&(ar->i) = " << &(ar->i) << "\n";
cout << "&(b.i) = " << &(b.i) << "\n";
cout << "&(c->i) = " << &(c->i) << "\n";
cout << "\n";
cout << "&b = " << &b << "\n";
cout << "as = " << as << "\n";
cout << "ar = " << ar << "\n";
cout << "c = " << c << "\n";

cout << "Press ENTER to exit.\n";
getline(cin,s);
}

Which results in output like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
as->i = 0
ar->i = 50ee64
b.i = 0
c->i = 0

&(as->i) = 00EFF978
&(ar->i) = 00EFF974
&(b.i) = 00EFF978
&(c->i) = 00EFF978

&b = 00EFF974
as = 00EFF978
ar = 00EFF974
c = 00EFF974
Press ENTER to exit.

It can be seen that the B object is built in memory as B-specific data first, followed by the embedded A object. The static_cast correctly returns the address of the embedded A object, and the pointer created by static_cast correctly gives the value of the data field. The pointer generated by reinterpret_cast treats b’s memory location as if it were a plain A object, and so when the pointer tries to get the data field it returns some B-specific data as if it were the contents of this field.

应用如下:
下面的应用根据输入的类型T判断后使用了reinterpret_cast去转换;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template <typename T>
void OptionManager::RegisterOption(const std::string& name, const T* option) {
if (std::is_same<T, bool>::value) {
options_bool_.emplace_back(name, reinterpret_cast<const bool*>(option));
} else if (std::is_same<T, int>::value) {
options_int_.emplace_back(name, reinterpret_cast<const int*>(option));
} else if (std::is_same<T, double>::value) {
options_double_.emplace_back(name, reinterpret_cast<const double*>(option));
} else if (std::is_same<T, std::string>::value) {
options_string_.emplace_back(name,
reinterpret_cast<const std::string*>(option));
} else {
LOG(FATAL) << "Unsupported option type";
}
}

glog使用:

项目地址:
git@github.com:xychen5/tryGlog.git
推荐使用类似于clion的ide,然后打开该项目,即可编译运行。

1 主要作用:

能够将glog的日志在cmd中打印
主要调用了函数:

1
google::SetStderrLogging(google::INFO); // print the logs whose severity > [info]

2 output:

样例输出代码如下:

1
2
3
4
5
6
7
I0718 16:09:07.626883 18628 main.cpp:13] glog used in cmd!!

W0718 16:09:07.627887 18628 main.cpp:14] glog used in cmd!!

E0718 16:09:07.628882 18628 main.cpp:15] glog used in cmd!!

F0718 16:09:07.628882 18628 main.cpp:16] glog used in cmd!!

3 使用clion需要注意编译器的版本得和库对应

如果使用x86版本的toolchain,会报错如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
====================[ Build | tryGlogLion | Debug ]=============================
"Z:\softwares\CLion 2021.1.3\bin\cmake\win\bin\cmake.exe" --build F:\cppTry\tryGlogLion\cmake-build-debug --target tryGlogLion
Scanning dependencies of target tryGlogLion
[ 50%] Building CXX object CMakeFiles/tryGlogLion.dir/main.cpp.obj
main.cpp
[100%] Linking CXX executable tryGlogLion.exe
NMAKE : fatal error U1077: “"Z:\softwares\CLion 2021.1.3\bin\cmake\win\bin\cmake.exe"”: 返回代码“0xffffffff”
Stop.
NMAKE : fatal error U1077: “"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\bin\HostX86\x86\nmake.exe"”: 返回代码“0x2”
Stop.
NMAKE : fatal error U1077: “"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\bin\HostX86\x86\nmake.exe"”: 返回代码“0x2”
Stop.
NMAKE : fatal error U1077: “"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\bin\HostX86\x86\nmake.exe"”: 返回代码“0x2”
Stop.
LINK Pass 1: command "C:\PROGRA~2\MICROS~2\2019\COMMUN~1\VC\Tools\MSVC\1429~1.300\bin\Hostx86\x86\link.exe /nologo @CMakeFiles\tryGlogLion.dir\objects1.rsp /out:tryGlogLion.exe /implib:tryGlogLion.lib /pdb:F:\cppTry\tryGlogLion\cmake-build-debug\tryGlogLion.pdb /version:0.0 /machine:X86 /debug /INCREMENTAL /subsystem:console -LIBPATH:F:\prjs\ThirdParty\glog\lib -LIBPATH:F:\prjs\ThirdParty\gflags\lib glog.lib gflags_static.lib glog.lib gflags_static.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib /MANIFEST /MANIFESTFILE:CMakeFiles\tryGlogLion.dir/intermediate.manifest CMakeFiles\tryGlogLion.dir/manifest.res" failed (exit code 1120) with the following output:
main.cpp.obj : error LNK2019: 无法解析的外部符号 "__declspec(dllimport) void __cdecl google::InitGoogleLogging(char const *)" (__imp_?InitGoogleLogging@google@@YAXPBD@Z),函数 _main 中引用了该符号
main.cpp.obj : error LNK2019: 无法解析的外部符号 "__declspec(dllimport) public: __thiscall google::LogMessage::LogMessage(char const *,int)" (__imp_??0LogMessage@google@@QAE@PBDH@Z),函数 _main 中引用了该符号
main.cpp.obj : error LNK2019: 无法解析的外部符号 "__declspec(dllimport) public: __thiscall google::LogMessage::LogMessage(char const *,int,int)" (__imp_??0LogMessage@google@@QAE@PBDHH@Z),函数 _main 中引用了该符号
main.cpp.obj : error LNK2019: 无法解析的外部符号 "__declspec(dllimport) public: __thiscall google::LogMessage::~LogMessage(void)" (__imp_??1LogMessage@google@@QAE@XZ),函数 _main 中引用了该符号
main.cpp.obj : error LNK2019: 无法解析的外部符号 "__declspec(dllimport) public: class std::basic_ostream<char,struct std::char_traits<char> > & __thiscall google::LogMessage::stream(void)" (__imp_?stream@LogMessage@google@@QAEAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@XZ),函数 _main 中引用了该符号
main.cpp.obj : error LNK2019: 无法解析的外部符号 "__declspec(dllimport) public: __thiscall google::LogMessageFatal::LogMessageFatal(char const *,int)" (__imp_??0LogMessageFatal@google@@QAE@PBDH@Z),函数 _main 中引用了该符号
main.cpp.obj : error LNK2019: 无法解析的外部符号 "__declspec(dllimport) public: __thiscall google::LogMessageFatal::~LogMessageFatal(void)" (__imp_??1LogMessageFatal@google@@QAE@XZ),函数 _main 中引用了该符号
main.cpp.obj : error LNK2019: 无法解析的外部符号 "__declspec(dllimport) void __cdecl google::SetStderrLogging(int)" (__imp_?SetStderrLogging@google@@YAXH@Z),函数 _main 中引用了该符号
F:\prjs\ThirdParty\glog\lib\glog.lib : warning LNK4272:库计算机类型“x64”与目标计算机类型“x86”冲突
F:\prjs\ThirdParty\gflags\lib\gflags_static.lib : warning LNK4272:库计算机类型“x64”与目标计算机类型“x86”冲突
tryGlogLion.exe : fatal error LNK1120: 8 个无法解析的外部命令

解决方案:
将files->settings->buidl,execut,deploy->toolchains->enviroment->architecture设置为x86_amd64

0 结果

FreeImage 转 cv::Mat

1 代码

将freeImage转为cv::mat,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include <FreeImage.h>
#include <opencv2\opencv.hpp>
using namespace cv;

// #define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable : 4996)
void FI2MAT(FIBITMAP* src, Mat& dst)
{
//FIT_BITMAP //standard image : 1 - , 4 - , 8 - , 16 - , 24 - , 32 - bit
//FIT_UINT16 //array of unsigned short : unsigned 16 - bit
//FIT_INT16 //array of short : signed 16 - bit
//FIT_UINT32 //array of unsigned long : unsigned 32 - bit
//FIT_INT32 //array of long : signed 32 - bit
//FIT_FLOAT //array of float : 32 - bit IEEE floating point
//FIT_DOUBLE //array of double : 64 - bit IEEE floating point
//FIT_COMPLEX //array of FICOMPLEX : 2 x 64 - bit IEEE floating point
//FIT_RGB16 //48 - bit RGB image : 3 x 16 - bit
//FIT_RGBA16 //64 - bit RGBA image : 4 x 16 - bit
//FIT_RGBF //96 - bit RGB float image : 3 x 32 - bit IEEE floating point
//FIT_RGBAF //128 - bit RGBA float image : 4 x 32 - bit IEEE floating point

int bpp = FreeImage_GetBPP(src);
FREE_IMAGE_TYPE fit = FreeImage_GetImageType(src);

int cv_type = -1;
int cv_cvt = -1;

switch (fit)
{
case FIT_UINT16: cv_type = DataType<ushort>::type; break;
case FIT_INT16: cv_type = DataType<short>::type; break;
case FIT_UINT32: cv_type = DataType<unsigned>::type; break;
case FIT_INT32: cv_type = DataType<int>::type; break;
case FIT_FLOAT: cv_type = DataType<float>::type; break;
case FIT_DOUBLE: cv_type = DataType<double>::type; break;
case FIT_COMPLEX: cv_type = DataType<Complex<double>>::type; break;
case FIT_RGB16: cv_type = DataType<Vec<ushort, 3>>::type; cv_cvt = COLOR_RGB2BGR; break;
case FIT_RGBA16: cv_type = DataType<Vec<ushort, 4>>::type; cv_cvt = COLOR_RGBA2BGRA; break;
case FIT_RGBF: cv_type = DataType<Vec<float, 3>>::type; cv_cvt = COLOR_RGB2BGR; break;
case FIT_RGBAF: cv_type = DataType<Vec<float, 4>>::type; cv_cvt = COLOR_RGBA2BGRA; break;
case FIT_BITMAP:
switch (bpp) {
case 8: cv_type = DataType<Vec<uchar, 1>>::type; break;
case 16: cv_type = DataType<Vec<uchar, 2>>::type; break;
case 24: cv_type = DataType<Vec<uchar, 3>>::type; break;
case 32: cv_type = DataType<Vec<uchar, 4>>::type; break;
default:
// 1, 4 // Unsupported natively
cv_type = -1;
}
break;
default:
// FIT_UNKNOWN // unknown type
dst = Mat(); // return empty Mat
return;
}

int width = FreeImage_GetWidth(src);
int height = FreeImage_GetHeight(src);
int step = FreeImage_GetPitch(src);

if (cv_type >= 0) {
dst = Mat(height, width, cv_type, FreeImage_GetBits(src), step);
if (cv_cvt > 0)
{
cvtColor(dst, dst, cv_cvt);
}
}
else {

std::vector<uchar> lut;
int n = pow(2, bpp);
for (int i = 0; i < n; ++i)
{
lut.push_back(static_cast<uchar>((255 / (n - 1))*i));
}

FIBITMAP* palletized = FreeImage_ConvertTo8Bits(src);
BYTE* data = FreeImage_GetBits(src);
for (int r = 0; r < height; ++r) {
for (int c = 0; c < width; ++c) {
dst.at<uchar>(r, c) = saturate_cast<uchar>(lut[data[r*step + c]]);
}
}
}

flip(dst, dst, 0);
}

int main()
{
FreeImage_Initialise();
// std::string imgPath = "F:/cppTry/index3.png";
std::string imgPath = "F:/cppTry/ger.JPG";
FREE_IMAGE_FORMAT format = FreeImage_GetFileType(imgPath.c_str(), 0);
FIBITMAP* fi_image = FreeImage_Load(format, imgPath.c_str());

Mat cvImg2;
FI2MAT(fi_image, cvImg2);
cvNamedWindow("Image1:",1);
cv::imshow("Image1:",cvImg2);
// imshow(cv_img);

IplImage *img = cvLoadImage(imgPath.c_str());
cvNamedWindow("Image2:",1);
cvShowImage("Image2:", img);
std::cout << "[free's conv]: type is: " << FreeImage_GetColorType(fi_image) << "\n";
std::cout << "[free's conv]: depth is: " << FreeImage_GetBPP(fi_image) << "\n";
std::cout << "[free's conv]: pitch is: " << FreeImage_GetPitch(fi_image) << "\n";
std::cout << "[cv2 conv]: type is: " << "/" << cvImg2.type() << "\n";
std::cout << "[cv2 conv]: depth is: " << "/" << cvImg2.depth() << "\n";
std::cout << "[cv2 conv]: channel is: " << "/" << cvImg2.channels() << "\n";
std::cout << "[cv2 conv]: elem size is: " << "/" << cvImg2.elemSize() << "\n";

cvWaitKey();
cvDestroyWindow("Image:");
return 0;
}

2 将1通道转为3通道

1
2
3
4
5
6
7
void convert1to3Channel(cv::Mat& src, cv::Mat& dst) {
vector<cv::Mat> channels;
for (int i = 0; i < 3; i++) {
channels.push_back(src);
}
cv::merge(&channels[0], channels.size(), dst);
}

0 结果展示

Pangolin 动态点云

1 下载编译pangolin的库

https://github.com/stevenlovegrove/Pangolin.git
该库中本身含有libpng, libjpg, libzip

2 当需要调用该库时:

调用时依赖如下:(本项目并未上传所有依赖,部分依赖需要单独下载然后放到thirdparty目录里)

1
2
3
4
5
# include
F:\prjs\ORB_SLAM3_Fix\ORB_SLAM3\Thirdparty\Pangolin\include;F:\prjs\ORB_SLAM3_Fix\ORB_SLAM3\Thirdparty\Pangolin\build\src\include;F:\prjs\ORB_SLAM3_Fix\ORB_SLAM3\Thirdparty\Pangolin\build\external\glew\include;F:\BASE_ENV\forOpenMVS\eigen;%(AdditionalIncludeDirectories)

# lib
..\..\..\lib\Release\pangolin.lib;opengl32.lib;glu32.lib;..\..\external\glew\lib\glew.lib;..\..\external\libpng\lib\libpng16_static.lib;..\..\external\zlib\lib\zlibstatic.lib;..\..\external\libjpeg\lib\jpeg.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib

3 动态展示点云的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
void viewThread(pangolin::OpenGlMatrix &Twc) {
pangolin::CreateWindowAndBind("Main", 1024, 768);
glEnable(GL_DEPTH_TEST);
// Issue specific OpenGl we might need
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

// Define Projection and initial ModelView matrix
pangolin::OpenGlRenderState s_cam(
pangolin::ProjectionMatrix(1024, 768, 500, 500, 512, 389, 0.1, 1000),
pangolin::ModelViewLookAt(50, 50, 50, 0, 0, 0, pangolin::AxisY)
);

glClear(GL_COLOR_BUFFER_BIT);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);

// Add named OpenGL viewport to window and provide 3D Handler
pangolin::View& d_cam = pangolin::CreateDisplay()
.SetBounds(0.0, 1.0, pangolin::Attach::Pix(175), 1.0, -1024.0f / 768.0f)
.SetHandler(new pangolin::Handler3D(s_cam));

size_t frame = 0;
while( !pangolin::ShouldQuit() ) {
// Clear screen and activate view to render into
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(1.0f,1.0f,1.0f,1.0f);
//! Activate Displays and set State Matrices
d_cam.Activate(s_cam);
// draw camera
const float w = 2;
const float h = w*0.75;
const float z = w*0.6;
glPushMatrix();
glMultMatrixd(Twc.m);
glLineWidth(1);
glColor3f(0.0f,1.0f,0.0f);
glBegin(GL_LINES);
glVertex3f(0,0,0);
glVertex3f(w,h,z);
glVertex3f(0,0,0);
glVertex3f(w,-h,z);
glVertex3f(0,0,0);
glVertex3f(-w,-h,z);
glVertex3f(0,0,0);
glVertex3f(-w,h,z);

glVertex3f(w,h,z);
glVertex3f(w,-h,z);

glVertex3f(-w,h,z);
glVertex3f(-w,-h,z);

glVertex3f(-w,h,z);
glVertex3f(w,h,z);

glVertex3f(-w,-h,z);
glVertex3f(w,-h,z);
glEnd();

glPopMatrix();

// draw points
glPointSize(2);
glBegin(GL_POINTS);
glColor3f(0.0, 0.0, 0.0);

// get all points to m_pos.m_ppatches
updatePointsMutex.lock();
for (size_t p = 0; p < (size_t)allPPatchesRender.size(); ++p)
{
auto patch = *allPPatchesRender[p];
glVertex3f(
patch.m_coord[0],
patch.m_coord[1],
patch.m_coord[2]
);
}
updatePointsMutex.unlock();
glEnd();

// Swap frames and Process Events
pangolin::FinishFrame();

std::this_thread::sleep_for(std::chrono::milliseconds(100)); // ms
// std::cout << "frame��" << ++frame << std::endl;
// std::cout << "point nums are: " << findMatch->m_pos.m_ppatches.size() << std::endl;
}
}


int main(int argc, char* argv[])
{
pangolin::OpenGlMatrix Twc, Twr;
Twc.SetIdentity();
std::thread viewThd(viewThread, Twc);

while(1) {
...<省略>
// update render points
updatePointsMutex.lock();
allPPatchesRender = findMatch->m_pos.m_ppatches; // to get the newest points
updatePointsMutex.unlock();
...<省略>
}


viewThd.join();

//writePointCloundPly(prefix);
releasePMVS();
return 0;
}


1 osg智能指针错误 - Warning: deleting still referenced object

1.1 问题描述

类的定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class QOsgWidget {
public:
///< essential widget, use this ptr to be the real widget
osgQOpenGLWidget* pWidget = nullptr;
// QOsgWidget(QWidget* parent = nullptr);
QOsgWidget(const std::string& modelPath, QWidget* parent = nullptr);
~QOsgWidget();

///< osg base vars
osg::ref_ptr<osg::Group> mRoot = nullptr; // root node of the osg scene
osg::ref_ptr<osg::Camera> camera = nullptr; // osg camera
osg::ref_ptr<osgViewer::Viewer> mViewer = nullptr; // osg viewer
osg::ref_ptr<osgGA::TrackballManipulator> trackball = nullptr;
osg::ref_ptr<osgGA::KeySwitchMatrixManipulator> keyswitchManipulator = nullptr;
osg::ref_ptr<osgViewer::StatsHandler> stats = nullptr;
<被省略>
}

类的析构函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
QOsgWidget::~QOsgWidget() {
// end the render thread and destroy the osgOpenglWidget
std::this_thread::sleep_for(std::chrono::microseconds(1000));
mViewer->setDone(true);
std::this_thread::sleep_for(std::chrono::microseconds(1000));
mViewer->stopThreading();

//////////////////////////////////////////////////////////////////////////
/// without folowing post process, u will crash with the bellowing warning when: delete pWidget
/// Warning: deleting still referenced object 000002433CA59BB0 of type 'class osg::Referenced * __ptr64'
/// the final reference count was 1, memory corruption possible.
///
/// the reason i guess is:
/// the bellowing vars can not be automatically set to null when delete pWidget, because they does
/// not belong to the pWidget, so u need to set them to null manually, otherwise when delete pWidget,
/// the destructor of pWidget will try to free the bellowing vars, but their ref != 0
///////////////////////////////// PART 1 /////////////////////////////////
mRoot = nullptr;
mViewer = nullptr;
camera = nullptr;
trackball = nullptr;
keyswitchManipulator = nullptr;
//////////////////////////////////////////////////////////////////////////

delete pWidget; // call the destructor of the osgOpenglWidge
}

问题描述,在执行类的析构的时候:
当没有PART1的时候,直接执行pWidget会出错

1.2 原因推测:

  • 1 osg::ref_ptr<> 是一种智能指针,自动计算指针的引用个数,当引用个数为0的时候,自动回收其指向的对象。
  • 2 于是出现一个大问题,当pWidget这个类指针所指向的类,里面很多组件(比如 camera,mViewer)是在QOsgWidget类中申明的,那么当析构pWidget的时候,它会去析构自己的组件,然后当它析构了自己类里面的那些指针后,发现QOsgWidget里面的智能指针不是null(这些指针很可能在被别的模块访问),所以引用计数不为0,所以无法删除,或者删除会crash

1.3 得出结论:

  • 1 如果类mon里面嵌套了类son,那么当mon的析构的时候,要注意如下两点:
    • 1.1 将son析构
    • 1.2 执行son的析构前,对son的析构可能产生影响的内存,指针,都要释放掉和置空