Visualizzazione post con etichetta Qt. Mostra tutti i post
Visualizzazione post con etichetta Qt. Mostra tutti i post

lunedì 15 settembre 2025

Compilare plugin personalizzati in CloudCompare in Debian Trixie

 

Per compilare un plugin per Cloudcompare si deve partire dai sorgenti 

git clone --recursive https://github.com/CloudCompare/CloudCompare

Cloudcompare si puo' compilare solo su Qt5 mentre nelle versioni recenti di Debian il default e' Qt6  (dove non specificato le librerie si possono scaricare dal apt come per esempio geotiff senza dover ricompilare da sorgenti)

sudo apt update
sudo apt install -y \
    git cmake build-essential \
    qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools \
    qttools5-dev qttools5-dev-tools qtscript5-dev \
    libqt5svg5-dev libqt5opengl5-dev \
    libeigen3-dev libboost-all-dev \
    libfftw3-dev libtbb-dev \
    libgdal-dev libproj-dev \
    libpcl-dev \
    libxi-dev libxmu-dev libglu1-mesa-dev freeglut3-dev mesa-common-dev \
    libsqlite3-dev

Uno dei problemi in compilazione e' che Liblas e pdal non sono scaricabili da apt e si devono compilare da sorgenti. LibLas e Pdal dipendono da Boost e sono compatibili con la versione 1.88 pero' Pdal dipende a sua volta da Gdal che su apt e' compilata su Boost 1.83 e cio' ovviamente rompe il sistema

Si deve procedere quindi a compilare Gdal da sorgenti usando Boost 1.88

git clone https://github.com/OSGeo/gdal.git
cd gdal
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release \
    -DBUILD_SHARED_LIBS=ON \
    -DCMAKE_INSTALL_PREFIX=/usr/local
make -j$(nproc)
sudo make install
sudo ldconfig

 

Compilazione di PDAL

git clone  https://github.com/PDAL/PDAL

cd ~/PDAL
mkdir -p build && cd build
rm -rf *

cmake .. \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_INSTALL_PREFIX=/usr/local \
    -DWITH_GDAL=ON \
    -DWITH_LASZIP=ON \
    -DWITH_LAZPERF=ON \
    -DBUILD_SHARED_LIBS=ON \
    -DINSTALL_CMAKE_CONFIG=ON

make -j$(nproc)
sudo make install
sudo ldconfig

 

Compilazione di LibLas (non piu' necessaria nelle nuove versioni)


git clone https://github.com/libLAS/libLAS.git
cd libLAS
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DWITH_GDAL=ON -DWITH_GEOTIFF=ON
make -j$(nproc)
sudo make install
sudo ldconfig

Infine per compilare CloudCompare vero e proprio (comprensivo di plugin)

 cmake .. -DCMAKE_BUILD_TYPE=Release -DOPTION_USE_QT5=ON -DPLUGIN_GL_QEDL=ON  -DPLUGIN_GL_QSSAO=ON  -DPLUGIN_IO_QADDITIONAL=ON  -DPLUGIN_IO_QCORE=ON  -DPLUGIN_IO_QPHOTOSCAN=ON  -DPLUGIN_STANDARD_QANIMATION=ON  -DPLUGIN_STANDARD_QBROOM=ON  -DPLUGIN_STANDARD_QCANUPO=ON  -DPLUGIN_STANDARD_QCOMPASS=ON  -DPLUGIN_STANDARD_QFACETS=ON  -DPLUGIN_STANDARD_QHOUGH_NORMALS=ON  -DPLUGIN_STANDARD_QHPR=ON  -DPLUGIN_STANDARD_QM3C2=ON  -DPLUGIN_STANDARD_QPCV=ON  -DPLUGIN_STANDARD_QPOISSON_RECON=ON  -DPLUGIN_STANDARD_QRANSAC_SD=OFF  -DPLUGIN_STANDARD_QSRA=ON  -DEIGEN_ROOT_DIR=/usr/include/eigen


per vedere i plugin attivi si deve fare make install (non usare il binario in build/qCC)
i plugin sono dei file .so che si andranno ad installare in /usr/local/lib/cloudcompare/plugins
 
a questo punto si deve passare a compilare il plugin personalizzato. La via che ho trovato piu' comoda e'copiare il progetto di esempio in CloudCompare/plugins/example/ExamplePlugin in un plugins/core/Standard cambiando il nome del folder

per aggiungere il plugin si deve modificare anche CMakeLists.txt in plugins/core/Standard
Diciamo che il plugin custom si chiamera' qQueryMesh andremo a modificare i vari files del progetto
 
info.json
{
"type" : "Standard",
"name" : "qQueryMesh",
"icon" : ":/CC/plugin/qQueryMesh/images/icon.png",
"description": "Click and get info about the mesh",
"authors" : [
{
"name" : "Luca Innocenti",
"email" : "lucainnoc@gmail.com"
}
],
"maintainers" : [
{
"name" : "Luca Innocenti",
"email" : "lucainnoc@gmail.com"
}
],
"references" : [
{
"text" : "The unsuccessful self-treatment of a case of “writer's block”",
"url" : "https://www.ncbi.nlm.nih.gov/pmc/articles/PMC1311997/"
}
]
}


 
 
CMakeLists.txt
# Add an option to CMake to control whether we build this plugin or not
option( PLUGIN_EXAMPLE_STANDARD "Install example plugin" ON )

if ( PLUGIN_EXAMPLE_STANDARD )
project( qQueryMesh )
AddPlugin( NAME ${PROJECT_NAME} )
add_subdirectory( include )
add_subdirectory( src )
# set dependencies to necessary libraries
# target_link_libraries( ${PROJECT_NAME} LIB1 )
endif()
 
 
qQueryMesh.qrc
 
<RCC>
  <qresource prefix="/CC/plugin/qQueryMesh" >
    <file>images/icon.png</file>
    <file>info.json</file>
  </qresource>
</RCC>
 
 
 qQueryMesh.h(deve essere modificato il file CMakeLists.txt in include)
//##########################################################################
//# #
//# CLOUDCOMPARE PLUGIN: ExamplePlugin #
//# #
//# This program is free software; you can redistribute it and/or modify #
//# it under the terms of the GNU General Public License as published by #
//# the Free Software Foundation; version 2 of the License. #
//# #
//# This program is distributed in the hope that it will be useful, #
//# but WITHOUT ANY WARRANTY; without even the implied warranty of #
//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
//# GNU General Public License for more details. #
//# #
//# COPYRIGHT: XXX #
//# #
//##########################################################################

#pragma once

#include "ccStdPluginInterface.h"
#include <QObject>
#include <QAction>

class qQueryMesh : public QObject, public ccStdPluginInterface
{
Q_OBJECT
Q_INTERFACES( ccPluginInterface ccStdPluginInterface )
Q_PLUGIN_METADATA( IID "cccorp.cloudcompare.plugin.qQueryMesh" FILE "../info.json" )

public:
explicit qQueryMesh( QObject *parent = nullptr );
~qQueryMesh() override = default;

//explicit qQueryPolygon(QObject* parent = nullptr);
QList<QAction*> getActions() override;
void onNewSelection(const ccHObject::Container& selectedEntities) override;


// Inherited from ccStdPluginInterface
//void onNewSelection( const ccHObject::Container &selectedEntities ) override;
//QList<QAction *> getActions() override;

private:
QAction* m_action;
};


 
 
 qQueryMesh.cpp (deve essere modificato il file CMakeLists.txt in src)
#include <QtGui>

#include "qQueryMesh.h"

#include <ccMesh.h>
#include <ccLog.h>
#include <ccPolyline.h>
#include <ccPointCloud.h>
#include <ccGLWindowInterface.h>

#include <fstream>
#include <cmath>

qQueryMesh::qQueryMesh(QObject *parent)
: QObject(parent)
, ccStdPluginInterface(":/CC/plugin/qQueryMesh/info.json")
, m_action(nullptr)
{
}

void qQueryMesh::onNewSelection(const ccHObject::Container &selectedEntities)
{
if (m_action == nullptr)
{
return;
}

for (ccHObject *entity : selectedEntities)
{
if (!entity)
continue;

// Stampiamo il nome dell'entità
ccLog::Print(QString("Oggetto selezionato: %1").arg(entity->getName()));

// Determiniamo il tipo dell'entità
if (entity->isKindOf(CC_TYPES::MESH))
{
ccLog::Print("Tipo: Mesh");
ccMesh *mesh = static_cast<ccMesh *>(entity);

ccPointCloud *vertices = dynamic_cast<ccPointCloud *>(mesh->getAssociatedCloud());
if (!vertices)
{
ccLog::Print("Errore: impossibile ottenere il PointCloud associato alla mesh");
continue;
}

// Colora tutti i vertici di grigio
ccColor::Rgb color(128, 128, 128);
for (unsigned i = 0; i < vertices->size(); ++i)
vertices->setPointColor(i, color);

// Mostra i colori sulla mesh
mesh->showColors(true);
mesh->prepareDisplayForRefresh();

if (mesh->hasNormals())
{
if (vertices->size() < 3)
continue;

CCVector3 avgNormal(0, 0, 0);
CCVector3 centroid(0, 0, 0);

double A = 0.0;
double B = 0.0;
double C = 0.0;
double D = 0.0;

unsigned triCount = mesh->size();
for (unsigned i = 0; i < triCount; ++i)
{
CCCoreLib::VerticesIndexes *tri = mesh->getTriangleVertIndexes(i);
if (!tri)
continue;

const CCVector3 *pA = vertices->getPoint(tri->i1);
const CCVector3 *pB = vertices->getPoint(tri->i2);
const CCVector3 *pC = vertices->getPoint(tri->i3);

// centroide per il calcolo di D
centroid += (*pA + *pB + *pC) / 3.0f;

// normale triangolo
CCVector3 AB = *pB - *pA;
CCVector3 AC = *pC - *pA;
CCVector3 N = AB.cross(AC);
N.normalize();

avgNormal += N;
}

if (triCount > 0)
{
avgNormal.normalize();
centroid /= static_cast<PointCoordinateType>(triCount);

// piano: Ax + By + Cz + D = 0
A = avgNormal.x;
B = avgNormal.y;
C = avgNormal.z;
D = -(A * centroid.x + B * centroid.y + C * centroid.z);

ccLog::Print(QString("Piano mesh: normale=(%1,%2,%3), D=%4")
.arg(A)
.arg(B)
.arg(C)
.arg(D));
}

// Calcolo della normale media
CCVector3 normalSum(0, 0, 0);
unsigned count = 0;

for (unsigned i = 0; i < mesh->size(); ++i) // numero di triangoli
{
const CCCoreLib::VerticesIndexes *tri = mesh->getTriangleVertIndexes(i);
if (!tri)
continue;

const CCVector3 *p0 = vertices->getPoint(tri->i1);
const CCVector3 *p1 = vertices->getPoint(tri->i2);
const CCVector3 *p2 = vertices->getPoint(tri->i3);

CCVector3 v1 = *p1 - *p0;
CCVector3 v2 = *p2 - *p0;
CCVector3 N = v1.cross(v2);

if (N.norm2() > 0)
{
N.normalize();
normalSum += N;
++count;
}
}

if (count > 0)
{
normalSum /= static_cast<PointCoordinateType>(count);
normalSum.normalize();
ccLog::Print(QString("Normale media: (%1, %2, %3)")
.arg(normalSum.x)
.arg(normalSum.y)
.arg(normalSum.z));
}

// Scrittura in CSV in append
// coseni direttori
double norm = sqrt(A * A + B * B + C * C);
double l = A / norm;
double m = B / norm;
double n = C / norm;

// angoli in radianti → gradi
double alpha = acos(l) * 180.0 / M_PI;
double beta = acos(m) * 180.0 / M_PI;
double gamma = acos(n) * 180.0 / M_PI;

const char *filename = "/home/luca/dati.csv";
bool writeHeader = false;

{
std::ifstream checkFile(filename);
if (!checkFile.good() || checkFile.peek() == std::ifstream::traits_type::eof())
writeHeader = true;
}

std::ofstream file(filename, std::ios::app);
if (file.is_open())
{
if (writeHeader)
{
file << "A,B,C,D,alpha,beta,gamma\n";
}
file << A << "," << B << "," << C << "," << D << ","
<< alpha << "," << beta << "," << gamma << "\n";
file.close();
}
}
else
{
ccLog::Print("Mesh senza normali");
}
}
else if (entity->isKindOf(CC_TYPES::POLY_LINE))
{
ccLog::Print("Tipo: Polilinea");
}
else if (entity->isKindOf(CC_TYPES::POINT_CLOUD))
{
ccLog::Print("Tipo: Point Cloud");
}
else
{
ccLog::Print("Tipo: Altro");
}
}

// abilita se è selezionato un tema poligonale
bool hasPoly = false;
for (ccHObject *obj : selectedEntities)
{
if (obj && obj->isKindOf(CC_TYPES::POLY_LINE))
{
hasPoly = true;
break;
}
}

m_action->setEnabled(hasPoly);
}

QList<QAction *> qQueryMesh::getActions()
{
if (!m_action)
{
m_action = new QAction(getName(), this);
m_action->setToolTip(getDescription());
m_action->setIcon(getIcon());

connect(m_action, &QAction::triggered, this, [this]()
{
if (!m_app)
return;

const ccHObject::Container& selected = m_app->getSelectedEntities();
if (selected.empty())
{
ccLog::Warning("[qQueryPolygon] No polygon selected!");
return;
}

for (ccHObject* obj : selected)
{
if (!obj || !obj->isKindOf(CC_TYPES::POLY_LINE))
continue;

ccPolyline* poly = static_cast<ccPolyline*>(obj);
if (!poly)
continue;

ccPointCloud* vertices = static_cast<ccPointCloud*>(poly->getAssociatedCloud());
if (!vertices)
continue;

// ---- INFO ----
QString info;
info += QString("Polygon: %1\n").arg(poly->getName());
info += QString("Vertices: %1\n").arg(vertices->size());
info += QString("Closed: %1\n").arg(poly->isClosed() ? "Yes" : "No");

PointCoordinateType length = poly->computeLength();
info += QString("\nPerimeter length: %1").arg(length);

ccLog::Print(info);

// ---- HIGHLIGHT ----
poly->setColor(ccColor::red); // change line color
poly->setWidth(4); // make it thicker
poly->showColors(true);

if (m_app->getActiveGLWindow())
m_app->getActiveGLWindow()->redraw();
} });
}

return {m_action};
}


 
 Si controlla che il plugin e' attivo in Help/About Plugins. Poi si aggiunge il seguente parametro a cmake ..
-DPLUGIN_STANDARD_QQUERYMESH=ON
 
infine make e make install. Se tutto e' corretto make, sudo make install avremo il file  libqQueryMesh.so


 

venerdì 9 agosto 2024

Compilare RTKLib Base5 QT su Linux

La libreria RTKLib che si scarica tramite apt e' obsoleta ed anche il github originale e' in abbandono. Attualmente e' piu' interessante il fork presente in https://github.com/rtklibexplorer/RTKLIB ma distribuisce solo versioni compilate per Windows

La ricetta per compilare su Linux e' la seguente

sudo apt install qt5base-dev
sudo apt-get install libqt5serialport5 
sudo apt-get install libqt5serialport5-dev

Per prima cosa si compila prima la libreria

cd src
mkdir build & cd build
qmake ../src.pro
make

si passa quindi a compilare le applicazione da consolle e qt

Queste si trovano in  app/consapp e in app/qtapp

Per le app da console si entra nel subfolder gcc e poi si digita

make

e' disponibile il make file per bcc ed nel caso di rnx2rtkp anche il makefile per gcc_mkl (le librerie Intel di calcolo matriciale. Istruzioni qui)

Attenzione: la compilazione genera un errore in rn2rtkp.c alla linea 76 perche' non e' terminata una stringa con il carattere doppio apice. Si risolve semplicemente editando 

Per le app Qt si deve cercare il subfolder gcc e lanciare make 

mkdir build
cd build
qmake ../rtklaunch_qt.pro (si seleziona il file .pro)
make

giovedì 19 novembre 2020

PCL Library e Visual Studio 2019

 Per installare ed usare PCL su Windows la cosa piu' comoda e' utilizzare il pacchetto PCL-1.11.1-AllInOne-msvc2019-win64 

Una volta installato il file exe per esempio in C:\Program Files\PCL 1.11.1


Per creare un progetto in QT con le PCL si devono aggiungere nella path OpenNI2 (stranamente l'installer non la ha aggiunta nella PATH)

Ho sostituito la versione di CMAKE da quella di default di QT con l'installer di CMake  (modificabile dai Kit di Qt) ed come compilatore ho selezionato MSVC2019



il file CMAKE e' il seguente

===============================================

cmake_minimum_required(VERSION 3.5)


project(t3 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(PCL 1.3 REQUIRED COMPONENTS common io features)
include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})


add_executable(t3 main.cpp)
target_link_libraries(t3 ${PCL_LIBRARIES})

===============================================

mentre un programma di esempio e' 
===============================================
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>

#include <pcl/features/normal_3d.h>

using namespace std;

int main()
{
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);

     if (pcl::io::loadPCDFile<pcl::PointXYZ> ("c://gabriele.pcd", *cloud) == -1) //* load the file
     {
       PCL_ERROR ("Couldn't read file test_pcd.pcd \n");
       return (-1);
     }
     //std::cout << "Loaded " << cloud->width * cloud->height << " data points from test_pcd.pcd with the following fields: " << std::endl;
     /*for (size_t i = 0; i < cloud->points.size (); ++i)
       std::cout << "    " << cloud->points[i].x << " "    << cloud->points[i].y << " "    << cloud->points[i].z << std::endl;
    */
     // Create the normal estimation class, and pass the input dataset to it

     pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
     ne.setInputCloud (cloud);
    // Create an empty kdtree representation, and pass it to the normal estimation object.
       // Its content will be filled inside the object, based on the given input dataset (as no other search surface is given).
       pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ> ());
       ne.setSearchMethod (tree);

       // Output datasets
       pcl::PointCloud<pcl::Normal>::Ptr cloud_normals (new pcl::PointCloud<pcl::Normal>);

       // Use all neighbors in a sphere of radius 3cm
       ne.setRadiusSearch (0.03);

       // Compute the features
       ne.compute (*cloud_normals);

       //std::cout << endl << cloud_normals->size() << endl;


       // cloud_normals->size () should have the same size as the input cloud->size ()*

       int i = 0;
       for (pcl::Normal n : *cloud_normals) {
           //std::cerr << i << " n = " << n.normal_x << ", " << n.normal_y << ", " << n.normal_z << "\n";
           std::cout << n.normal_x << "," << n.normal_y << "," << n.normal_z << "\n";
           i++;
       }



    return 0;
}


Analisi MNF su spettri di riflettanza di plastica

Devo cerca di lavorare su spettri di riflettanza di plastica e la prima domanda e': quale sono le bande significative? Sono partito dal ...