Nama saya Andrey Gladkov, saya seorang pengembang di bidang kendaraan tak berawak. Hari ini saya akan berbagi dengan komunitas Habr tugas yang berkaitan dengan sensor terpenting dari drone - lidar, dan bagaimana kami memproses data lidar. Anda dapat mencoba menyelesaikan sendiri masalahnya di platform Kontes. Sistem akan memeriksa solusi menggunakan tes otomatis dan segera melaporkan hasilnya. Kode parsing dan solusi - di spoiler menjelang akhir posting. Bagi mereka yang menghadiri pertemuan di bengkel kami tahun lalu, tugas tersebut akan terasa familier - kami menawarkannya sebagai tiket masuk, tetapi kami tidak pernah membagikannya secara publik.
Kondisi
| Batas waktu | 15 detik |
| Batasan memori | 64 MB |
| Memasukkan | input standar atau input.txt |
| Keluaran | keluaran standar atau keluaran.txt |
Dimensi adalah satu set poin dengan koordinat , dan ... Lebih dari 50% titik dimiliki oleh jalan. Model posisi titik-titik yang menjadi milik jalan di angkasa adalah bidang dengan parameterisasi
Titik-titik yang termasuk dalam jalan menyimpang dari model tidak lebih dari jumlah yang ditentukan ...
Temukan parameter dan pesawat yang sesuai dengan jalan. Jumlah titik yang menyimpang dari model tidak lebih dari, harus minimal 50% dari total jumlah poin.
Masukkan format
Data masukan diberikan dalam format teks. Baris pertama berisi ambang batas tetap ... Baris kedua berisi jumlah poin ... Pengikut garis berisi koordinat , dan untuk setiap titik, pembatasnya adalah karakter tab (format string
"x[TAB]y[TAB]z"). Bilangan real tidak lebih dari 6 tempat desimal.
Format output
Parameter keluaran , , dan pesawat yang sesuai dengan jalan. Pisahkan angka dengan spasi. Parameter keluaran harus menentukan bidang yang benar.
Contoh 1
| Memasukkan | Keluaran |
0,01 3 20 0 0 10-10 0 10 10 0 |
0.000000 0.000000 1.000000 0.000000 |
Contoh 2
| Memasukkan | Keluaran |
0,01 3 20 0 3 10-10 2 10 10 2 |
-0,099504 0,000000 0,995037 -0,995037 |
Contoh 3
| Memasukkan | Keluaran |
0,01 sepuluh 20-10 0,2 20 0 0.2 20 10 0.2 15-10 0,15 15 0 0.15 15 10 0.15 10-10 0.1 10 10 0.1 20 18 1.7 15-15 1.2 |
-0.010000 0.000000 0.999950 0.000000 |
Di mana harus memutuskan
Anda dapat mencoba tangan Anda di Kontes
Parsing dan kode selesai
Parsing
Kode C ++
#include <algorithm>
#include <array>
#include <cmath>
#include <cstdint>
#include <iostream>
#include <random>
#include <vector>
struct Point3d {
double X = 0.0;
double Y = 0.0;
double Z = 0.0;
};
using PointCloud = std::vector<Point3d>;
struct Plane {
double A = 0.0;
double B = 0.0;
double C = 0.0;
double D = 0.0;
};
bool CreatePlane(
Plane& plane,
const Point3d& p1,
const Point3d& p2,
const Point3d& p3) {
const double matrix[3][3] =
{{ 0, 0, 0}, // this row is not used
{p2.X - p1.X, p2.Y - p1.Y, p2.Z - p1.Z},
{p3.X - p1.X, p3.Y - p1.Y, p3.Z - p1.Z}};
auto getMatrixSignedMinor = [&matrix](size_t i, size_t j) {
const auto& m = matrix;
return (m[(i + 1) % 3][(j + 1) % 3] * m[(i + 2) % 3][(j + 2) % 3] -
m[(i + 2) % 3][(j + 1) % 3] * m[(i + 1) % 3][(j + 2) % 3]);
};
const double A = getMatrixSignedMinor(0, 0);
const double B = getMatrixSignedMinor(0, 1);
const double C = getMatrixSignedMinor(0, 2);
const double D = -(A * p1.X + B * p1.Y + C * p1.Z);
const double norm_coeff = std::sqrt(A * A + B * B + C * C);
const double kMinValidNormCoeff = 1.0e-6;
if (norm_coeff < kMinValidNormCoeff) {
return false;
}
plane.A = A / norm_coeff;
plane.B = B / norm_coeff;
plane.C = C / norm_coeff;
plane.D = D / norm_coeff;
return true;
}
double CalcDistance(const Plane& plane, const Point3d& point) {
// assume that the plane is valid
const auto numerator = std::abs(
plane.A * point.X + plane.B * point.Y + plane.C * point.Z + plane.D);
const auto denominator = std::sqrt(
plane.A * plane.A + plane.B * plane.B + plane.C * plane.C);
return numerator / denominator;
}
int CountInliers(
const Plane& plane,
const PointCloud& cloud,
double threshold) {
return std::count_if(cloud.begin(), cloud.end(),
[&plane, threshold](const Point3d& point) {
return CalcDistance(plane, point) <= threshold; });
}
Plane FindPlaneWithFullSearch(const PointCloud& cloud, double threshold) {
const size_t cloud_size = cloud.size();
Plane best_plane;
int best_number_of_inliers = 0;
for (size_t i = 0; i < cloud_size - 2; ++i) {
for (size_t j = i + 1; j < cloud_size - 1; ++j) {
for (size_t k = j + 1; k < cloud_size; ++k) {
Plane sample_plane;
if (!CreatePlane(sample_plane, cloud[i], cloud[j], cloud[k])) {
continue;
}
const int number_of_inliers = CountInliers(
sample_plane, cloud, threshold);
if (number_of_inliers > best_number_of_inliers) {
best_plane = sample_plane;
best_number_of_inliers = number_of_inliers;
}
}
}
}
return best_plane;
}
Plane FindPlaneWithSimpleRansac(
const PointCloud& cloud,
double threshold,
size_t iterations) {
const size_t cloud_size = cloud.size();
// use uint64_t to calculate the number of all combinations
// for N <= 25000 without overflow
const auto cloud_size_ull = static_cast<uint64_t>(cloud_size);
const auto number_of_combinations =
cloud_size_ull * (cloud_size_ull - 1) * (cloud_size_ull - 2) / 6;
if (number_of_combinations <= iterations) {
return FindPlaneWithFullSearch(cloud, threshold);
}
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<size_t> distr(0, cloud_size - 1);
Plane best_plane;
int best_number_of_inliers = 0;
for (size_t i = 0; i < iterations; ++i) {
std::array<size_t, 3> indices;
do {
indices = {distr(gen), distr(gen), distr(gen)};
std::sort(indices.begin(), indices.end());
} while (indices[0] == indices[1] || indices[1] == indices[2]);
Plane sample_plane;
if (!CreatePlane(sample_plane,
cloud[indices[0]],
cloud[indices[1]],
cloud[indices[2]])) {
continue;
}
const int number_of_inliers = CountInliers(
sample_plane, cloud, threshold);
if (number_of_inliers > best_number_of_inliers) {
best_plane = sample_plane;
best_number_of_inliers = number_of_inliers;
}
}
return best_plane;
}
int main() {
double p = 0.0;
std::cin >> p;
size_t points_number = 0;
std::cin >> points_number;
PointCloud cloud;
cloud.reserve(points_number);
for (size_t i = 0; i < points_number; ++i) {
Point3d point;
std::cin >> point.X >> point.Y >> point.Z;
cloud.push_back(point);
}
const Plane plane = FindPlaneWithSimpleRansac(cloud, p, 100);
std::cout << plane.A << ' '
<< plane.B << ' '
<< plane.C << ' '
<< plane.D << std::endl;
return 0;
} , :
( ). , . , , , , .
, . unordered_set .
const double matrix[3][3] =
{{ 0, 0, 0}, // this row is not used
{p2.X - p1.X, p2.Y - p1.Y, p2.Z - p1.Z},
{p3.X - p1.X, p3.Y - p1.Y, p3.Z - p1.Z}};
auto getMatrixSignedMinor = [&matrix](size_t i, size_t j) {
const auto& m = matrix;
return (m[(i + 1) % 3][(j + 1) % 3] * m[(i + 2) % 3][(j + 2) % 3] -
m[(i + 2) % 3][(j + 1) % 3] * m[(i + 1) % 3][(j + 2) % 3]);
};
const double A = getMatrixSignedMinor(0, 0);
const double B = getMatrixSignedMinor(0, 1);
const double C = getMatrixSignedMinor(0, 2);
const double D = -(A * p1.X + B * p1.Y + C * p1.Z);
( ). , . , , , , .
, . unordered_set .