Объединение 2 некоррелированных трехмерных облаков точек (вращение, перемещение, масштаб) в Java - PullRequest
0 голосов
/ 08 мая 2020

Проблема: учитывая два трехмерных облака точек (представляющих сканирование глубины) с ~ 50 000 точек на облако и небольшое смещение камеры между ними, как я могу выровнять облака точек?

Я прочитал Нахождение перевода и масштабирования на двух наборах точек, чтобы получить наименьшую квадратичную ошибку в их расстоянии? что было близко, но не совсем так, потому что предполагается, что мы знаем, что точка1 Cloud1 соответствует точке2 Cloud2. Точно так же эта статья предполагает двухточечное знание.

Это кажется так, как будто это должно быть легко:

  1. Для некоторой выборки точек
  2. Найти ближайшего соседа в другом облаке <- - очень быстро с помощью ch.ethz.globis.phtree </li>
  3. «Суммируйте» векторы смещения в один жесткий поворот / преобразование <- не знаю, как это сделать! Или, если это даже правильно думать об этом </li>
  4. Переместите облако точек, попробуйте еще раз, добавив также масштаб.

typealias PointCloud = Array<Point3d>

private fun pointCloudToTransformedTree(pc: PointCloud, transformer: Transform3D = Transform3D()): PhTree<LongArray> {
    val tree: PhTree<LongArray> = PhTree.create(3)
    val transformedPoint = Point3d()
    pc.map { p3d ->
        transformer.transform(p3d, transformedPoint)
        val long3d = longArrayOf(transformedPoint.x.toLong(), transformedPoint.y.toLong(), transformedPoint.z.toLong())
        tree.put(long3d, long3d)
    }
    return tree
}

private fun imageFileToPointCloud(imageFile: File): PointCloud { ... } // unpack the scan 



fun main() {
    val allImageFiles = File("./faces/").walk().filter {
        "png"==it.extension.toLowerCase()
    }

    // Load up all images as a set of long points in 3d.
    val initialPointClouds = allImageFiles.map { imageFileToPointCloud(it) }.toMutableList()

    val baselineCloud = initialPointClouds.removeAt(0)
    val baselineTree = pointCloudToTransformedTree(baselineCloud)
    val secondCloud = initialPointClouds.removeAt(0)

    val fractionOfDegree = 0.2
    val everyNth = 100
    // *** this part here is CLEARLY silly ***
    val rotations: List<Pair<Triple<Double, Double, Double>, Double>> =
        (-1..1).map { it * fractionOfDegree }.map { xd ->
            (-1..1).map { it * fractionOfDegree }.map { yd ->
                (-1..1).map { it * fractionOfDegree }.map { zd ->
                    val xform = Transform3D().apply {
                        val xformRotX = Transform3D()
                        val xformRotY = Transform3D()
                        val xformRotZ = Transform3D()

                        xformRotX.rotX(xd )
                        xformRotY.rotY(yd )
                        xformRotZ.rotZ(zd )

                        this.mul(xformRotX)
                        this.mul(xformRotY)
                        this.mul(xformRotZ)
                    }


                    var secondDistRot = 0.0
                    val secondTreeRot = pointCloudToTransformedTree(secondCloud, xform)
                    baselineTree.queryExtent()
                        .asSequence()
                        .chunked(everyNth)
                        .map {it.first()}
                        .forEach { coordinates ->
                        secondDistRot += coordinates.distSq(secondTreeRot.nearestNeighbour(1, *coordinates).next()!!)
                    }
                    println("rot: $xd,$yd,$zd  = $secondDistRot")
                    Pair(Triple(xd, yd, zd), secondDistRot)
                }
            }.flatten()
        }.flatten().sortedBy { it.second }

    val (bestTransform, bestDistance) = rotations.first()
    println("Best Rotation: ${bestTransform.first},${bestTransform.second},${bestTransform.third}  dist:$bestDistance")
}
...