kopia lustrzana https://github.com/OpenDroneMap/ODM
Merge branch 'dev' into orb_slam2
Conflicts:
configure.sh
opendm/config.py
opendm/types.py
scripts/odm_app.py
Former-commit-id: efb2520241
pull/1161/head
commit
f5f3c628bb
|
@ -3,4 +3,12 @@ tests/test_data
|
|||
SuperBuild/build
|
||||
SuperBuild/download
|
||||
SuperBuild/install
|
||||
SuperBuild/src
|
||||
SuperBuild/src
|
||||
build
|
||||
opensfm
|
||||
pmvs
|
||||
odm_orthophoto
|
||||
odm_texturing
|
||||
odm_meshing
|
||||
odm_georeferencing
|
||||
images_resize
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
# Contributing to OpenDroneMap
|
||||
|
||||
:+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
|
||||
|
||||
### Code of Conduct
|
||||
|
||||
This project adheres to the Contributor Covenant [code of conduct](code_of_conduct.md).
|
||||
By participating, you are expected to uphold this code.
|
||||
Please report unacceptable behavior to the [Project Maintainer](mailto:svm@clevelandmetroparks.com).
|
||||
|
||||
## How can I contribute?
|
||||
|
||||
### Reporting bugs
|
||||
|
||||
Bugs are tracked as Github issues. Please create an issue in the repository and tag it with the Bug tag.
|
||||
|
||||
Explain the problem and include additional details to help maintainers reproduce the problem:
|
||||
|
||||
* **Use a clear and descriptive title** for the issue to identify the problem.
|
||||
* **Describe the exact steps which reproduce the problem** in as many details as possible. For example, start by explaining how you run ODM (Docker, Vagrant, etc), e.g. which command exactly you used in the terminal. When listing steps, **don't just say what you did, but explain how you did it**.
|
||||
* **Provide specific examples to demonstrate the steps**. Include links to files or GitHub projects, or copy/pasteable snippets, which you use in those examples. If you're providing snippets in the issue, use [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines).
|
||||
* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior.
|
||||
* **Explain which behavior you expected to see instead and why.**
|
||||
* **Include screenshots and animated GIFs** which show you following the described steps and clearly demonstrate the problem. If you use the keyboard while following the steps, **record the GIF with the [Keybinding Resolver](https://github.com/atom/keybinding-resolver) shown**. You can use [this tool](http://www.cockos.com/licecap/) to record GIFs on macOS and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or [this tool](https://github.com/GNOME/byzanz) on Linux.
|
||||
* **If the problem is related to performance**, please post your machine's specs (host and guest machine).
|
||||
* **If the problem wasn't triggered by a specific action**, describe what you were doing before the problem happened and share more information using the guidelines below.
|
||||
|
||||
Include details about your configuration and environment:
|
||||
|
||||
* **Which version of ODM are you using?** A stable release? a clone of master or dev?
|
||||
* **What's the name and version of the OS you're using**?
|
||||
* **Are you running ODM in a virtual machine?** If so, which VM software are you using and which operating systems and versions are used for the host and the guest?
|
||||
|
||||
#### Template For Submitting Bug Reports
|
||||
|
||||
[Short description of problem here]
|
||||
|
||||
**Reproduction Steps:**
|
||||
|
||||
1. [First Step]
|
||||
2. [Second Step]
|
||||
3. [Other Steps...]
|
||||
|
||||
**Expected behavior:**
|
||||
|
||||
[Describe expected behavior here]
|
||||
|
||||
**Observed behavior:**
|
||||
|
||||
[Describe observed behavior here]
|
||||
|
||||
**Screenshots and GIFs**
|
||||
|
||||

|
||||
|
||||
**ODM version:** [Enter Atom version here]
|
||||
**OS and version:** [Enter OS name and version here]
|
||||
|
||||
**Additional information:**
|
||||
|
||||
* Problem started happening recently, didn't happen in an older version of ODM: [Yes/No]
|
||||
* Problem can be reliably reproduced, doesn't happen randomly: [Yes/No]
|
||||
* Problem happens with all datasets and projects, not only some datasets or projects: [Yes/No]
|
||||
|
||||
### Pull Requests
|
||||
* ***Pull Requests should be made to the `dev` branch unless it is a critical bug.***
|
||||
* Include screenshots and animated GIFs in your pull request whenever possible.
|
||||
* Follow the [PEP8 Python Style Guide](https://www.python.org/dev/peps/pep-0008/).
|
||||
* End files with a newline.
|
||||
* Avoid platform-dependent code:
|
||||
* Use `require('fs-plus').getHomeDirectory()` to get the home directory.
|
||||
* Use `path.join()` to concatenate filenames.
|
||||
* Use `os.tmpdir()` rather than `/tmp` when you need to reference the
|
||||
temporary directory.
|
||||
* Using a plain `return` when returning explicitly at the end of a function.
|
||||
* Not `return null`, `return undefined`, `null`, or `undefined`
|
||||
|
||||
|
90
Dockerfile
90
Dockerfile
|
@ -1,80 +1,32 @@
|
|||
FROM ubuntu:14.04
|
||||
MAINTAINER Danilo Bargen <mail@dbrgn.ch>
|
||||
|
||||
# Env variables
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
|
||||
# Install dependencies
|
||||
RUN apt-get update \
|
||||
&& sudo apt-get remove libdc1394-22-dev \
|
||||
&& apt-get install -y --install-recommends \
|
||||
build-essential \
|
||||
cmake \
|
||||
git \
|
||||
python-pip \
|
||||
libgdal-dev \
|
||||
libgeotiff-dev \
|
||||
pkg-config \
|
||||
libgtk2.0-dev \
|
||||
libavcodec-dev \
|
||||
libavformat-dev \
|
||||
libswscale-dev \
|
||||
python-dev \
|
||||
python-numpy \
|
||||
libtbb2 \
|
||||
libtbb-dev \
|
||||
libjpeg-dev \
|
||||
libpng-dev \
|
||||
libtiff-dev \
|
||||
libjasper-dev \
|
||||
libflann-dev \
|
||||
libproj-dev \
|
||||
libxext-dev \
|
||||
liblapack-dev \
|
||||
libeigen3-dev \
|
||||
libvtk5-dev \
|
||||
python-networkx \
|
||||
libgoogle-glog-dev \
|
||||
libsuitesparse-dev \
|
||||
libboost-filesystem-dev \
|
||||
libboost-iostreams-dev \
|
||||
libboost-regex-dev \
|
||||
libboost-python-dev \
|
||||
libboost-date-time-dev \
|
||||
libboost-thread-dev \
|
||||
python-empy \
|
||||
python-nose \
|
||||
python-pyside \
|
||||
python-pyexiv2 \
|
||||
python-scipy \
|
||||
jhead \
|
||||
liblas-bin \
|
||||
&& apt-get autoremove \
|
||||
&& apt-get clean
|
||||
|
||||
# Add users
|
||||
RUN useradd -m -U odm
|
||||
#Pull in previously built packages image with lots of libraries.
|
||||
FROM packages
|
||||
|
||||
# Prepare directories
|
||||
RUN mkdir /code
|
||||
WORKDIR /code
|
||||
|
||||
# Add repository files
|
||||
ADD . /code/
|
||||
# Copy repository files
|
||||
COPY ccd_defs_check.py /code/ccd_defs_check.py
|
||||
COPY CMakeLists.txt /code/CMakeLists.txt
|
||||
COPY configure.sh /code/configure.sh
|
||||
COPY /.git/ /code/.git/
|
||||
COPY .gitignore /code/.gitignore
|
||||
COPY .gitmodules /code/.gitmodules
|
||||
COPY /modules/ /code/modules/
|
||||
COPY /opendm/ /code/opendm/
|
||||
COPY /patched_files/ /code/patched_files/
|
||||
COPY run.py /code/run.py
|
||||
COPY /scripts/ /code/scripts/
|
||||
COPY /SuperBuild/cmake/ /code/SuperBuild/cmake/
|
||||
COPY /SuperBuild/CMakeLists.txt /code/SuperBuild/CMakeLists.txt
|
||||
COPY /tests/ /code/tests/
|
||||
|
||||
# Update submodules
|
||||
RUN git submodule init && git submodule update
|
||||
|
||||
# Build OpenDroneMap
|
||||
RUN bash ./configure.sh && \
|
||||
mkdir build && cd build && cmake .. && make && cd .. && \
|
||||
chown -R odm:odm /code
|
||||
USER odm
|
||||
|
||||
ENV PYTHONPATH=${PYTHONPATH}:/code/SuperBuild/install/lib/python2.7/dist-packages:/code/SuperBuild/src/opensfm \
|
||||
LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/code/SuperBuild/install/lib
|
||||
#Compile code in SuperBuild and root directories
|
||||
RUN cd SuperBuild && mkdir build && cd build && cmake .. && make -j$(nproc) \
|
||||
&& cd ../.. && mkdir build && cd build && cmake .. && make -j$(nproc)
|
||||
|
||||
# Entry point
|
||||
VOLUME ["/images"]
|
||||
# WORKDIR /images
|
||||
ENTRYPOINT ["python", "/code/run.py", "--project-path", "/images"]
|
||||
ENTRYPOINT ["python", "/code/run.py", "--project-path", "/code/"]
|
||||
|
|
139
README.md
139
README.md
|
@ -1,13 +1,12 @@
|
|||
# OpenDroneMap
|
||||
|
||||

|
||||

|
||||
|
||||
What is it?
|
||||
===========
|
||||
## What is it?
|
||||
|
||||
OpenDroneMap is an open source toolkit for processing aerial drone imagery. Typical drones use simple point-and-shoot cameras, so the images from drones, while from a different perspective, are similar to any pictures taken from point-and-shoot cameras, i.e. non-metric imagery. OpenDroneMap turns those simple images into three dimensional geographic data that can be used in combination with other geographic datasets.
|
||||
|
||||

|
||||

|
||||
|
||||
In a word, OpenDroneMap is a toolchain for processing raw civilian UAS imagery to other useful products. What kind of products?
|
||||
|
||||
|
@ -19,67 +18,58 @@ In a word, OpenDroneMap is a toolchain for processing raw civilian UAS imagery t
|
|||
6. Digital Elevation Models
|
||||
7. etc.
|
||||
|
||||
So far, it does Point Clouds, Digital Surface Models, Textured Digital Surface Models, and Orthorectified Imagery.
|
||||
So far, it does Point Clouds, Digital Surface Models, Textured Digital Surface Models, and Orthorectified Imagery. Open Drone Map now includes state-of-the-art 3D reconstruction work by Michael Waechter, Nils Moehrle, and Michael Goesele. See their publication at http://www.gcc.tu-darmstadt.de/media/gcc/papers/Waechter-2014-LTB.pdf.
|
||||
|
||||
Users' mailing list: http://lists.osgeo.org/cgi-bin/mailman/listinfo/opendronemap-users
|
||||
|
||||
Developer's mailing list: http://lists.osgeo.org/cgi-bin/mailman/listinfo/opendronemap-dev
|
||||
## QUICKSTART
|
||||
|
||||
Overview video: https://www.youtube.com/watch?v=0UctfoeNB_Y
|
||||
Requires Ubuntu 14.04 or later, see https://github.com/OpenDroneMap/odm_vagrant for running on Windows in a VM
|
||||
|
||||
Developers
|
||||
=================
|
||||
*Support for Ubuntu 12.04 is currently BROKEN with the addition of OpenSfM and Ceres-Solver. It is likely to remain broken unless a champion is found to fix it.*
|
||||
|
||||
Help improve our software!
|
||||
**[Download the latest release here](https://github.com/OpenDroneMap/OpenDroneMap/releases)**
|
||||
|
||||
[](https://gitter.im/OpenDroneMap/OpenDroneMap?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
Current version: 0.2 (beta)
|
||||
|
||||
1. Try to keep commits clean and simple
|
||||
2. Submit a pull request with detailed changes and test results
|
||||
### Installation
|
||||
|
||||
Before installing you need to set your environment variables. Open the ~/.bashrc file on your machine and add the following 3 lines at the end. The file can be opened with ```gedit ~/.bashrc``` if you are using an Ubuntu desktop environment. Be sure to replace the "/your/path/" with the correct path to the location where you extracted OpenDroneMap:
|
||||
|
||||
Steps to get OpenDroneMap running:
|
||||
==================================
|
||||
export PYTHONPATH=$PYTHONPATH:/your/path/OpenDroneMap/SuperBuild/install/lib/python2.7/dist-packages
|
||||
export PYTHONPATH=$PYTHONPATH:/your/path/OpenDroneMap/SuperBuild/src/opensfm
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/your/path/OpenDroneMap/SuperBuild/install/lib
|
||||
|
||||
(Requires Ubuntu 14.04 or later, see https://github.com/OpenDroneMap/odm_vagrant for running on Windows in a VM)
|
||||
|
||||
Support for Ubuntu 12.04 is currently BROKEN with the addition of OpenSfM and Ceres-Solver. We are working hard to get it working again in the future.
|
||||
|
||||
#### Building OpenDroneMap using git
|
||||
|
||||
cd path/to/odm/dir
|
||||
git clone https://github.com/OpenDroneMap/OpenDroneMap.git .
|
||||
export PYTHONPATH=$PYTHONPATH:`pwd`/SuperBuild/install/lib/python2.7/dist-packages:`pwd`/SuperBuild/src/opensfm
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:`pwd`/SuperBuild/install/lib
|
||||
Now, enter the OpenDroneMap directory and compile all of the code by executing a single configuration script:
|
||||
|
||||
bash configure.sh
|
||||
mkdir build && cd build && cmake .. && make && cd ..
|
||||
|
||||
For Ubuntu 15.10 users, this will help you get running:
|
||||
For Ubuntu 15.10 users, this will help you get running:
|
||||
|
||||
sudo apt-get install python-xmltodict
|
||||
sudo ln -s /usr/lib/x86_64-linux-gnu/libproj.so.9 /usr/lib/libproj.so
|
||||
|
||||
#### Running OpenDroneMap
|
||||
### Run OpenDroneMap
|
||||
|
||||
First you need a set of images, which may or may not be georeferenced. There are two ways OpenDroneMap can understand geographic coordinates. First, the images can be geotagged in their EXIF data. This is the default. Alternatively, you can create a GCP file, [a process detailed here](https://github.com/OpenDroneMap/OpenDroneMap/wiki/2.-Running-OpenDroneMap#running-odm-with-ground-control)
|
||||
First you need a set of images, taken from a drone or otherwise.
|
||||
|
||||
Create a project folder and places your images in an "images" directory:
|
||||
|
||||
|
||||
|-- /path/to/project/
|
||||
|-- images/
|
||||
|-- img-1234.jpg
|
||||
|-- ...
|
||||
|
||||
|
||||
Example data can be cloned from https://github.com/OpenDroneMap/odm_data
|
||||
|
||||
Then run:
|
||||
|
||||
python run.py --project-path /path/to/project
|
||||
|
||||
There are many options for tuning your project. See the [wiki](https://github.com/OpenDroneMap/OpenDroneMap/wiki/3.-Run-Time-Parameters) or run `python run.py -h`
|
||||
There are many options for tuning your project. See the [wiki](https://github.com/OpenDroneMap/OpenDroneMap/wiki/Run-Time-Parameters) or run `python run.py -h`
|
||||
|
||||
When the process finishes, the results will be organized as follows
|
||||
### View Results
|
||||
|
||||
When the process finishes, the results will be organized as follows:
|
||||
|
||||
|-- images/
|
||||
|-- img-1234.jpg
|
||||
|
@ -88,11 +78,11 @@ When the process finishes, the results will be organized as follows
|
|||
|-- img-1234.jpg
|
||||
|-- ...
|
||||
|-- opensfm/
|
||||
|-- not much useful in here
|
||||
|-- see mapillary/opensfm repository for more info
|
||||
|-- pmvs/
|
||||
|-- recon0/
|
||||
|-- models/
|
||||
|-- option-0000.ply # Dense point cloud
|
||||
|-- option-0000.ply # Dense point cloud (not georeferenced)
|
||||
|-- odm_meshing/
|
||||
|-- odm_mesh.ply # A 3D mesh
|
||||
|-- odm_meshing_log.txt # Output of the meshing task. May point out errors.
|
||||
|
@ -112,44 +102,79 @@ When the process finishes, the results will be organized as follows
|
|||
|-- odm_orthophoto_log.txt # Log file
|
||||
|-- gdal_translate_log.txt # Log for georeferencing the png file
|
||||
|
||||
##### Viewing your results
|
||||
|
||||
Any file ending in .obj or .ply can be opened and viewed in [MeshLab](http://meshlab.sourceforge.net/) or similar software. That includes `pmvs/recon0/models/option-000.ply`, `odm_meshing/odm_mesh.ply`, `odm_texturing/odm_textured_model[_geo].obj`, or `odm_georeferencing/odm_georeferenced_model.ply`. Below is an example textured mesh:
|
||||
|
||||

|
||||

|
||||
|
||||
You can also view the orthophoto GeoTIFF in QGIS or other mapping software:
|
||||
|
||||

|
||||

|
||||
|
||||
#### Using Docker
|
||||
## Build and Run Using Docker
|
||||
|
||||
You can build and run OpenDroneMap in a Docker container:
|
||||
(Instructions below apply to Ubuntu 14.04, but the Docker image workflow
|
||||
has equivalent procedures for Mac OS X and Windows. See [docs.docker.com](docs.docker.com))
|
||||
|
||||
export IMAGES=/absolute/path/to/your/project
|
||||
docker build -t opendronemap:latest .
|
||||
docker run -v $IMAGES:/images opendronemap:latest
|
||||
OpenDroneMap is Dockerized, meaning you can use containerization to build and run it without tampering with the configuration of libraries and packages already
|
||||
installed on your machine. Docker software is free to install and use in this context. If you don't have it installed,
|
||||
see the [Docker Ubuntu installation tutorial](https://docs.docker.com/engine/installation/linux/ubuntulinux/) and follow the
|
||||
instructions up until "Create a Docker group" inclusive. Once Docker is installed, an OpenDroneMap Docker image can be created
|
||||
like so:
|
||||
|
||||
Replace /absolute/path/to/your/images with an absolute path to the directory containing your project (where the images are)
|
||||
To pass in custom parameters to the `run.py` script, simply pass it as arguments to the `docker run` command.
|
||||
git clone https://github.com/OpenDroneMap/OpenDroneMap.git
|
||||
cd OpenDroneMap
|
||||
docker build -t packages -f packages.Dockerfile .
|
||||
docker build -t odm_image .
|
||||
docker run -it --user root\
|
||||
-v $(pwd)/images:/code/images\
|
||||
-v $(pwd)/odm_orthophoto:/code/odm_orthophoto\
|
||||
-v $(pwd)/odm_texturing:/code/odm_texturing\
|
||||
--rm odm_image
|
||||
|
||||
---
|
||||
Using this method, the containerized ODM will process the images in the OpenDroneMap/images directory and output results
|
||||
to the OpenDroneMap/odm_orthophoto and OpenDroneMap/odm_texturing directories as described in the **Viewing Results** section.
|
||||
If you want to view other results outside the Docker image simply add which directories you're interested in to the run command in the same pattern
|
||||
established above. For example, if you're interested in the dense cloud results generated by PMVS and in the orthophoto,
|
||||
simply use the following `docker run` command after building the image:
|
||||
|
||||
docker run -it --user root\
|
||||
-v $(pwd)/images:/code/images\
|
||||
-v $(pwd)/pmvs:/code/pmvs\
|
||||
-v $(pwd)/odm_orthophoto:/code/odm_orthophoto\
|
||||
--rm odm_image
|
||||
|
||||
To pass in custom parameters to the run.py script, simply pass it as arguments to the `docker run` command.
|
||||
|
||||
## User Interface
|
||||
|
||||
A web interface and API to OpenDroneMap is currently under active development in the [WebODM](https://github.com/OpenDroneMap/WebODM) repository.
|
||||
|
||||
## Examples
|
||||
|
||||
Here are some other videos, which may be outdated:
|
||||
|
||||
- https://www.youtube.com/watch?v=7ZTufQkODLs (2015-01-30)
|
||||
- https://www.youtube.com/watch?v=m0i4GQdfl8A (2015-03-15)
|
||||
|
||||
Now that texturing is in the code base, you can access the full textured meshes using MeshLab. Open MeshLab, choose `File:Import Mesh` and choose your textured mesh from a location similar to the following: `reconstruction-with-image-size-1200-results\odm_texturing\odm_textured_model.obj`
|
||||
Now that texturing is in the code base, you can access the full textured meshes using MeshLab.
|
||||
Open MeshLab, choose `File:Import Mesh` and choose your textured mesh from a location similar to the following:
|
||||
`reconstruction-with-image-size-1200-results\odm_texturing\odm_textured_model.obj`. Long term, the aim is for
|
||||
the toolchain to also be able to optionally push to a variety of online data repositories, pushing hi-resolution
|
||||
aerials to [OpenAerialMap](https://openaerialmap.org/), point clouds to [OpenTopography](http://opentopography.org/),
|
||||
and pushing digital elevation models to an emerging global repository (yet to be named...). That leaves only
|
||||
digital surface model meshes and UV textured meshes with no global repository home.
|
||||
|
||||
---
|
||||
## Documentation:
|
||||
|
||||
Long term, the aim is for the toolchain to also be able to optionally push to a variety of online data repositories, pushing hi-resolution aerials to [OpenAerialMap](http://opentopography.org/), point clouds to [OpenTopography](http://opentopography.org/), and pushing digital elevation models to an emerging global repository (yet to be named...). That leaves only digital surface model meshes and UV textured meshes with no global repository home.
|
||||
---
|
||||
For documentation, please take a look at our [wiki](https://github.com/OpenDroneMap/OpenDroneMap/wiki).Check here first if you are heaving problems. If you still need help, look through the issue queue or create one. There's also a general help chat [here](https://gitter.im/OpenDroneMap/generalhelp).
|
||||
|
||||
## Developers
|
||||
|
||||
Help improve our software!
|
||||
|
||||
[](https://gitter.im/OpenDroneMap/OpenDroneMap?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
1. Try to keep commits clean and simple
|
||||
2. Submit a pull request with detailed changes and test results
|
||||
|
||||
|
||||
Documentation:
|
||||
==============
|
||||
|
||||
For documentation, please take a look at our [wiki](https://github.com/OpenDroneMap/OpenDroneMap/wiki).
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
|
||||
project(ODM-SuperBuild)
|
||||
|
||||
|
@ -103,7 +103,9 @@ set(custom_libs OpenGV
|
|||
CMVS
|
||||
Catkin
|
||||
Ecto
|
||||
PDAL)
|
||||
PDAL
|
||||
MvsTexturing
|
||||
)
|
||||
|
||||
# Dependencies of the SLAM module
|
||||
if(ODM_BUILD_SLAM)
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
set(_proj_name mvstexturing)
|
||||
set(_SB_BINARY_DIR "${SB_BINARY_DIR}/${_proj_name}")
|
||||
|
||||
ExternalProject_Add(${_proj_name}
|
||||
DEPENDS
|
||||
PREFIX ${_SB_BINARY_DIR}
|
||||
TMP_DIR ${_SB_BINARY_DIR}/tmp
|
||||
STAMP_DIR ${_SB_BINARY_DIR}/stamp
|
||||
#--Download step--------------
|
||||
DOWNLOAD_DIR ${SB_DOWNLOAD_DIR}
|
||||
URL https://github.com/nmoehrle/mvs-texturing/archive/dab68acaa693275c183c254a958130ee6d29c3e4.zip
|
||||
URL_MD5 0b0466f5d1046699594ce7fc77bdad02
|
||||
#--Update/Patch step----------
|
||||
UPDATE_COMMAND ""
|
||||
#--Configure step-------------
|
||||
SOURCE_DIR ${SB_SOURCE_DIR}/${_proj_name}
|
||||
CMAKE_ARGS
|
||||
-DRESEARCH=OFF
|
||||
-DCMAKE_BUILD_TYPE:STRING=Release
|
||||
-DCMAKE_INSTALL_PREFIX:PATH=${SB_INSTALL_DIR}
|
||||
#--Build step-----------------
|
||||
BINARY_DIR ${_SB_BINARY_DIR}
|
||||
#--Install step---------------
|
||||
INSTALL_DIR ${SB_INSTALL_DIR}
|
||||
#--Output logging-------------
|
||||
LOG_DOWNLOAD OFF
|
||||
LOG_CONFIGURE OFF
|
||||
LOG_BUILD OFF
|
||||
)
|
|
@ -8,15 +8,17 @@ ExternalProject_Add(${_proj_name}
|
|||
STAMP_DIR ${_SB_BINARY_DIR}/stamp
|
||||
#--Download step--------------
|
||||
DOWNLOAD_DIR ${SB_DOWNLOAD_DIR}
|
||||
URL https://github.com/mapillary/OpenSfM/archive/a4b07056aec1184692c1432fbdd1074710aec32b.zip
|
||||
URL_MD5 42B2B1994C3309BBF4525C8CC1F6F741
|
||||
URL https://github.com/mapillary/OpenSfM/archive/odm-4.zip
|
||||
URL_MD5 C7444B8595AAE33C6E191D8E8518BD5E
|
||||
#--Update/Patch step----------
|
||||
UPDATE_COMMAND ""
|
||||
#--Configure step-------------
|
||||
SOURCE_DIR ${SB_SOURCE_DIR}/${_proj_name}
|
||||
CONFIGURE_COMMAND cmake <SOURCE_DIR>/${_proj_name}/src
|
||||
-DCERES_ROOT_DIR=${SB_INSTALL_DIR}
|
||||
-DCERES_ROOT_DIR=${SB_INSTALL_DIR}
|
||||
-DOpenCV_DIR=${SB_INSTALL_DIR}/share/OpenCV
|
||||
-DOPENSFM_BUILD_TESTS=off
|
||||
|
||||
#--Build step-----------------
|
||||
BINARY_DIR ${_SB_BINARY_DIR}
|
||||
#--Install step---------------
|
||||
|
|
|
@ -1,15 +1,74 @@
|
|||
# Contributor Code of Conduct
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
||||
## Our Pledge
|
||||
|
||||
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level of experience,
|
||||
nationality, personal appearance, race, religion, or sexual identity and
|
||||
orientation.
|
||||
|
||||
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
||||
## Our Standards
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.1.0, available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/)
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at [INSERT EMAIL ADDRESS]. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
|
|
|
@ -1,43 +1,32 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Check OS
|
||||
if [ ! $(command -v apt-get) ]; then
|
||||
echo -e "\e[1;31mERROR: Not a Debian-based linux system.
|
||||
Impossible to install OpenCV with this script\e[0;39m"
|
||||
exit 1
|
||||
fi
|
||||
## Set up library paths
|
||||
RUNPATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
export PYTHONPATH=$RUNPATH/SuperBuild/install/lib/python2.7/dist-packages:$RUNPATH/SuperBuild/src/opensfm:$PYTHONPATH
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$RUNPATH/SuperBuild/install/lib
|
||||
|
||||
## Before installing
|
||||
echo -e "\e[1;34mUpdating the system\e[0;39m"
|
||||
echo "Updating the system"
|
||||
sudo apt-get update
|
||||
END_CMD1=$?
|
||||
# sudo apt-get upgrade -y
|
||||
# END_CMD2=$?
|
||||
if [ $END_CMD1 -ne 0 ]
|
||||
then
|
||||
echo -e "\e[1;31mERROR: \e[39mWhen Updating the system\e[0m"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
## Install Required Requisites
|
||||
echo -e "\e[1;34mInstalling Required Requisites\e[0;39m"
|
||||
sudo apt-get install build-essential \
|
||||
cmake \
|
||||
echo "Installing Required Requisites"
|
||||
sudo apt-get install -y -qq build-essential \
|
||||
git \
|
||||
cmake \
|
||||
python-pip \
|
||||
libgdal-dev \
|
||||
gdal-bin \
|
||||
libgeotiff-dev \
|
||||
pkg-config -y -qq
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo -e "\e[1;31mERROR: \e[39mWhen Installing Required Requisites\e[0m"
|
||||
exit 1
|
||||
fi
|
||||
pkg-config
|
||||
|
||||
## Installing Optional Requisites
|
||||
echo -e "\e[1;34mInstalling OpenCV Dependencies\e[0;39m"
|
||||
sudo apt-get install libgtk2.0-dev \
|
||||
echo "Getting CMake 3.1 for MVS-Texturing"
|
||||
sudo apt-get install -y software-properties-common python-software-properties
|
||||
sudo add-apt-repository -y ppa:george-edison55/cmake-3.x
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y --only-upgrade cmake
|
||||
|
||||
echo "Installing OpenCV Dependencies"
|
||||
sudo apt-get install -y -qq libgtk2.0-dev \
|
||||
libavcodec-dev \
|
||||
libavformat-dev \
|
||||
libswscale-dev \
|
||||
|
@ -54,20 +43,14 @@ sudo apt-get install libgtk2.0-dev \
|
|||
libxext-dev \
|
||||
liblapack-dev \
|
||||
libeigen3-dev \
|
||||
libvtk5-dev -y -qq
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo -e "\e[1;31mERROR: \e[39mError when Installing Dependencies Requisites\e[0m"
|
||||
exit 1
|
||||
fi
|
||||
libvtk5-dev
|
||||
|
||||
## Remove libdc1394-22-dev due to python opencv issue
|
||||
echo -e "\e[1;34mRemoving libdc1394-22-dev\e[0;39m"
|
||||
echo "Removing libdc1394-22-dev due to python opencv issue"
|
||||
sudo apt-get remove libdc1394-22-dev
|
||||
|
||||
## Installing OpenSfM Requisites
|
||||
echo -e "\e[1;34mInstalling OpenSfM Dependencies\e[0;39m"
|
||||
sudo apt-get install python-networkx \
|
||||
echo "Installing OpenSfM Dependencies"
|
||||
sudo apt-get install -y -qq python-networkx \
|
||||
libgoogle-glog-dev \
|
||||
libsuitesparse-dev \
|
||||
libboost-filesystem-dev \
|
||||
|
@ -75,54 +58,34 @@ sudo apt-get install python-networkx \
|
|||
libboost-regex-dev \
|
||||
libboost-python-dev \
|
||||
libboost-date-time-dev \
|
||||
libboost-thread-dev -y -qq
|
||||
libboost-thread-dev \
|
||||
python-pyproj
|
||||
|
||||
sudo pip install -U PyYAML \
|
||||
exifread \
|
||||
gpxpy \
|
||||
xmltodict
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo -e "\e[1;31mERROR: \e[39mError when Installing OpenSfM Dependencies\e[0m"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
## Installing Ecto Requisites
|
||||
echo -e "\e[1;34mInstalling Ecto Dependencies\e[0;39m"
|
||||
echo "Installing Ecto Dependencies"
|
||||
sudo pip install -U catkin-pkg
|
||||
sudo apt-get install python-empy \
|
||||
sudo apt-get install -y -qq python-empy \
|
||||
python-nose \
|
||||
python-pyside -y -qq
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo -e "\e[1;31mERROR: \e[39mError when Installing Ecto Dependencies\e[0m"
|
||||
exit 1
|
||||
fi
|
||||
python-pyside
|
||||
|
||||
## Installing OpenDroneMap Requisites
|
||||
echo -e "\e[1;34mInstalling OpenDroneMap Dependencies\e[0;39m"
|
||||
sudo apt-get install python-pyexiv2 \
|
||||
echo "Installing OpenDroneMap Dependencies"
|
||||
sudo apt-get install -y -qq python-pyexiv2 \
|
||||
python-scipy \
|
||||
jhead \
|
||||
liblas-bin -y -qq
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo -e "\e[1;31mERROR: \e[39mError when Installing OpenDroneMap Dependencies\e[0m"
|
||||
exit 1
|
||||
fi
|
||||
liblas-bin
|
||||
|
||||
## Get sys vars
|
||||
NUM_CORES=`grep -c processor /proc/cpuinfo`
|
||||
|
||||
## Add SuperBuild path to the library path
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:`pwd`/SuperBuild/install/lib
|
||||
|
||||
## Add SuperBuild path to the python path
|
||||
export PYTHONPATH=$PYTHONPATH:`pwd`/SuperBuild/install/lib/python2.7/dist-packages:`pwd`/SuperBuild/src/opensfm
|
||||
|
||||
## Compile SuperBuild
|
||||
echo "Compiling SuperBuild"
|
||||
cd SuperBuild
|
||||
mkdir -p build && cd build
|
||||
cmake .. && make -j${NUM_CORES}
|
||||
cmake .. && make -j$(nproc)
|
||||
|
||||
echo -e "\e[1;34mScript finished\e[0;39m"
|
||||
echo "Compiling build"
|
||||
cd ../..
|
||||
mkdir -p build && cd build
|
||||
cmake .. && make -j$(nproc)
|
||||
|
||||
echo "Configuration Finished"
|
||||
|
|
|
@ -24,3 +24,4 @@ Licensing for portions of OpenDroneMap are as follows:
|
|||
* vtk5 - BSD - http://www.vtk.org/VTK/project/license.html
|
||||
* libext - https://github.com/OpenDroneMap/OpenDroneMap/blob/gh-pages/licenses/libext_copyright.txt
|
||||
* libx11 - https://github.com/OpenDroneMap/OpenDroneMap/blob/gh-pages/licenses/libx11_copyright.txt
|
||||
* MVS Texturing - BSD - https://github.com/nmoehrle/mvs-texturing/blob/master/LICENSE.txt
|
||||
|
|
|
@ -210,7 +210,7 @@ void UtmExtractor::extractUtm()
|
|||
// Open output file
|
||||
std::ofstream outputCoordStream(outputCoordFileName_.c_str());
|
||||
if (!outputCoordStream.good()) {
|
||||
throw UtmExtractorException("Failed to openg " + outputCoordFileName_ + " for writing.");
|
||||
throw UtmExtractorException("Failed to open " + outputCoordFileName_ + " for writing.");
|
||||
}
|
||||
outputCoordStream.precision(10);
|
||||
|
||||
|
|
|
@ -1,203 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by Qt Creator 2.4.1, 2015-02-03T10:36:15. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.ActiveTarget</variable>
|
||||
<value type="int">0</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.EditorSettings</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
|
||||
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
|
||||
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
|
||||
<value type="QString" key="language">Cpp</value>
|
||||
<valuemap type="QVariantMap" key="value">
|
||||
<value type="QString" key="CurrentPreferences">CppGlobal</value>
|
||||
</valuemap>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
|
||||
<value type="QString" key="language">QmlJS</value>
|
||||
<valuemap type="QVariantMap" key="value">
|
||||
<value type="QString" key="CurrentPreferences">QmlJSGlobal</value>
|
||||
</valuemap>
|
||||
</valuemap>
|
||||
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
|
||||
<value type="QByteArray" key="EditorConfiguration.Codec">System</value>
|
||||
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
|
||||
<value type="int" key="EditorConfiguration.IndentSize">4</value>
|
||||
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
|
||||
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
|
||||
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
|
||||
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
|
||||
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
|
||||
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
|
||||
<value type="int" key="EditorConfiguration.TabSize">8</value>
|
||||
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
|
||||
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
|
||||
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
|
||||
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
|
||||
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
|
||||
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.PluginSettings</variable>
|
||||
<valuemap type="QVariantMap"/>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Target.0</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.DefaultCMakeTarget</value>
|
||||
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
|
||||
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
||||
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
|
||||
<value type="QString" key="CMakeProjectManager.CMakeBuildConfiguration.BuildDirectory">/home/spotscale/odm/OpenDroneMap-texturing_orthophoto_spotscale_additions/odm_georef-build</value>
|
||||
<value type="QString" key="CMakeProjectManager.CMakeBuildConfiguration.ToolChain">ProjectExplorer.ToolChain.Gcc:/usr/bin/g++.x86-linux-generic-elf-32bit./usr/bin/gdb</value>
|
||||
<value type="QString" key="ProjectExplorer.BuildCOnfiguration.ToolChain">ProjectExplorer.ToolChain.Gcc:/usr/bin/g++.x86-linux-generic-elf-32bit./usr/bin/gdb</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<value type="QString" key="CMakeProjectManager.MakeStep.AdditionalArguments"></value>
|
||||
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets"/>
|
||||
<value type="bool" key="CMakeProjectManager.MakeStep.Clean">false</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<value type="QString" key="CMakeProjectManager.MakeStep.AdditionalArguments">clean</value>
|
||||
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets"/>
|
||||
<value type="bool" key="CMakeProjectManager.MakeStep.Clean">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">all</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">No deployment</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
|
||||
<value type="bool" key="Analyzer.Project.UseGlobal">true</value>
|
||||
<value type="bool" key="Analyzer.Project.UseGlobal">true</value>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
|
||||
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
|
||||
<value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
|
||||
<value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
|
||||
<value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
|
||||
<value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
|
||||
<value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
|
||||
<value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
|
||||
<value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
|
||||
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
|
||||
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
|
||||
<value type="int">0</value>
|
||||
<value type="int">1</value>
|
||||
<value type="int">2</value>
|
||||
<value type="int">3</value>
|
||||
<value type="int">4</value>
|
||||
<value type="int">5</value>
|
||||
<value type="int">6</value>
|
||||
<value type="int">7</value>
|
||||
<value type="int">8</value>
|
||||
<value type="int">9</value>
|
||||
<value type="int">10</value>
|
||||
<value type="int">11</value>
|
||||
<value type="int">12</value>
|
||||
<value type="int">13</value>
|
||||
<value type="int">14</value>
|
||||
</valuelist>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
|
||||
<value type="int">0</value>
|
||||
<value type="int">1</value>
|
||||
<value type="int">2</value>
|
||||
<value type="int">3</value>
|
||||
<value type="int">4</value>
|
||||
<value type="int">5</value>
|
||||
<value type="int">6</value>
|
||||
<value type="int">7</value>
|
||||
<value type="int">8</value>
|
||||
<value type="int">9</value>
|
||||
<value type="int">10</value>
|
||||
<value type="int">11</value>
|
||||
<value type="int">12</value>
|
||||
<value type="int">13</value>
|
||||
<value type="int">14</value>
|
||||
</valuelist>
|
||||
<value type="int" key="CMakeProjectManager.BaseEnvironmentBase">2</value>
|
||||
<value type="QString" key="CMakeProjectManager.CMakeRunConfiguation.Title">odm_georef</value>
|
||||
<value type="QString" key="CMakeProjectManager.CMakeRunConfiguration.Arguments">-verbose -bundleFile ../../../shared_folder/seneca/reconstruction-with-image-size-1200/pmvs/bundle.rd.out -inputFile ../../../shared_folder/seneca/reconstruction-with-image-size-1200-results/odm_texturing/odm_textured_model.obj -imagesListPath ../../../shared_folder/seneca/reconstruction-with-image-size-1200/pmvs/list.rd.txt -gcpFile ../../../shared_folder/seneca_georef_input/gcp_list.txt -imagesPath ../../../shared_folder/seneca/ -bundleResizedTo 1200</value>
|
||||
<value type="bool" key="CMakeProjectManager.CMakeRunConfiguration.UseTerminal">false</value>
|
||||
<valuelist type="QVariantList" key="CMakeProjectManager.CMakeRunConfiguration.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="CMakeProjectManager.CMakeRunConfiguration.UserWorkingDirectory"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">odm_georef</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeRunConfiguration.</value>
|
||||
<value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebugger">true</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">false</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.TargetCount</variable>
|
||||
<value type="int">1</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Updater.EnvironmentId</variable>
|
||||
<value type="QString">{785a73be-b55f-490c-9d46-e1451c235840}</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
|
||||
<value type="int">10</value>
|
||||
</data>
|
||||
</qtcreator>
|
|
@ -6,9 +6,6 @@
|
|||
#include <opencv2/imgproc/imgproc.hpp>
|
||||
#include <opencv2/highgui/highgui.hpp>
|
||||
|
||||
// Modified PCL
|
||||
#include "modifiedPclFunctions.hpp"
|
||||
|
||||
// This
|
||||
#include "Georef.hpp"
|
||||
|
||||
|
@ -753,7 +750,7 @@ void Georef::performGeoreferencingWithGCP()
|
|||
log_ << "Reading mesh file " << inputObjFilename_ <<"\n";
|
||||
log_ << '\n';
|
||||
pcl::TextureMesh mesh;
|
||||
if (pcl::io::loadOBJFile(inputObjFilename_, mesh) == -1)
|
||||
if (loadObjFile(inputObjFilename_, mesh) == -1)
|
||||
{
|
||||
throw GeorefException("Error when reading model from:\n" + inputObjFilename_ + "\n");
|
||||
}
|
||||
|
@ -1077,7 +1074,7 @@ void Georef::createGeoreferencedModelFromExifData()
|
|||
log_ << '\n';
|
||||
log_ << "Reading mesh file...\n";
|
||||
pcl::TextureMesh mesh;
|
||||
pcl::io::loadOBJFile(inputObjFilename_, mesh);
|
||||
loadObjFile(inputObjFilename_, mesh);
|
||||
log_ << ".. mesh file read.\n";
|
||||
|
||||
// Contains the vertices of the mesh.
|
||||
|
@ -1249,3 +1246,364 @@ void Georef::printGeorefSystem()
|
|||
}
|
||||
|
||||
|
||||
bool Georef::loadObjFile(std::string inputFile, pcl::TextureMesh &mesh)
|
||||
{
|
||||
int data_type;
|
||||
unsigned int data_idx;
|
||||
int file_version;
|
||||
int offset = 0;
|
||||
Eigen::Vector4f origin;
|
||||
Eigen::Quaternionf orientation;
|
||||
|
||||
if (!readHeader(inputFile, mesh.cloud, origin, orientation, file_version, data_type, data_idx, offset))
|
||||
{
|
||||
throw GeorefException("Problem reading header in modelfile!\n");
|
||||
}
|
||||
|
||||
std::ifstream fs;
|
||||
|
||||
fs.open (inputFile.c_str (), std::ios::binary);
|
||||
if (!fs.is_open () || fs.fail ())
|
||||
{
|
||||
//PCL_ERROR ("[pcl::OBJReader::readHeader] Could not open file '%s'! Error : %s\n", file_name.c_str (), strerror(errno));
|
||||
fs.close ();
|
||||
log_<<"Could not read mesh from file ";
|
||||
log_ << inputFile.c_str();
|
||||
log_ <<"\n";
|
||||
|
||||
throw GeorefException("Problem reading mesh from file!\n");
|
||||
}
|
||||
|
||||
// Seek at the given offset
|
||||
fs.seekg (data_idx, std::ios::beg);
|
||||
|
||||
// Get normal_x field indices
|
||||
int normal_x_field = -1;
|
||||
for (std::size_t i = 0; i < mesh.cloud.fields.size (); ++i)
|
||||
{
|
||||
if (mesh.cloud.fields[i].name == "normal_x")
|
||||
{
|
||||
normal_x_field = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t v_idx = 0;
|
||||
std::size_t vn_idx = 0;
|
||||
std::size_t vt_idx = 0;
|
||||
std::size_t f_idx = 0;
|
||||
std::string line;
|
||||
std::vector<std::string> st;
|
||||
std::vector<Eigen::Vector2f> coordinates;
|
||||
std::vector<Eigen::Vector2f> allTexCoords;
|
||||
|
||||
std::map<int, int> f2vt;
|
||||
|
||||
try
|
||||
{
|
||||
while (!fs.eof ())
|
||||
{
|
||||
getline (fs, line);
|
||||
// Ignore empty lines
|
||||
if (line == "")
|
||||
continue;
|
||||
|
||||
// Tokenize the line
|
||||
std::stringstream sstream (line);
|
||||
sstream.imbue (std::locale::classic ());
|
||||
line = sstream.str ();
|
||||
boost::trim (line);
|
||||
boost::split (st, line, boost::is_any_of ("\t\r "), boost::token_compress_on);
|
||||
|
||||
// Ignore comments
|
||||
if (st[0] == "#")
|
||||
continue;
|
||||
// Vertex
|
||||
if (st[0] == "v")
|
||||
{
|
||||
try
|
||||
{
|
||||
for (int i = 1, f = 0; i < 4; ++i, ++f)
|
||||
{
|
||||
float value = boost::lexical_cast<float> (st[i]);
|
||||
memcpy (&mesh.cloud.data[v_idx * mesh.cloud.point_step + mesh.cloud.fields[f].offset], &value, sizeof (float));
|
||||
}
|
||||
|
||||
++v_idx;
|
||||
}
|
||||
catch (const boost::bad_lexical_cast &e)
|
||||
{
|
||||
log_<<"Unable to convert %s to vertex coordinates!\n";
|
||||
throw GeorefException("Unable to convert %s to vertex coordinates!");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Vertex normal
|
||||
if (st[0] == "vn")
|
||||
{
|
||||
try
|
||||
{
|
||||
for (int i = 1, f = normal_x_field; i < 4; ++i, ++f)
|
||||
{
|
||||
float value = boost::lexical_cast<float> (st[i]);
|
||||
memcpy (&mesh.cloud.data[vn_idx * mesh.cloud.point_step + mesh.cloud.fields[f].offset],
|
||||
&value,
|
||||
sizeof (float));
|
||||
}
|
||||
++vn_idx;
|
||||
}
|
||||
catch (const boost::bad_lexical_cast &e)
|
||||
{
|
||||
log_<<"Unable to convert %s to vertex normal!\n";
|
||||
throw GeorefException("Unable to convert %s to vertex normal!");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Texture coordinates
|
||||
if (st[0] == "vt")
|
||||
{
|
||||
try
|
||||
{
|
||||
Eigen::Vector3f c (0, 0, 0);
|
||||
for (std::size_t i = 1; i < st.size (); ++i)
|
||||
c[i-1] = boost::lexical_cast<float> (st[i]);
|
||||
|
||||
if (c[2] == 0)
|
||||
coordinates.push_back (Eigen::Vector2f (c[0], c[1]));
|
||||
else
|
||||
coordinates.push_back (Eigen::Vector2f (c[0]/c[2], c[1]/c[2]));
|
||||
++vt_idx;
|
||||
|
||||
}
|
||||
catch (const boost::bad_lexical_cast &e)
|
||||
{
|
||||
log_<<"Unable to convert %s to vertex texture coordinates!\n";
|
||||
throw GeorefException("Unable to convert %s to vertex texture coordinates!");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Material
|
||||
if (st[0] == "usemtl")
|
||||
{
|
||||
mesh.tex_polygons.push_back (std::vector<pcl::Vertices> ());
|
||||
mesh.tex_materials.push_back (pcl::TexMaterial ());
|
||||
for (std::size_t i = 0; i < companions_.size (); ++i)
|
||||
{
|
||||
std::vector<pcl::TexMaterial>::const_iterator mat_it = companions_[i].getMaterial (st[1]);
|
||||
if (mat_it != companions_[i].materials_.end ())
|
||||
{
|
||||
mesh.tex_materials.back () = *mat_it;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// We didn't find the appropriate material so we create it here with name only.
|
||||
if (mesh.tex_materials.back ().tex_name == "")
|
||||
mesh.tex_materials.back ().tex_name = st[1];
|
||||
mesh.tex_coordinates.push_back (coordinates);
|
||||
coordinates.clear ();
|
||||
continue;
|
||||
}
|
||||
// Face
|
||||
if (st[0] == "f")
|
||||
{
|
||||
//We only care for vertices indices
|
||||
pcl::Vertices face_v; face_v.vertices.resize (st.size () - 1);
|
||||
for (std::size_t i = 1; i < st.size (); ++i)
|
||||
{
|
||||
int v;
|
||||
sscanf (st[i].c_str (), "%d", &v);
|
||||
v = (v < 0) ? v_idx + v : v - 1;
|
||||
face_v.vertices[i-1] = v;
|
||||
|
||||
int v2, vt, vn;
|
||||
sscanf (st[i].c_str (), "%d/%d/%d", &v2, &vt, &vn);
|
||||
f2vt[3*(f_idx) + i-1] = vt-1;
|
||||
}
|
||||
mesh.tex_polygons.back ().push_back (face_v);
|
||||
++f_idx;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const char *exception)
|
||||
{
|
||||
fs.close ();
|
||||
log_<<"Unable to read file!\n";
|
||||
throw GeorefException("Unable to read file!");
|
||||
}
|
||||
|
||||
if (vt_idx != v_idx)
|
||||
{
|
||||
std::vector<Eigen::Vector2f> texcoordinates = std::vector<Eigen::Vector2f>(0);
|
||||
|
||||
for (size_t faceIndex = 0; faceIndex < f_idx; ++faceIndex)
|
||||
{
|
||||
for(size_t i = 0; i < 3; ++i)
|
||||
{
|
||||
Eigen::Vector2f vt = mesh.tex_coordinates[0][f2vt[3*faceIndex+i]];
|
||||
texcoordinates.push_back(vt);
|
||||
}
|
||||
}
|
||||
|
||||
mesh.tex_coordinates.clear();
|
||||
mesh.tex_coordinates.push_back(texcoordinates);
|
||||
}
|
||||
|
||||
fs.close();
|
||||
return (0);
|
||||
}
|
||||
|
||||
bool Georef::readHeader (const std::string &file_name, pcl::PCLPointCloud2 &cloud,
|
||||
Eigen::Vector4f &origin, Eigen::Quaternionf &orientation,
|
||||
int &file_version, int &data_type, unsigned int &data_idx,
|
||||
const int offset)
|
||||
{
|
||||
origin = Eigen::Vector4f::Zero ();
|
||||
orientation = Eigen::Quaternionf::Identity ();
|
||||
file_version = 0;
|
||||
cloud.width = cloud.height = cloud.point_step = cloud.row_step = 0;
|
||||
cloud.data.clear ();
|
||||
data_type = 0;
|
||||
data_idx = offset;
|
||||
|
||||
std::ifstream fs;
|
||||
std::string line;
|
||||
|
||||
if (file_name == "" || !boost::filesystem::exists (file_name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open file in binary mode to avoid problem of
|
||||
// std::getline() corrupting the result of ifstream::tellg()
|
||||
fs.open (file_name.c_str (), std::ios::binary);
|
||||
if (!fs.is_open () || fs.fail ())
|
||||
{
|
||||
fs.close ();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Seek at the given offset
|
||||
fs.seekg (offset, std::ios::beg);
|
||||
|
||||
// Read the header and fill it in with wonderful values
|
||||
bool vertex_normal_found = false;
|
||||
bool vertex_texture_found = false;
|
||||
// Material library, skip for now!
|
||||
// bool material_found = false;
|
||||
std::vector<std::string> material_files;
|
||||
std::size_t nr_point = 0;
|
||||
std::vector<std::string> st;
|
||||
|
||||
try
|
||||
{
|
||||
while (!fs.eof ())
|
||||
{
|
||||
getline (fs, line);
|
||||
// Ignore empty lines
|
||||
if (line == "")
|
||||
continue;
|
||||
|
||||
// Tokenize the line
|
||||
std::stringstream sstream (line);
|
||||
sstream.imbue (std::locale::classic ());
|
||||
line = sstream.str ();
|
||||
boost::trim (line);
|
||||
boost::split (st, line, boost::is_any_of ("\t\r "), boost::token_compress_on);
|
||||
// Ignore comments
|
||||
if (st.at (0) == "#")
|
||||
continue;
|
||||
|
||||
// Vertex
|
||||
if (st.at (0) == "v")
|
||||
{
|
||||
++nr_point;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Vertex texture
|
||||
if ((st.at (0) == "vt") && !vertex_texture_found)
|
||||
{
|
||||
vertex_texture_found = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Vertex normal
|
||||
if ((st.at (0) == "vn") && !vertex_normal_found)
|
||||
{
|
||||
vertex_normal_found = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Material library, skip for now!
|
||||
if (st.at (0) == "mtllib")
|
||||
{
|
||||
material_files.push_back (st.at (1));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const char *exception)
|
||||
{
|
||||
fs.close ();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!nr_point)
|
||||
{
|
||||
fs.close ();
|
||||
return false;
|
||||
}
|
||||
|
||||
int field_offset = 0;
|
||||
for (int i = 0; i < 3; ++i, field_offset += 4)
|
||||
{
|
||||
cloud.fields.push_back (pcl::PCLPointField ());
|
||||
cloud.fields[i].offset = field_offset;
|
||||
cloud.fields[i].datatype = pcl::PCLPointField::FLOAT32;
|
||||
cloud.fields[i].count = 1;
|
||||
}
|
||||
|
||||
cloud.fields[0].name = "x";
|
||||
cloud.fields[1].name = "y";
|
||||
cloud.fields[2].name = "z";
|
||||
|
||||
if (vertex_normal_found)
|
||||
{
|
||||
std::string normals_names[3] = { "normal_x", "normal_y", "normal_z" };
|
||||
for (int i = 0; i < 3; ++i, field_offset += 4)
|
||||
{
|
||||
cloud.fields.push_back (pcl::PCLPointField ());
|
||||
pcl::PCLPointField& last = cloud.fields.back ();
|
||||
last.name = normals_names[i];
|
||||
last.offset = field_offset;
|
||||
last.datatype = pcl::PCLPointField::FLOAT32;
|
||||
last.count = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (material_files.size () > 0)
|
||||
{
|
||||
for (std::size_t i = 0; i < material_files.size (); ++i)
|
||||
{
|
||||
pcl::MTLReader companion;
|
||||
|
||||
if (companion.read (file_name, material_files[i]))
|
||||
{
|
||||
log_<<"Problem reading material file.";
|
||||
}
|
||||
|
||||
companions_.push_back (companion);
|
||||
}
|
||||
}
|
||||
|
||||
cloud.point_step = field_offset;
|
||||
cloud.width = nr_point;
|
||||
cloud.height = 1;
|
||||
cloud.row_step = cloud.point_step * cloud.width;
|
||||
cloud.is_dense = true;
|
||||
cloud.data.resize (cloud.point_step * nr_point);
|
||||
fs.close ();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
// PCL
|
||||
#include <pcl/common/eigen.h>
|
||||
#include <pcl/common/common.h>
|
||||
// Modified PCL
|
||||
#include "modifiedPclFunctions.hpp"
|
||||
|
||||
// Logger
|
||||
#include "Logger.hpp"
|
||||
|
@ -204,6 +206,24 @@ private:
|
|||
**/
|
||||
void printGeorefSystem();
|
||||
|
||||
/*!
|
||||
* \brief Loads a model from an .obj file (replacement for the pcl obj loader).
|
||||
*
|
||||
* \param inputFile Path to the .obj file.
|
||||
* \param mesh The model.
|
||||
* \return True if model was loaded successfully.
|
||||
*/
|
||||
bool loadObjFile(std::string inputFile, pcl::TextureMesh &mesh);
|
||||
|
||||
/*!
|
||||
* \brief Function is compied straight from the function in the pcl::io module.
|
||||
*/
|
||||
bool readHeader (const std::string &file_name, pcl::PCLPointCloud2 &cloud,
|
||||
Eigen::Vector4f &origin, Eigen::Quaternionf &orientation,
|
||||
int &file_version, int &data_type, unsigned int &data_idx,
|
||||
const int offset);
|
||||
|
||||
|
||||
Logger log_; /**< Logging object. */
|
||||
std::string logFile_; /**< The path to the output log file. */
|
||||
|
||||
|
@ -230,6 +250,10 @@ private:
|
|||
std::vector<std::string> imageList_; /**< A vector containing the names of the corresponding cameras. **/
|
||||
|
||||
GeorefSystem georefSystem_; /**< Contains the georeference system. **/
|
||||
|
||||
bool multiMaterial_; /**< True if the mesh has multiple materials. **/
|
||||
|
||||
std::vector<pcl::MTLReader> companions_; /**< Materials (used by loadOBJFile). **/
|
||||
};
|
||||
|
||||
/*!
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <pcl/point_types.h>
|
||||
#include <pcl/surface/texture_mapping.h>
|
||||
#include <pcl/io/ply_io.h>
|
||||
#include <pcl/io/obj_io.h>
|
||||
|
||||
int saveOBJFile(const std::string &file_name, const pcl::TextureMesh &tex_mesh, unsigned precision);
|
||||
|
||||
|
|
|
@ -1,203 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by Qt Creator 2.4.1, 2015-02-04T09:12:31. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.ActiveTarget</variable>
|
||||
<value type="int">0</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.EditorSettings</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
|
||||
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
|
||||
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
|
||||
<value type="QString" key="language">Cpp</value>
|
||||
<valuemap type="QVariantMap" key="value">
|
||||
<value type="QString" key="CurrentPreferences">CppGlobal</value>
|
||||
</valuemap>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
|
||||
<value type="QString" key="language">QmlJS</value>
|
||||
<valuemap type="QVariantMap" key="value">
|
||||
<value type="QString" key="CurrentPreferences">QmlJSGlobal</value>
|
||||
</valuemap>
|
||||
</valuemap>
|
||||
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
|
||||
<value type="QByteArray" key="EditorConfiguration.Codec">System</value>
|
||||
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
|
||||
<value type="int" key="EditorConfiguration.IndentSize">4</value>
|
||||
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
|
||||
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
|
||||
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
|
||||
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
|
||||
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
|
||||
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
|
||||
<value type="int" key="EditorConfiguration.TabSize">8</value>
|
||||
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
|
||||
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
|
||||
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
|
||||
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
|
||||
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
|
||||
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.PluginSettings</variable>
|
||||
<valuemap type="QVariantMap"/>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Target.0</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.DefaultCMakeTarget</value>
|
||||
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
|
||||
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
||||
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
|
||||
<value type="QString" key="CMakeProjectManager.CMakeBuildConfiguration.BuildDirectory">/home/spotscale/odm/OpenDroneMap-texturing_orthophoto_spotscale_additions/odm_orthophoto-build</value>
|
||||
<value type="QString" key="CMakeProjectManager.CMakeBuildConfiguration.ToolChain">ProjectExplorer.ToolChain.Gcc:/usr/bin/g++.x86-linux-generic-elf-32bit./usr/bin/gdb</value>
|
||||
<value type="QString" key="ProjectExplorer.BuildCOnfiguration.ToolChain">ProjectExplorer.ToolChain.Gcc:/usr/bin/g++.x86-linux-generic-elf-32bit./usr/bin/gdb</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<value type="QString" key="CMakeProjectManager.MakeStep.AdditionalArguments"></value>
|
||||
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets"/>
|
||||
<value type="bool" key="CMakeProjectManager.MakeStep.Clean">false</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<value type="QString" key="CMakeProjectManager.MakeStep.AdditionalArguments">clean</value>
|
||||
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets"/>
|
||||
<value type="bool" key="CMakeProjectManager.MakeStep.Clean">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">all</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">No deployment</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
|
||||
<value type="bool" key="Analyzer.Project.UseGlobal">true</value>
|
||||
<value type="bool" key="Analyzer.Project.UseGlobal">true</value>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
|
||||
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
|
||||
<value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
|
||||
<value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
|
||||
<value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
|
||||
<value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
|
||||
<value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
|
||||
<value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
|
||||
<value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
|
||||
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
|
||||
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
|
||||
<value type="int">0</value>
|
||||
<value type="int">1</value>
|
||||
<value type="int">2</value>
|
||||
<value type="int">3</value>
|
||||
<value type="int">4</value>
|
||||
<value type="int">5</value>
|
||||
<value type="int">6</value>
|
||||
<value type="int">7</value>
|
||||
<value type="int">8</value>
|
||||
<value type="int">9</value>
|
||||
<value type="int">10</value>
|
||||
<value type="int">11</value>
|
||||
<value type="int">12</value>
|
||||
<value type="int">13</value>
|
||||
<value type="int">14</value>
|
||||
</valuelist>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
|
||||
<value type="int">0</value>
|
||||
<value type="int">1</value>
|
||||
<value type="int">2</value>
|
||||
<value type="int">3</value>
|
||||
<value type="int">4</value>
|
||||
<value type="int">5</value>
|
||||
<value type="int">6</value>
|
||||
<value type="int">7</value>
|
||||
<value type="int">8</value>
|
||||
<value type="int">9</value>
|
||||
<value type="int">10</value>
|
||||
<value type="int">11</value>
|
||||
<value type="int">12</value>
|
||||
<value type="int">13</value>
|
||||
<value type="int">14</value>
|
||||
</valuelist>
|
||||
<value type="int" key="CMakeProjectManager.BaseEnvironmentBase">2</value>
|
||||
<value type="QString" key="CMakeProjectManager.CMakeRunConfiguation.Title">odm_orthophoto</value>
|
||||
<value type="QString" key="CMakeProjectManager.CMakeRunConfiguration.Arguments"></value>
|
||||
<value type="bool" key="CMakeProjectManager.CMakeRunConfiguration.UseTerminal">false</value>
|
||||
<valuelist type="QVariantList" key="CMakeProjectManager.CMakeRunConfiguration.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="CMakeProjectManager.CMakeRunConfiguration.UserWorkingDirectory"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">odm_orthophoto</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeRunConfiguration.</value>
|
||||
<value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebugger">true</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">false</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.TargetCount</variable>
|
||||
<value type="int">1</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Updater.EnvironmentId</variable>
|
||||
<value type="QString">{785a73be-b55f-490c-9d46-e1451c235840}</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
|
||||
<value type="int">10</value>
|
||||
</data>
|
||||
</qtcreator>
|
|
@ -266,7 +266,7 @@ void OdmOrthoPhoto::printHelp()
|
|||
log_ << "Call the program with flag \"-help\", or without parameters to print this message, or check any generated log file.\n";
|
||||
log_ << "Call the program with flag \"-verbose\", to print log messages in the standard output stream as well as in the log file.\n\n";
|
||||
|
||||
log_ << "Parameters are specified as: \"-<argument name> <argument>\", (without <>), and the following parameters are configureable:n";
|
||||
log_ << "Parameters are specified as: \"-<argument name> <argument>\", (without <>), and the following parameters are configureable:\n";
|
||||
log_ << "\"-inputFile <path>\" (mandatory)\n";
|
||||
log_ << "\"Input obj file that must contain a textured mesh.\n\n";
|
||||
|
||||
|
@ -319,20 +319,22 @@ void OdmOrthoPhoto::createOrthoPhoto()
|
|||
}
|
||||
|
||||
log_ << "Reading mesh file...\n";
|
||||
// The textureds mesh.
|
||||
// The textured mesh.
|
||||
pcl::TextureMesh mesh;
|
||||
pcl::io::loadOBJFile(inputFile_, mesh);
|
||||
loadObjFile(inputFile_, mesh);
|
||||
log_ << ".. mesh file read.\n\n";
|
||||
|
||||
// Does the model have more than one material?
|
||||
multiMaterial_ = 1 < mesh.tex_materials.size();
|
||||
|
||||
bool splitModel = false;
|
||||
|
||||
if(multiMaterial_)
|
||||
{
|
||||
// Need to check relationship between texture coordinates and faces.
|
||||
if(!isModelOk(mesh))
|
||||
{
|
||||
throw OdmOrthoPhotoException("Could not generate ortho photo: The given mesh has multiple textures, but the number of texture coordinates is NOT equal to 3 times the number of faces.");
|
||||
splitModel = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -357,7 +359,7 @@ void OdmOrthoPhoto::createOrthoPhoto()
|
|||
float yDiff = yMax - yMin;
|
||||
log_ << "Ortho photo area : " << xDiff*yDiff << "m2\n";
|
||||
|
||||
// The resolution neccesary to fit the area with the given resolution.
|
||||
// The resolution necessary to fit the area with the given resolution.
|
||||
int rowRes = static_cast<int>(std::ceil(resolution_*yDiff));
|
||||
int colRes = static_cast<int>(std::ceil(resolution_*xDiff));
|
||||
log_ << "Ortho photo resolution, width x height : " << colRes << "x" << rowRes << '\n';
|
||||
|
@ -386,6 +388,57 @@ void OdmOrthoPhoto::createOrthoPhoto()
|
|||
pcl::PointCloud<pcl::PointXYZ>::Ptr meshCloud (new pcl::PointCloud<pcl::PointXYZ>);
|
||||
pcl::fromPCLPointCloud2 (mesh.cloud, *meshCloud);
|
||||
|
||||
// Split model and make copies of vertices and texture coordinates for all faces
|
||||
if (splitModel)
|
||||
{
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr meshCloudSplit (new pcl::PointCloud<pcl::PointXYZ>);
|
||||
std::vector<Eigen::Vector2f> textureCoordinates = std::vector<Eigen::Vector2f>(0);
|
||||
|
||||
size_t vertexIndexCount = 0;
|
||||
for(size_t t = 0; t < mesh.tex_polygons.size(); ++t)
|
||||
{
|
||||
|
||||
for(size_t faceIndex = 0; faceIndex < mesh.tex_polygons[t].size(); ++faceIndex)
|
||||
{
|
||||
pcl::Vertices polygon = mesh.tex_polygons[t][faceIndex];
|
||||
|
||||
// The index to the vertices of the polygon.
|
||||
size_t v1i = polygon.vertices[0];
|
||||
size_t v2i = polygon.vertices[1];
|
||||
size_t v3i = polygon.vertices[2];
|
||||
|
||||
// The polygon's points.
|
||||
pcl::PointXYZ v1 = meshCloud->points[v1i];
|
||||
pcl::PointXYZ v2 = meshCloud->points[v2i];
|
||||
pcl::PointXYZ v3 = meshCloud->points[v3i];
|
||||
|
||||
Eigen::Vector2f vt1 = mesh.tex_coordinates[0][3*faceIndex];
|
||||
Eigen::Vector2f vt2 = mesh.tex_coordinates[0][3*faceIndex + 1];
|
||||
Eigen::Vector2f vt3 = mesh.tex_coordinates[0][3*faceIndex + 2];
|
||||
|
||||
meshCloudSplit->points.push_back(v1);
|
||||
textureCoordinates.push_back(vt1);
|
||||
mesh.tex_polygons[t][faceIndex].vertices[0] = vertexIndexCount;
|
||||
++vertexIndexCount;
|
||||
|
||||
meshCloudSplit->points.push_back(v2);
|
||||
textureCoordinates.push_back(vt2);
|
||||
mesh.tex_polygons[t][faceIndex].vertices[1] = vertexIndexCount;
|
||||
++vertexIndexCount;
|
||||
|
||||
meshCloudSplit->points.push_back(v3);
|
||||
textureCoordinates.push_back(vt3);
|
||||
mesh.tex_polygons[t][faceIndex].vertices[2] = vertexIndexCount;
|
||||
++vertexIndexCount;
|
||||
}
|
||||
}
|
||||
|
||||
mesh.tex_coordinates.clear();
|
||||
mesh.tex_coordinates.push_back(textureCoordinates);
|
||||
|
||||
meshCloud = meshCloudSplit;
|
||||
}
|
||||
|
||||
// Creates a transformation which aligns the area for the ortho photo.
|
||||
Eigen::Transform<float, 3, Eigen::Affine> transform = getROITransform(xMin, -yMax);
|
||||
|
||||
|
@ -395,12 +448,13 @@ void OdmOrthoPhoto::createOrthoPhoto()
|
|||
pcl::transformPointCloud(*meshCloud, *meshCloud, transform);
|
||||
log_ << ".. mesh translated and scaled.\n\n";
|
||||
|
||||
// Flatten texture coordiantes.
|
||||
// Flatten texture coordinates.
|
||||
std::vector<Eigen::Vector2f> uvs;
|
||||
for(size_t t = 0; t < mesh.tex_coordinates.size(); ++t)
|
||||
{
|
||||
uvs.insert(uvs.end(), mesh.tex_coordinates[t].begin(), mesh.tex_coordinates[t].end());
|
||||
}
|
||||
//cv::namedWindow("dsfs");
|
||||
|
||||
// The current material texture
|
||||
cv::Mat texture;
|
||||
|
@ -416,7 +470,7 @@ void OdmOrthoPhoto::createOrthoPhoto()
|
|||
// The material of the current submesh.
|
||||
pcl::TexMaterial material = mesh.tex_materials[t];
|
||||
texture = cv::imread(material.tex_file);
|
||||
|
||||
|
||||
// Check for missing files.
|
||||
if(texture.empty())
|
||||
{
|
||||
|
@ -591,7 +645,7 @@ void OdmOrthoPhoto::adjustBoundsForEntireModel(const pcl::TextureMesh &mesh)
|
|||
|
||||
Eigen::Transform<float, 3, Eigen::Affine> OdmOrthoPhoto::getROITransform(float xMin, float yMin) const
|
||||
{
|
||||
// The tranform used to move the chosen area into the ortho photo.
|
||||
// The transform used to move the chosen area into the ortho photo.
|
||||
Eigen::Transform<float, 3, Eigen::Affine> transform;
|
||||
|
||||
transform(0, 0) = resolution_; // x Scaling.
|
||||
|
@ -653,12 +707,13 @@ void OdmOrthoPhoto::drawTexturedTriangle(const cv::Mat &texture, const pcl::Vert
|
|||
v2x = v2.x; v2y = v2.y; v2z = v2.z;
|
||||
v3x = v3.x; v3y = v3.y; v3z = v3.z;
|
||||
|
||||
// Get texture coorinates. (Special cases for PCL when using multiple materials vs one material)
|
||||
// Get texture coordinates. (Special cases for PCL when using multiple materials vs one material)
|
||||
if(multiMaterial_)
|
||||
{
|
||||
v1u = uvs[3*faceIndex][0]; v1v = uvs[3*faceIndex][1];
|
||||
v2u = uvs[3*faceIndex+1][0]; v2v = uvs[3*faceIndex+1][1];
|
||||
v3u = uvs[3*faceIndex+2][0]; v3v = uvs[3*faceIndex+2][1];
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -671,22 +726,22 @@ void OdmOrthoPhoto::drawTexturedTriangle(const cv::Mat &texture, const pcl::Vert
|
|||
int xMin = static_cast<int>(std::min(std::min(v1x, v2x), v3x));
|
||||
if(xMin > photo_.cols)
|
||||
{
|
||||
return; // Completly outside to the right.
|
||||
return; // Completely outside to the right.
|
||||
}
|
||||
int xMax = static_cast<int>(std::max(std::max(v1x, v2x), v3x));
|
||||
if(xMax < 0)
|
||||
{
|
||||
return; // Completly outside to the left.
|
||||
return; // Completely outside to the left.
|
||||
}
|
||||
int yMin = static_cast<int>(std::min(std::min(v1y, v2y), v3y));
|
||||
if(yMin > photo_.rows)
|
||||
{
|
||||
return; // Completly outside to the top.
|
||||
return; // Completely outside to the top.
|
||||
}
|
||||
int yMax = static_cast<int>(std::max(std::max(v1y, v2y), v3y));
|
||||
if(yMax < 0)
|
||||
{
|
||||
return; // Completly outside to the bottom.
|
||||
return; // Completely outside to the bottom.
|
||||
}
|
||||
|
||||
// Top point row and column positions
|
||||
|
@ -785,7 +840,7 @@ void OdmOrthoPhoto::drawTexturedTriangle(const cv::Mat &texture, const pcl::Vert
|
|||
// The last pixel row for the top part of the triangle.
|
||||
int rqEnd = std::min(static_cast<int>(std::floor(midR+0.5f)), photo_.rows);
|
||||
|
||||
// Travers along row from top to middle.
|
||||
// Traverse along row from top to middle.
|
||||
for(int rq = rqStart; rq < rqEnd; ++rq)
|
||||
{
|
||||
// Set the current column positions.
|
||||
|
@ -800,8 +855,8 @@ void OdmOrthoPhoto::drawTexturedTriangle(const cv::Mat &texture, const pcl::Vert
|
|||
for(int cq = cqStart; cq < cqEnd; ++cq)
|
||||
{
|
||||
// Get barycentric coordinates for the current point.
|
||||
getBarycentricCoordiantes(v1, v2, v3, static_cast<float>(cq)+0.5f, static_cast<float>(rq)+0.5f, l1, l2, l3);
|
||||
|
||||
getBarycentricCoordinates(v1, v2, v3, static_cast<float>(cq)+0.5f, static_cast<float>(rq)+0.5f, l1, l2, l3);
|
||||
|
||||
if(0.f > l1 || 0.f > l2 || 0.f > l3)
|
||||
{
|
||||
//continue;
|
||||
|
@ -843,7 +898,7 @@ void OdmOrthoPhoto::drawTexturedTriangle(const cv::Mat &texture, const pcl::Vert
|
|||
// The last pixel row for the bottom part of the triangle.
|
||||
int rqEnd = std::min(static_cast<int>(std::floor(botR+0.5f)), photo_.rows);
|
||||
|
||||
// Travers along row from middle to bottom.
|
||||
// Traverse along row from middle to bottom.
|
||||
for(int rq = rqStart; rq < rqEnd; ++rq)
|
||||
{
|
||||
// Set the current column positions.
|
||||
|
@ -858,8 +913,8 @@ void OdmOrthoPhoto::drawTexturedTriangle(const cv::Mat &texture, const pcl::Vert
|
|||
for(int cq = cqStart; cq < cqEnd; ++cq)
|
||||
{
|
||||
// Get barycentric coordinates for the current point.
|
||||
getBarycentricCoordiantes(v1, v2, v3, static_cast<float>(cq)+0.5f, static_cast<float>(rq)+0.5f, l1, l2, l3);
|
||||
|
||||
getBarycentricCoordinates(v1, v2, v3, static_cast<float>(cq)+0.5f, static_cast<float>(rq)+0.5f, l1, l2, l3);
|
||||
|
||||
if(0.f > l1 || 0.f > l2 || 0.f > l3)
|
||||
{
|
||||
//continue;
|
||||
|
@ -941,7 +996,7 @@ void OdmOrthoPhoto::renderPixel(int row, int col, float s, float t, const cv::Ma
|
|||
photo_.at<cv::Vec4b>(row,col) = cv::Vec4b(static_cast<unsigned char>(b), static_cast<unsigned char>(g), static_cast<unsigned char>(r), 255);
|
||||
}
|
||||
|
||||
void OdmOrthoPhoto::getBarycentricCoordiantes(pcl::PointXYZ v1, pcl::PointXYZ v2, pcl::PointXYZ v3, float x, float y, float &l1, float &l2, float &l3) const
|
||||
void OdmOrthoPhoto::getBarycentricCoordinates(pcl::PointXYZ v1, pcl::PointXYZ v2, pcl::PointXYZ v3, float x, float y, float &l1, float &l2, float &l3) const
|
||||
{
|
||||
// Diff along y.
|
||||
float y2y3 = v2.y-v3.y;
|
||||
|
@ -994,3 +1049,365 @@ bool OdmOrthoPhoto::isModelOk(const pcl::TextureMesh &mesh)
|
|||
|
||||
return 3*nFaces == nTextureCoordinates;
|
||||
}
|
||||
|
||||
|
||||
bool OdmOrthoPhoto::loadObjFile(std::string inputFile, pcl::TextureMesh &mesh)
|
||||
{
|
||||
int data_type;
|
||||
unsigned int data_idx;
|
||||
int file_version;
|
||||
int offset = 0;
|
||||
Eigen::Vector4f origin;
|
||||
Eigen::Quaternionf orientation;
|
||||
|
||||
if (!readHeader(inputFile, mesh.cloud, origin, orientation, file_version, data_type, data_idx, offset))
|
||||
{
|
||||
throw OdmOrthoPhotoException("Problem reading header in modelfile!\n");
|
||||
}
|
||||
|
||||
std::ifstream fs;
|
||||
|
||||
fs.open (inputFile.c_str (), std::ios::binary);
|
||||
if (!fs.is_open () || fs.fail ())
|
||||
{
|
||||
//PCL_ERROR ("[pcl::OBJReader::readHeader] Could not open file '%s'! Error : %s\n", file_name.c_str (), strerror(errno));
|
||||
fs.close ();
|
||||
log_<<"Could not read mesh from file ";
|
||||
log_ << inputFile.c_str();
|
||||
log_ <<"\n";
|
||||
|
||||
throw OdmOrthoPhotoException("Problem reading mesh from file!\n");
|
||||
}
|
||||
|
||||
// Seek at the given offset
|
||||
fs.seekg (data_idx, std::ios::beg);
|
||||
|
||||
// Get normal_x field indices
|
||||
int normal_x_field = -1;
|
||||
for (std::size_t i = 0; i < mesh.cloud.fields.size (); ++i)
|
||||
{
|
||||
if (mesh.cloud.fields[i].name == "normal_x")
|
||||
{
|
||||
normal_x_field = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t v_idx = 0;
|
||||
std::size_t vn_idx = 0;
|
||||
std::size_t vt_idx = 0;
|
||||
std::size_t f_idx = 0;
|
||||
std::string line;
|
||||
std::vector<std::string> st;
|
||||
std::vector<Eigen::Vector2f> coordinates;
|
||||
std::vector<Eigen::Vector2f> allTexCoords;
|
||||
|
||||
std::map<int, int> f2vt;
|
||||
|
||||
try
|
||||
{
|
||||
while (!fs.eof ())
|
||||
{
|
||||
getline (fs, line);
|
||||
// Ignore empty lines
|
||||
if (line == "")
|
||||
continue;
|
||||
|
||||
// Tokenize the line
|
||||
std::stringstream sstream (line);
|
||||
sstream.imbue (std::locale::classic ());
|
||||
line = sstream.str ();
|
||||
boost::trim (line);
|
||||
boost::split (st, line, boost::is_any_of ("\t\r "), boost::token_compress_on);
|
||||
|
||||
// Ignore comments
|
||||
if (st[0] == "#")
|
||||
continue;
|
||||
// Vertex
|
||||
if (st[0] == "v")
|
||||
{
|
||||
try
|
||||
{
|
||||
for (int i = 1, f = 0; i < 4; ++i, ++f)
|
||||
{
|
||||
float value = boost::lexical_cast<float> (st[i]);
|
||||
memcpy (&mesh.cloud.data[v_idx * mesh.cloud.point_step + mesh.cloud.fields[f].offset], &value, sizeof (float));
|
||||
}
|
||||
|
||||
++v_idx;
|
||||
}
|
||||
catch (const boost::bad_lexical_cast &e)
|
||||
{
|
||||
log_<<"Unable to convert %s to vertex coordinates!\n";
|
||||
throw OdmOrthoPhotoException("Unable to convert %s to vertex coordinates!");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Vertex normal
|
||||
if (st[0] == "vn")
|
||||
{
|
||||
try
|
||||
{
|
||||
for (int i = 1, f = normal_x_field; i < 4; ++i, ++f)
|
||||
{
|
||||
float value = boost::lexical_cast<float> (st[i]);
|
||||
memcpy (&mesh.cloud.data[vn_idx * mesh.cloud.point_step + mesh.cloud.fields[f].offset],
|
||||
&value,
|
||||
sizeof (float));
|
||||
}
|
||||
++vn_idx;
|
||||
}
|
||||
catch (const boost::bad_lexical_cast &e)
|
||||
{
|
||||
log_<<"Unable to convert %s to vertex normal!\n";
|
||||
throw OdmOrthoPhotoException("Unable to convert %s to vertex normal!");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Texture coordinates
|
||||
if (st[0] == "vt")
|
||||
{
|
||||
try
|
||||
{
|
||||
Eigen::Vector3f c (0, 0, 0);
|
||||
for (std::size_t i = 1; i < st.size (); ++i)
|
||||
c[i-1] = boost::lexical_cast<float> (st[i]);
|
||||
|
||||
if (c[2] == 0)
|
||||
coordinates.push_back (Eigen::Vector2f (c[0], c[1]));
|
||||
else
|
||||
coordinates.push_back (Eigen::Vector2f (c[0]/c[2], c[1]/c[2]));
|
||||
++vt_idx;
|
||||
|
||||
}
|
||||
catch (const boost::bad_lexical_cast &e)
|
||||
{
|
||||
log_<<"Unable to convert %s to vertex texture coordinates!\n";
|
||||
throw OdmOrthoPhotoException("Unable to convert %s to vertex texture coordinates!");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Material
|
||||
if (st[0] == "usemtl")
|
||||
{
|
||||
mesh.tex_polygons.push_back (std::vector<pcl::Vertices> ());
|
||||
mesh.tex_materials.push_back (pcl::TexMaterial ());
|
||||
for (std::size_t i = 0; i < companions_.size (); ++i)
|
||||
{
|
||||
std::vector<pcl::TexMaterial>::const_iterator mat_it = companions_[i].getMaterial (st[1]);
|
||||
if (mat_it != companions_[i].materials_.end ())
|
||||
{
|
||||
mesh.tex_materials.back () = *mat_it;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// We didn't find the appropriate material so we create it here with name only.
|
||||
if (mesh.tex_materials.back ().tex_name == "")
|
||||
mesh.tex_materials.back ().tex_name = st[1];
|
||||
mesh.tex_coordinates.push_back (coordinates);
|
||||
coordinates.clear ();
|
||||
continue;
|
||||
}
|
||||
// Face
|
||||
if (st[0] == "f")
|
||||
{
|
||||
//We only care for vertices indices
|
||||
pcl::Vertices face_v; face_v.vertices.resize (st.size () - 1);
|
||||
for (std::size_t i = 1; i < st.size (); ++i)
|
||||
{
|
||||
int v;
|
||||
sscanf (st[i].c_str (), "%d", &v);
|
||||
v = (v < 0) ? v_idx + v : v - 1;
|
||||
face_v.vertices[i-1] = v;
|
||||
|
||||
int v2, vt, vn;
|
||||
sscanf (st[i].c_str (), "%d/%d/%d", &v2, &vt, &vn);
|
||||
f2vt[3*(f_idx) + i-1] = vt-1;
|
||||
}
|
||||
mesh.tex_polygons.back ().push_back (face_v);
|
||||
++f_idx;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const char *exception)
|
||||
{
|
||||
fs.close ();
|
||||
log_<<"Unable to read file!\n";
|
||||
throw OdmOrthoPhotoException("Unable to read file!");
|
||||
}
|
||||
|
||||
if (vt_idx != v_idx)
|
||||
{
|
||||
std::vector<Eigen::Vector2f> texcoordinates = std::vector<Eigen::Vector2f>(0);
|
||||
|
||||
for (size_t faceIndex = 0; faceIndex < f_idx; ++faceIndex)
|
||||
{
|
||||
for(size_t i = 0; i < 3; ++i)
|
||||
{
|
||||
Eigen::Vector2f vt = mesh.tex_coordinates[0][f2vt[3*faceIndex+i]];
|
||||
texcoordinates.push_back(vt);
|
||||
}
|
||||
}
|
||||
|
||||
mesh.tex_coordinates.clear();
|
||||
mesh.tex_coordinates.push_back(texcoordinates);
|
||||
}
|
||||
|
||||
fs.close();
|
||||
return (0);
|
||||
}
|
||||
|
||||
bool OdmOrthoPhoto::readHeader (const std::string &file_name, pcl::PCLPointCloud2 &cloud,
|
||||
Eigen::Vector4f &origin, Eigen::Quaternionf &orientation,
|
||||
int &file_version, int &data_type, unsigned int &data_idx,
|
||||
const int offset)
|
||||
{
|
||||
origin = Eigen::Vector4f::Zero ();
|
||||
orientation = Eigen::Quaternionf::Identity ();
|
||||
file_version = 0;
|
||||
cloud.width = cloud.height = cloud.point_step = cloud.row_step = 0;
|
||||
cloud.data.clear ();
|
||||
data_type = 0;
|
||||
data_idx = offset;
|
||||
|
||||
std::ifstream fs;
|
||||
std::string line;
|
||||
|
||||
if (file_name == "" || !boost::filesystem::exists (file_name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open file in binary mode to avoid problem of
|
||||
// std::getline() corrupting the result of ifstream::tellg()
|
||||
fs.open (file_name.c_str (), std::ios::binary);
|
||||
if (!fs.is_open () || fs.fail ())
|
||||
{
|
||||
fs.close ();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Seek at the given offset
|
||||
fs.seekg (offset, std::ios::beg);
|
||||
|
||||
// Read the header and fill it in with wonderful values
|
||||
bool vertex_normal_found = false;
|
||||
bool vertex_texture_found = false;
|
||||
// Material library, skip for now!
|
||||
// bool material_found = false;
|
||||
std::vector<std::string> material_files;
|
||||
std::size_t nr_point = 0;
|
||||
std::vector<std::string> st;
|
||||
|
||||
try
|
||||
{
|
||||
while (!fs.eof ())
|
||||
{
|
||||
getline (fs, line);
|
||||
// Ignore empty lines
|
||||
if (line == "")
|
||||
continue;
|
||||
|
||||
// Tokenize the line
|
||||
std::stringstream sstream (line);
|
||||
sstream.imbue (std::locale::classic ());
|
||||
line = sstream.str ();
|
||||
boost::trim (line);
|
||||
boost::split (st, line, boost::is_any_of ("\t\r "), boost::token_compress_on);
|
||||
// Ignore comments
|
||||
if (st.at (0) == "#")
|
||||
continue;
|
||||
|
||||
// Vertex
|
||||
if (st.at (0) == "v")
|
||||
{
|
||||
++nr_point;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Vertex texture
|
||||
if ((st.at (0) == "vt") && !vertex_texture_found)
|
||||
{
|
||||
vertex_texture_found = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Vertex normal
|
||||
if ((st.at (0) == "vn") && !vertex_normal_found)
|
||||
{
|
||||
vertex_normal_found = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Material library, skip for now!
|
||||
if (st.at (0) == "mtllib")
|
||||
{
|
||||
material_files.push_back (st.at (1));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const char *exception)
|
||||
{
|
||||
fs.close ();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!nr_point)
|
||||
{
|
||||
fs.close ();
|
||||
return false;
|
||||
}
|
||||
|
||||
int field_offset = 0;
|
||||
for (int i = 0; i < 3; ++i, field_offset += 4)
|
||||
{
|
||||
cloud.fields.push_back (pcl::PCLPointField ());
|
||||
cloud.fields[i].offset = field_offset;
|
||||
cloud.fields[i].datatype = pcl::PCLPointField::FLOAT32;
|
||||
cloud.fields[i].count = 1;
|
||||
}
|
||||
|
||||
cloud.fields[0].name = "x";
|
||||
cloud.fields[1].name = "y";
|
||||
cloud.fields[2].name = "z";
|
||||
|
||||
if (vertex_normal_found)
|
||||
{
|
||||
std::string normals_names[3] = { "normal_x", "normal_y", "normal_z" };
|
||||
for (int i = 0; i < 3; ++i, field_offset += 4)
|
||||
{
|
||||
cloud.fields.push_back (pcl::PCLPointField ());
|
||||
pcl::PCLPointField& last = cloud.fields.back ();
|
||||
last.name = normals_names[i];
|
||||
last.offset = field_offset;
|
||||
last.datatype = pcl::PCLPointField::FLOAT32;
|
||||
last.count = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (material_files.size () > 0)
|
||||
{
|
||||
for (std::size_t i = 0; i < material_files.size (); ++i)
|
||||
{
|
||||
pcl::MTLReader companion;
|
||||
|
||||
if (companion.read (file_name, material_files[i]))
|
||||
{
|
||||
log_<<"Problem reading material file.";
|
||||
}
|
||||
|
||||
companions_.push_back (companion);
|
||||
}
|
||||
}
|
||||
|
||||
cloud.point_step = field_offset;
|
||||
cloud.width = nr_point;
|
||||
cloud.height = 1;
|
||||
cloud.row_step = cloud.point_step * cloud.width;
|
||||
cloud.is_dense = true;
|
||||
cloud.data.resize (cloud.point_step * nr_point);
|
||||
fs.close ();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
#include "Logger.hpp"
|
||||
|
||||
/*!
|
||||
* \brief The WorldPoint struct encapsules world coordiantes used for the orhto photo boundary.
|
||||
* Points are separated into integersand fractional parts for high numerical stability.
|
||||
* \brief The WorldPoint struct encapsules world coordinates used for the ortho photo boundary.
|
||||
* Points are separated into integers and fractional parts for high numerical stability.
|
||||
*/
|
||||
struct WorldPoint
|
||||
{
|
||||
|
@ -54,7 +54,7 @@ struct WorldPoint
|
|||
};
|
||||
|
||||
/*!
|
||||
* \brief The OdmOrthoPhoto class is used to create an orthograpic photo over a given area.
|
||||
* \brief The OdmOrthoPhoto class is used to create an orthographic photo over a given area.
|
||||
* The class reads an oriented textured mesh from an OBJ-file.
|
||||
* The class uses file read from pcl.
|
||||
* The class uses image read and write from opencv.
|
||||
|
@ -125,7 +125,7 @@ private:
|
|||
* \param texture The texture of the polygon.
|
||||
* \param polygon The polygon as athree indices relative meshCloud.
|
||||
* \param meshCloud Contains all vertices.
|
||||
* \param uvs Contains the texture coordiantes for the active material.
|
||||
* \param uvs Contains the texture coordinates for the active material.
|
||||
* \param faceIndex The index of the face.
|
||||
*/
|
||||
void drawTexturedTriangle(const cv::Mat &texture, const pcl::Vertices &polygon, const pcl::PointCloud<pcl::PointXYZ>::Ptr &meshCloud, const std::vector<Eigen::Vector2f> &uvs, size_t faceIndex);
|
||||
|
@ -140,9 +140,9 @@ private:
|
|||
* \param texture The texture from which to get the color.
|
||||
**/
|
||||
void renderPixel(int row, int col, float u, float v, const cv::Mat &texture);
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Calcualtes the barycentric coordinates of a point in a triangle.
|
||||
* \brief Calculates the barycentric coordinates of a point in a triangle.
|
||||
*
|
||||
* \param v1 The first triangle vertex.
|
||||
* \param v2 The second triangle vertex.
|
||||
|
@ -153,8 +153,8 @@ private:
|
|||
* \param l2 The second vertex weight.
|
||||
* \param l3 The third vertex weight.
|
||||
*/
|
||||
void getBarycentricCoordiantes(pcl::PointXYZ v1, pcl::PointXYZ v2, pcl::PointXYZ v3, float x, float y, float &l1, float &l2, float &l3) const;
|
||||
|
||||
void getBarycentricCoordinates(pcl::PointXYZ v1, pcl::PointXYZ v2, pcl::PointXYZ v3, float x, float y, float &l1, float &l2, float &l3) const;
|
||||
|
||||
/*!
|
||||
* \brief Check if a given polygon is a sliver polygon.
|
||||
*
|
||||
|
@ -172,6 +172,23 @@ private:
|
|||
*/
|
||||
bool isModelOk(const pcl::TextureMesh &mesh);
|
||||
|
||||
/*!
|
||||
* \brief Loads a model from an .obj file (replacement for the pcl obj loader).
|
||||
*
|
||||
* \param inputFile Path to the .obj file.
|
||||
* \param mesh The model.
|
||||
* \return True if model was loaded successfully.
|
||||
*/
|
||||
bool loadObjFile(std::string inputFile, pcl::TextureMesh &mesh);
|
||||
|
||||
/*!
|
||||
* \brief Function is compied straight from the function in the pcl::io module.
|
||||
*/
|
||||
bool readHeader (const std::string &file_name, pcl::PCLPointCloud2 &cloud,
|
||||
Eigen::Vector4f &origin, Eigen::Quaternionf &orientation,
|
||||
int &file_version, int &data_type, unsigned int &data_idx,
|
||||
const int offset);
|
||||
|
||||
Logger log_; /**< Logging object. */
|
||||
|
||||
std::string inputFile_; /**< Path to the textured mesh as an obj-file. */
|
||||
|
@ -184,10 +201,10 @@ private:
|
|||
|
||||
bool boundaryDefined_; /**< True if the user has defined a boundary. */
|
||||
|
||||
WorldPoint worldPoint1_; /**< The first boundary point for the ortho photo, in world coordiantes. */
|
||||
WorldPoint worldPoint2_; /**< The second boundary point for the ortho photo, in world coordiantes. */
|
||||
WorldPoint worldPoint3_; /**< The third boundary point for the ortho photo, in world coordiantes. */
|
||||
WorldPoint worldPoint4_; /**< The fourth boundary point for the ortho photo, in world coordiantes. */
|
||||
WorldPoint worldPoint1_; /**< The first boundary point for the ortho photo, in world coordinates. */
|
||||
WorldPoint worldPoint2_; /**< The second boundary point for the ortho photo, in world coordinates. */
|
||||
WorldPoint worldPoint3_; /**< The third boundary point for the ortho photo, in world coordinates. */
|
||||
WorldPoint worldPoint4_; /**< The fourth boundary point for the ortho photo, in world coordinates. */
|
||||
|
||||
Eigen::Vector2f boundaryPoint1_; /**< The first boundary point for the ortho photo, in local coordinates. */
|
||||
Eigen::Vector2f boundaryPoint2_; /**< The second boundary point for the ortho photo, in local coordinates. */
|
||||
|
@ -198,6 +215,8 @@ private:
|
|||
cv::Mat depth_; /**< The depth of the ortho photo as an OpenCV matrix, CV_32F. */
|
||||
|
||||
bool multiMaterial_; /**< True if the mesh has multiple materials. **/
|
||||
|
||||
std::vector<pcl::MTLReader> companions_; /**< Materials (used by loadOBJFile). **/
|
||||
};
|
||||
|
||||
/*!
|
||||
|
|
|
@ -3,7 +3,7 @@ import context
|
|||
|
||||
# parse arguments
|
||||
processopts = ['resize', 'opensfm', 'slam', 'cmvs', 'pmvs',
|
||||
'odm_meshing', 'odm_texturing', 'odm_georeferencing',
|
||||
'odm_meshing', 'mvs_texturing', 'odm_georeferencing',
|
||||
'odm_orthophoto']
|
||||
|
||||
|
||||
|
@ -89,7 +89,7 @@ def config():
|
|||
type=float,
|
||||
help=('Ignore matched keypoints if the two images share '
|
||||
'less than <float> percent of keypoints. Default:'
|
||||
' $(default)s'))
|
||||
' %(default)s'))
|
||||
|
||||
parser.add_argument('--matcher-ratio',
|
||||
metavar='<float>',
|
||||
|
@ -118,6 +118,12 @@ def config():
|
|||
'images based on GPS exif data. Set to 0 to skip '
|
||||
'pre-matching. Default: %(default)s')
|
||||
|
||||
parser.add_argument('--use-opensfm-pointcloud',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='Use OpenSfM to compute the point cloud instead '
|
||||
'of PMVS')
|
||||
|
||||
parser.add_argument('--cmvs-maxImages',
|
||||
metavar='<integer>',
|
||||
default=500,
|
||||
|
@ -207,6 +213,46 @@ def config():
|
|||
'times slightly but helps reduce memory usage. '
|
||||
'Default: %(default)s'))
|
||||
|
||||
parser.add_argument('--mvs_texturing-dataTerm',
|
||||
metavar='<string>',
|
||||
default='gmi',
|
||||
help=('Data term: [area, gmi]. Default: %(default)s'))
|
||||
|
||||
parser.add_argument('--mvs_texturing-outlierRemovalType',
|
||||
metavar='<string>',
|
||||
default='none',
|
||||
help=('Type of photometric outlier removal method: '
|
||||
'[none, gauss_damping, gauss_clamping]. Default: '
|
||||
'%(default)s'))
|
||||
|
||||
parser.add_argument('--mvs_texturing-skipGeometricVisibilityTest',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=('Skip geometric visibility test. Default: %(default)s'))
|
||||
|
||||
parser.add_argument('--mvs_texturing-skipGlobalSeamLeveling',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=('Skip geometric visibility test. Default: %(default)s'))
|
||||
|
||||
parser.add_argument('--mvs_texturing-skipLocalSeamLeveling',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=('Skip local seam blending. Default: %(default)s'))
|
||||
|
||||
parser.add_argument('--mvs_texturing-skipHoleFilling',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=('Skip filling of holes in the mesh. Default: %(default)s'))
|
||||
|
||||
parser.add_argument('--mvs_texturing-keepUnseenFaces',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=('Keep faces in the mesh that are not seen in any camera. '
|
||||
'Default: %(default)s'))
|
||||
|
||||
# Old odm_texturing arguments
|
||||
|
||||
parser.add_argument('--odm_texturing-textureResolution',
|
||||
metavar='<positive integer>',
|
||||
default=4096,
|
||||
|
@ -221,6 +267,8 @@ def config():
|
|||
help=('The resolution to rescale the images performing '
|
||||
'the texturing. Default: %(default)s'))
|
||||
|
||||
# End of old odm_texturing arguments
|
||||
|
||||
parser.add_argument('--odm_georeferencing-gcpFile',
|
||||
metavar='<path string>',
|
||||
default='gcp_list.txt',
|
||||
|
|
|
@ -26,6 +26,9 @@ cmvs_path = os.path.join(superbuild_path, "install/bin/cmvs")
|
|||
cmvs_opts_path = os.path.join(superbuild_path, "install/bin/genOption")
|
||||
pmvs2_path = os.path.join(superbuild_path, "install/bin/pmvs2")
|
||||
|
||||
# define mvstex path
|
||||
mvstex_path = os.path.join(superbuild_path, "install/bin/texrecon")
|
||||
|
||||
# define txt2las path
|
||||
txt2las_path = os.path.join(superbuild_path, 'src/las-tools/bin')
|
||||
pdal_path = os.path.join(superbuild_path, 'build/pdal/bin')
|
||||
|
|
|
@ -13,7 +13,8 @@ tasks_dict = {'1': 'resize',
|
|||
'3': 'cmvs',
|
||||
'4': 'pmvs',
|
||||
'5': 'odm_meshing',
|
||||
'6': 'odm_texturing',
|
||||
# '6': 'odm_texturing',
|
||||
'6': 'mvs_texturing',
|
||||
'7': 'odm_georeferencing',
|
||||
'8': 'odm_orthophoto',
|
||||
'9': 'zip_results'}
|
||||
|
@ -68,7 +69,7 @@ class ODMTaskManager(object):
|
|||
command = None
|
||||
inputs = {}
|
||||
|
||||
elif task_name == 'odm_texturing':
|
||||
elif task_name == 'mvs_texturing':
|
||||
# setup this task
|
||||
command = None
|
||||
inputs = {}
|
||||
|
|
|
@ -25,9 +25,9 @@ class ODM_Photo:
|
|||
self.focal_length = None
|
||||
self.focal_length_px = None
|
||||
# other attributes
|
||||
self.camera_make = None
|
||||
self.camera_model = None
|
||||
self.make_model = None
|
||||
self.camera_make = ''
|
||||
self.camera_model = ''
|
||||
self.make_model = ''
|
||||
# parse values from metadata
|
||||
self.parse_pyexiv2_values(self.path_file, force_focal, force_ccd)
|
||||
# compute focal length into pixels
|
||||
|
@ -69,8 +69,11 @@ class ODM_Photo:
|
|||
self.focal_length = float(val)
|
||||
except (pyexiv2.ExifValueError, ValueError) as e:
|
||||
pass
|
||||
except NotImplementedError as e:
|
||||
pass
|
||||
|
||||
self.make_model = sensor_string(self.camera_make, self.camera_model)
|
||||
if self.camera_make and self.camera_model:
|
||||
self.make_model = sensor_string(self.camera_make, self.camera_model)
|
||||
|
||||
# needed to do that since sometimes metadata contains wrong data
|
||||
img = cv2.imread(_path_file)
|
||||
|
@ -357,6 +360,7 @@ class ODM_Tree(object):
|
|||
self.opensfm_bundle_list = io.join_paths(self.opensfm, 'list_r000.out')
|
||||
self.opensfm_image_list = io.join_paths(self.opensfm, 'image_list.txt')
|
||||
self.opensfm_reconstruction = io.join_paths(self.opensfm, 'reconstruction.json')
|
||||
self.opensfm_model = io.join_paths(self.opensfm, 'depthmaps/merged.ply')
|
||||
|
||||
# pmvs
|
||||
self.pmvs_rec_path = io.join_paths(self.pmvs, 'recon0')
|
||||
|
@ -369,13 +373,14 @@ class ODM_Tree(object):
|
|||
self.odm_mesh = io.join_paths(self.odm_meshing, 'odm_mesh.ply')
|
||||
self.odm_meshing_log = io.join_paths(self.odm_meshing, 'odm_meshing_log.txt')
|
||||
|
||||
# odm_texturing
|
||||
# texturing
|
||||
self.odm_texturing_undistorted_image_path = io.join_paths(
|
||||
self.odm_texturing, 'undistorted')
|
||||
self.odm_textured_model_obj = io.join_paths(
|
||||
self.odm_texturing, 'odm_textured_model.obj')
|
||||
self.odm_textured_model_mtl = io.join_paths(
|
||||
self.odm_texturing, 'odm_textured_model.mtl')
|
||||
# Log is only used by old odm_texturing
|
||||
self.odm_texuring_log = io.join_paths(
|
||||
self.odm_texturing, 'odm_texturing_log.txt')
|
||||
|
||||
|
@ -406,6 +411,6 @@ class ODM_Tree(object):
|
|||
# odm_orthophoto
|
||||
self.odm_orthophoto_file = io.join_paths(self.odm_orthophoto, 'odm_orthophoto.png')
|
||||
self.odm_orthophoto_tif = io.join_paths(self.odm_orthophoto, 'odm_orthophoto.tif')
|
||||
self.odm_orthophoto_corners = io.join_paths(self.odm_orthophoto, 'odm_orthphoto_corners.txt')
|
||||
self.odm_orthophoto_corners = io.join_paths(self.odm_orthophoto, 'odm_orthophoto_corners.txt')
|
||||
self.odm_orthophoto_log = io.join_paths(self.odm_orthophoto, 'odm_orthophoto_log.txt')
|
||||
self.odm_orthophoto_tif_log = io.join_paths(self.odm_orthophoto, 'gdal_translate_log.txt')
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
FROM ubuntu:14.04
|
||||
MAINTAINER Alex Hagiopol <alex.hagiopol@icloud.com>
|
||||
|
||||
# Env variables
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
|
||||
#Install dependencies
|
||||
#Required Requisites
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y -qq \
|
||||
build-essential \
|
||||
cmake \
|
||||
git \
|
||||
python-pip \
|
||||
libgdal-dev \
|
||||
gdal-bin \
|
||||
libgeotiff-dev \
|
||||
pkg-config
|
||||
|
||||
#CMake 3.1 for MVS-Texturing
|
||||
RUN apt-get install -y software-properties-common python-software-properties
|
||||
RUN add-apt-repository -y ppa:george-edison55/cmake-3.x
|
||||
RUN apt-get update -y
|
||||
RUN apt-get install -y --only-upgrade cmake
|
||||
|
||||
#Installing OpenCV Dependencies
|
||||
RUN apt-get install -y -qq libgtk2.0-dev \
|
||||
libavcodec-dev \
|
||||
libavformat-dev \
|
||||
libswscale-dev \
|
||||
python-dev \
|
||||
python-numpy \
|
||||
libtbb2 \
|
||||
libtbb-dev \
|
||||
libjpeg-dev \
|
||||
libpng-dev \
|
||||
libtiff-dev \
|
||||
libjasper-dev \
|
||||
libflann-dev \
|
||||
libproj-dev \
|
||||
libxext-dev \
|
||||
liblapack-dev \
|
||||
libeigen3-dev \
|
||||
libvtk5-dev
|
||||
|
||||
#Removing libdc1394-22-dev due to python opencv issue
|
||||
RUN apt-get remove libdc1394-22-dev
|
||||
|
||||
#Installing OpenSfM Dependencies
|
||||
RUN apt-get install -y -qq python-networkx \
|
||||
libgoogle-glog-dev \
|
||||
libsuitesparse-dev \
|
||||
libboost-filesystem-dev \
|
||||
libboost-iostreams-dev \
|
||||
libboost-regex-dev \
|
||||
libboost-python-dev \
|
||||
libboost-date-time-dev \
|
||||
libboost-thread-dev \
|
||||
python-pyproj
|
||||
RUN pip install -U PyYAML \
|
||||
exifread \
|
||||
gpxpy \
|
||||
xmltodict \
|
||||
catkin-pkg
|
||||
|
||||
#Installing Ecto Dependencies
|
||||
RUN apt-get install -y -qq python-empy \
|
||||
python-nose \
|
||||
python-pyside
|
||||
|
||||
#"Installing OpenDroneMap Dependencies"
|
||||
RUN apt-get install -y python-pyexiv2 \
|
||||
python-scipy \
|
||||
jhead \
|
||||
liblas-bin -y -qq
|
||||
|
||||
RUN apt-get install -y python-empy \
|
||||
python-nose \
|
||||
python-pyside \
|
||||
python-pyexiv2 \
|
||||
python-scipy \
|
||||
jhead \
|
||||
liblas-bin \
|
||||
python-matplotlib \
|
||||
libatlas-base-dev \
|
||||
libatlas3gf-base
|
||||
|
||||
ENV PYTHONPATH="$PYTHONPATH:/code/SuperBuild/install/lib/python2.7/dist-packages"
|
||||
ENV PYTHONPATH="$PYTHONPATH:/code/SuperBuild/src/opensfm"
|
||||
ENV LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/code/SuperBuild/install/lib"
|
||||
|
12
run.py
12
run.py
|
@ -6,6 +6,7 @@ from opendm import system
|
|||
|
||||
import sys
|
||||
import ecto
|
||||
import os
|
||||
|
||||
from scripts.odm_app import ODMApp
|
||||
|
||||
|
@ -26,6 +27,17 @@ if __name__ == '__main__':
|
|||
if args.project_path is None:
|
||||
usage()
|
||||
|
||||
#If user asks to rerun everything, delete all of the existing progress directories.
|
||||
if args.rerun_all:
|
||||
os.system("rm -rf "
|
||||
+ args.project_path + "images_resize/ "
|
||||
+ args.project_path + "odm_georeferencing/ "
|
||||
+ args.project_path + "odm_meshing/ "
|
||||
+ args.project_path + "odm_orthophoto/ "
|
||||
+ args.project_path + "odm_texturing/ "
|
||||
+ args.project_path + "opensfm/ "
|
||||
+ args.project_path + "pmvs/")
|
||||
|
||||
# create an instance of my App BlackBox
|
||||
# internally configure all tasks
|
||||
app = ODMApp(args=args)
|
||||
|
|
|
@ -1,23 +1,31 @@
|
|||
import os
|
||||
import ecto
|
||||
|
||||
from functools import partial
|
||||
from multiprocessing import Pool
|
||||
from opendm import context
|
||||
from opendm import io
|
||||
from opendm import types
|
||||
from opendm import log
|
||||
|
||||
|
||||
def make_odm_photo(force_focal, force_ccd, path_file):
|
||||
return types.ODM_Photo(path_file,
|
||||
force_focal,
|
||||
force_ccd)
|
||||
|
||||
|
||||
class ODMLoadDatasetCell(ecto.Cell):
|
||||
|
||||
def declare_params(self, params):
|
||||
params.declare("force_focal", 'Override the focal length information for the '
|
||||
'images', None)
|
||||
params.declare("force_ccd", 'Override the ccd widht information for the '
|
||||
params.declare("force_ccd", 'Override the ccd width information for the '
|
||||
'images', None)
|
||||
|
||||
def declare_io(self, params, inputs, outputs):
|
||||
inputs.declare("tree", "Struct with paths", [])
|
||||
outputs.declare("photos", "list of ODMPhoto's", [])
|
||||
outputs.declare("photos", "list of ODMPhotos", [])
|
||||
|
||||
def process(self, inputs, outputs):
|
||||
# check if the extension is sopported
|
||||
|
@ -49,13 +57,11 @@ class ODMLoadDatasetCell(ecto.Cell):
|
|||
|
||||
if files:
|
||||
# create ODMPhoto list
|
||||
photos = []
|
||||
for f in files:
|
||||
path_file = io.join_paths(images_dir, f)
|
||||
photo = types.ODM_Photo(path_file,
|
||||
self.params.force_focal,
|
||||
self.params.force_ccd)
|
||||
photos.append(photo)
|
||||
path_files = [io.join_paths(images_dir, f) for f in files]
|
||||
photos = Pool().map(
|
||||
partial(make_odm_photo, self.params.force_focal, self.params.force_ccd),
|
||||
path_files
|
||||
)
|
||||
|
||||
log.ODM_INFO('Found %s usable images' % len(photos))
|
||||
else:
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
import ecto
|
||||
|
||||
from opendm import log
|
||||
from opendm import io
|
||||
from opendm import system
|
||||
from opendm import context
|
||||
|
||||
import pmvs2nvmcams
|
||||
|
||||
class ODMMvsTexCell(ecto.Cell):
|
||||
def declare_params(self, params):
|
||||
params.declare("data_term", 'Data term: [area, gmi] default: gmi', "gmi")
|
||||
params.declare("outlier_rem_type", 'Type of photometric outlier removal method: [none, gauss_damping, gauss_clamping]. default: none', "none")
|
||||
params.declare("skip_vis_test", 'Skip geometric visibility test based on ray intersection.', False)
|
||||
params.declare("skip_glob_seam_leveling", 'Skip global seam leveling.', False)
|
||||
params.declare("skip_loc_seam_leveling", 'Skip local seam leveling (Poisson editing).', False)
|
||||
params.declare("skip_hole_fill", 'Skip hole filling.', False)
|
||||
params.declare("keep_unseen_faces", 'Keep unseen faces.', False)
|
||||
|
||||
def declare_io(self, params, inputs, outputs):
|
||||
inputs.declare("tree", "Struct with paths", [])
|
||||
inputs.declare("args", "The application arguments.", {})
|
||||
inputs.declare("reconstruction", "Clusters output. list of ODMReconstructions", [])
|
||||
outputs.declare("reconstruction", "Clusters output. list of ODMReconstructions", [])
|
||||
|
||||
|
||||
|
||||
def process(self, inputs, outputs):
|
||||
|
||||
# Benchmarking
|
||||
start_time = system.now_raw()
|
||||
|
||||
log.ODM_INFO('Running MVS Texturing Cell')
|
||||
|
||||
# get inputs
|
||||
args = self.inputs.args
|
||||
tree = self.inputs.tree
|
||||
|
||||
# define paths and create working directories
|
||||
system.mkdir_p(tree.odm_texturing)
|
||||
|
||||
# check if we rerun cell or not
|
||||
rerun_cell = (args.rerun is not None and
|
||||
args.rerun == 'mvs_texturing') or \
|
||||
(args.rerun_all) or \
|
||||
(args.rerun_from is not None and
|
||||
'mvs_texturing' in args.rerun_from)
|
||||
|
||||
if not io.file_exists(tree.odm_textured_model_obj) or rerun_cell:
|
||||
log.ODM_DEBUG('Writing MVS Textured file in: %s'
|
||||
% tree.odm_textured_model_obj)
|
||||
|
||||
|
||||
# Format arguments to fit Mvs-Texturing app
|
||||
skipGeometricVisibilityTest = ""
|
||||
skipGlobalSeamLeveling = ""
|
||||
skipLocalSeamLeveling = ""
|
||||
skipHoleFilling = ""
|
||||
keepUnseenFaces = ""
|
||||
|
||||
if (self.params.skip_vis_test):
|
||||
skipGeometricVisibilityTest = "--skip_geometric_visibility_test"
|
||||
if (self.params.skip_glob_seam_leveling):
|
||||
skipGlobalSeamLeveling = "--skip_global_seam_leveling"
|
||||
if (self.params.skip_loc_seam_leveling):
|
||||
skipLocalSeamLeveling = "--skip_local_seam_leveling"
|
||||
if (self.params.skip_hole_fill):
|
||||
skipHoleFilling = "--skip_hole_filling"
|
||||
if (self.params.keep_unseen_faces):
|
||||
keepUnseenFaces = "--keep_unseen_faces"
|
||||
|
||||
# mvstex definitions
|
||||
kwargs = {
|
||||
'bin': context.mvstex_path,
|
||||
'out_dir': io.join_paths(tree.odm_texturing, "odm_textured_model"),
|
||||
'pmvs_folder': tree.pmvs_rec_path,
|
||||
'nvm_file': io.join_paths(tree.pmvs_rec_path, "nvmCams.nvm"),
|
||||
'model': tree.odm_mesh,
|
||||
'dataTerm': self.params.data_term,
|
||||
'outlierRemovalType': self.params.outlier_rem_type,
|
||||
'skipGeometricVisibilityTest': skipGeometricVisibilityTest,
|
||||
'skipGlobalSeamLeveling': skipGlobalSeamLeveling,
|
||||
'skipLocalSeamLeveling': skipLocalSeamLeveling,
|
||||
'skipHoleFilling': skipHoleFilling,
|
||||
'keepUnseenFaces': keepUnseenFaces
|
||||
}
|
||||
|
||||
if args.use_opensfm_pointcloud:
|
||||
kwargs['nvm_file'] = io.join_paths(tree.opensfm,
|
||||
"reconstruction.nvm")
|
||||
else:
|
||||
log.ODM_DEBUG('Generating .nvm file from pmvs output: %s'
|
||||
% '{nvm_file}'.format(**kwargs))
|
||||
|
||||
# Create .nvm camera file.
|
||||
pmvs2nvmcams.run('{pmvs_folder}'.format(**kwargs),
|
||||
'{nvm_file}'.format(**kwargs))
|
||||
|
||||
# run texturing binary
|
||||
system.run('{bin} {nvm_file} {model} {out_dir} '
|
||||
'-d {dataTerm} -o {outlierRemovalType} '
|
||||
'{skipGeometricVisibilityTest} '
|
||||
'{skipGlobalSeamLeveling} '
|
||||
'{skipLocalSeamLeveling} '
|
||||
'{skipHoleFilling} '
|
||||
'{keepUnseenFaces}'.format(**kwargs))
|
||||
else:
|
||||
log.ODM_WARNING('Found a valid ODM Texture file in: %s'
|
||||
% tree.odm_textured_model_obj)
|
||||
|
||||
if args.time:
|
||||
system.benchmark(start_time, tree.benchmarking, 'Texturing')
|
||||
|
||||
log.ODM_INFO('Running ODM Texturing Cell - Finished')
|
||||
return ecto.OK if args.end_with != 'odm_texturing' else ecto.QUIT
|
|
@ -14,7 +14,8 @@ from odm_slam import ODMSlamCell
|
|||
from pmvs import ODMPmvsCell
|
||||
from cmvs import ODMCmvsCell
|
||||
from odm_meshing import ODMeshingCell
|
||||
from odm_texturing import ODMTexturingCell
|
||||
#from odm_texturing import ODMTexturingCell
|
||||
from mvstex import ODMMvsTexCell
|
||||
from odm_georeferencing import ODMGeoreferencingCell
|
||||
from odm_orthophoto import ODMOrthoPhotoCell
|
||||
|
||||
|
@ -59,9 +60,16 @@ class ODMApp(ecto.BlackBox):
|
|||
oct_tree=p.args.odm_meshing_octreeDepth,
|
||||
samples=p.args.odm_meshing_samplesPerNode,
|
||||
solver=p.args.odm_meshing_solverDivide),
|
||||
'texturing': ODMTexturingCell(resize=p.args.resize_to,
|
||||
resolution=p.args.odm_texturing_textureResolution,
|
||||
size=p.args.odm_texturing_textureWithSize),
|
||||
'texturing': ODMMvsTexCell(data_term=p.args.mvs_texturing_dataTerm,
|
||||
outlier_rem_type=p.args.mvs_texturing_outlierRemovalType,
|
||||
skip_vis_test=p.args.mvs_texturing_skipGeometricVisibilityTest,
|
||||
skip_glob_seam_leveling=p.args.mvs_texturing_skipGlobalSeamLeveling,
|
||||
skip_loc_seam_leveling=p.args.mvs_texturing_skipLocalSeamLeveling,
|
||||
skip_hole_fill=p.args.mvs_texturing_skipHoleFilling,
|
||||
keep_unseen_faces=p.args.mvs_texturing_keepUnseenFaces),
|
||||
# Old odm_texturing
|
||||
# 'texturing': ODMTexturingCell(resize=p.args['resize_to'],
|
||||
# resolution=p.args['odm_texturing_textureResolution'],
|
||||
'georeferencing': ODMGeoreferencingCell(img_size=p.args.resize_to,
|
||||
gcp_file=p.args.odm_georeferencing_gcpFile,
|
||||
use_gcp=p.args.odm_georeferencing_useGcp),
|
||||
|
@ -83,7 +91,8 @@ class ODMApp(ecto.BlackBox):
|
|||
b.write('ODM Benchmarking file created %s\nNumber of Cores: %s\n\n' % (system.now(), context.num_cores))
|
||||
|
||||
def connections(self, _p):
|
||||
run_slam = _p.args.video
|
||||
if _p.args.video:
|
||||
return self.slam_connections(_p)
|
||||
|
||||
# define initial task
|
||||
# TODO: What is this?
|
||||
|
@ -91,36 +100,72 @@ class ODMApp(ecto.BlackBox):
|
|||
# initial_task_id = config.processopts.index(initial_task)
|
||||
|
||||
# define the connections like you would for the plasm
|
||||
connections = []
|
||||
|
||||
if run_slam:
|
||||
# run slam cell
|
||||
connections += [self.tree[:] >> self.slam['tree'],
|
||||
self.args[:] >> self.slam['args']]
|
||||
# load the dataset
|
||||
connections = [self.tree[:] >> self.dataset['tree']]
|
||||
|
||||
# run cmvs
|
||||
connections += [self.tree[:] >> self.cmvs['tree'],
|
||||
self.args[:] >> self.cmvs['args'],
|
||||
self.slam['reconstruction'] >> self.cmvs['reconstruction']]
|
||||
# run resize cell
|
||||
connections += [self.tree[:] >> self.resize['tree'],
|
||||
self.args[:] >> self.resize['args'],
|
||||
self.dataset['photos'] >> self.resize['photos']]
|
||||
|
||||
# run opensfm with images from load dataset
|
||||
connections += [self.tree[:] >> self.opensfm['tree'],
|
||||
self.args[:] >> self.opensfm['args'],
|
||||
self.resize['photos'] >> self.opensfm['photos']]
|
||||
|
||||
if _p.args.use_opensfm_pointcloud:
|
||||
# create odm mesh from opensfm point cloud
|
||||
connections += [self.tree[:] >> self.meshing['tree'],
|
||||
self.args[:] >> self.meshing['args'],
|
||||
self.opensfm['reconstruction'] >> self.meshing['reconstruction']]
|
||||
else:
|
||||
# load the dataset
|
||||
connections += [self.tree[:] >> self.dataset['tree']]
|
||||
|
||||
# run resize cell
|
||||
connections += [self.tree[:] >> self.resize['tree'],
|
||||
self.args[:] >> self.resize['args'],
|
||||
self.dataset['photos'] >> self.resize['photos']]
|
||||
|
||||
# run opensfm with images from load dataset
|
||||
connections += [self.tree[:] >> self.opensfm['tree'],
|
||||
self.args[:] >> self.opensfm['args'],
|
||||
self.resize['photos'] >> self.opensfm['photos']]
|
||||
|
||||
# run cmvs
|
||||
connections += [self.tree[:] >> self.cmvs['tree'],
|
||||
self.args[:] >> self.cmvs['args'],
|
||||
self.opensfm['reconstruction'] >> self.cmvs['reconstruction']]
|
||||
|
||||
# run pmvs
|
||||
connections += [self.tree[:] >> self.pmvs['tree'],
|
||||
self.args[:] >> self.pmvs['args'],
|
||||
self.cmvs['reconstruction'] >> self.pmvs['reconstruction']]
|
||||
|
||||
# create odm mesh from pmvs point cloud
|
||||
connections += [self.tree[:] >> self.meshing['tree'],
|
||||
self.args[:] >> self.meshing['args'],
|
||||
self.pmvs['reconstruction'] >> self.meshing['reconstruction']]
|
||||
|
||||
# create odm texture
|
||||
connections += [self.tree[:] >> self.texturing['tree'],
|
||||
self.args[:] >> self.texturing['args'],
|
||||
self.meshing['reconstruction'] >> self.texturing['reconstruction']]
|
||||
|
||||
# create odm georeference
|
||||
connections += [self.tree[:] >> self.georeferencing['tree'],
|
||||
self.args[:] >> self.georeferencing['args'],
|
||||
self.dataset['photos'] >> self.georeferencing['photos'],
|
||||
self.texturing['reconstruction'] >> self.georeferencing['reconstruction']]
|
||||
|
||||
# create odm orthophoto
|
||||
connections += [self.tree[:] >> self.orthophoto['tree'],
|
||||
self.args[:] >> self.orthophoto['args'],
|
||||
self.georeferencing['reconstruction'] >> self.orthophoto['reconstruction']]
|
||||
|
||||
return connections
|
||||
|
||||
def slam_connections(self, _p):
|
||||
"""Get connections used when running from video instead of images."""
|
||||
connections = []
|
||||
|
||||
# run slam cell
|
||||
connections += [self.tree[:] >> self.slam['tree'],
|
||||
self.args[:] >> self.slam['args']]
|
||||
|
||||
# run cmvs
|
||||
connections += [self.tree[:] >> self.cmvs['tree'],
|
||||
self.args[:] >> self.cmvs['args'],
|
||||
self.slam['reconstruction'] >> self.cmvs['reconstruction']]
|
||||
|
||||
# run pmvs
|
||||
connections += [self.tree[:] >> self.pmvs['tree'],
|
||||
self.args[:] >> self.pmvs['args'],
|
||||
|
@ -136,16 +181,4 @@ class ODMApp(ecto.BlackBox):
|
|||
self.args[:] >> self.texturing['args'],
|
||||
self.meshing['reconstruction'] >> self.texturing['reconstruction']]
|
||||
|
||||
if not run_slam:
|
||||
# create odm georeference
|
||||
connections += [self.tree[:] >> self.georeferencing['tree'],
|
||||
self.args[:] >> self.georeferencing['args'],
|
||||
self.dataset['photos'] >> self.georeferencing['photos'],
|
||||
self.texturing['reconstruction'] >> self.georeferencing['reconstruction']]
|
||||
|
||||
# create odm orthophoto
|
||||
connections += [self.tree[:] >> self.orthophoto['tree'],
|
||||
self.args[:] >> self.orthophoto['args'],
|
||||
self.georeferencing['reconstruction'] >> self.orthophoto['reconstruction']]
|
||||
|
||||
return connections
|
||||
|
|
|
@ -50,7 +50,7 @@ class ODMGeoreferencingCell(ecto.Cell):
|
|||
# odm_georeference definitions
|
||||
kwargs = {
|
||||
'bin': context.odm_modules_path,
|
||||
'imgs': tree.dataset_raw,
|
||||
'imgs': tree.dataset_resize,
|
||||
'imgs_list': tree.opensfm_bundle_list,
|
||||
'coords': tree.odm_georeferencing_coords,
|
||||
'log': tree.odm_georeferencing_utm_log
|
||||
|
@ -82,10 +82,9 @@ class ODMGeoreferencingCell(ecto.Cell):
|
|||
kwargs = {
|
||||
'bin': context.odm_modules_path,
|
||||
'bundle': tree.opensfm_bundle,
|
||||
'imgs': tree.dataset_raw,
|
||||
'imgs': tree.dataset_resize,
|
||||
'imgs_list': tree.opensfm_bundle_list,
|
||||
'model': tree.odm_textured_model_obj,
|
||||
'pc': tree.pmvs_model,
|
||||
'log': tree.odm_georeferencing_log,
|
||||
'coords': tree.odm_georeferencing_coords,
|
||||
'pc_geo': tree.odm_georeferencing_model_ply_geo,
|
||||
|
@ -95,6 +94,10 @@ class ODMGeoreferencingCell(ecto.Cell):
|
|||
'gcp': gcpfile,
|
||||
|
||||
}
|
||||
if args.use_opensfm_pointcloud:
|
||||
kwargs['pc'] = tree.opensfm_model
|
||||
else:
|
||||
kwargs['pc'] = tree.pmvs_model
|
||||
|
||||
if self.params.use_gcp and \
|
||||
io.file_exists(gcpfile):
|
||||
|
|
|
@ -52,7 +52,6 @@ class ODMeshingCell(ecto.Cell):
|
|||
|
||||
kwargs = {
|
||||
'bin': context.odm_modules_path,
|
||||
'infile': tree.pmvs_model,
|
||||
'outfile': tree.odm_mesh,
|
||||
'log': tree.odm_meshing_log,
|
||||
'max_vertex': self.params.max_vertex,
|
||||
|
@ -60,6 +59,10 @@ class ODMeshingCell(ecto.Cell):
|
|||
'samples': self.params.samples,
|
||||
'solver': self.params.solver
|
||||
}
|
||||
if args.use_opensfm_pointcloud:
|
||||
kwargs['infile'] = tree.opensfm_model
|
||||
else:
|
||||
kwargs['infile'] = tree.pmvs_model
|
||||
|
||||
# run meshing binary
|
||||
system.run('{bin}/odm_meshing -inputFile {infile} '
|
||||
|
|
|
@ -89,6 +89,12 @@ class ODMOrthoPhotoCell(ecto.Cell):
|
|||
}
|
||||
|
||||
system.run('gdal_translate -a_ullr {ulx} {uly} {lrx} {lry} '
|
||||
'-co TILED=yes '
|
||||
'-co COMPRESS=DEFLATE '
|
||||
'-co PREDICTOR=2 '
|
||||
'-co BLOCKXSIZE=512 '
|
||||
'-co BLOCKYSIZE=512 '
|
||||
'-co NUM_THREADS=ALL_CPUS '
|
||||
'-a_srs \"EPSG:{epsg}\" {png} {tiff} > {log}'.format(**kwargs))
|
||||
geotiffcreated = True
|
||||
if not geotiffcreated:
|
||||
|
|
|
@ -48,9 +48,13 @@ class ODMOpenSfMCell(ecto.Cell):
|
|||
(args.rerun_from is not None and
|
||||
'opensfm' in args.rerun_from)
|
||||
|
||||
# check if reconstruction was done before
|
||||
if args.use_opensfm_pointcloud:
|
||||
output_file = tree.opensfm_model
|
||||
else:
|
||||
output_file = tree.opensfm_reconstruction
|
||||
|
||||
if not io.file_exists(tree.opensfm_reconstruction) or rerun_cell:
|
||||
# check if reconstruction was done before
|
||||
if not io.file_exists(output_file) or rerun_cell:
|
||||
# create file list
|
||||
list_path = io.join_paths(tree.opensfm, 'image_list.txt')
|
||||
with open(list_path, 'w') as fout:
|
||||
|
@ -77,12 +81,18 @@ class ODMOpenSfMCell(ecto.Cell):
|
|||
# run OpenSfM reconstruction
|
||||
system.run('PYTHONPATH=%s %s/bin/run_all %s' %
|
||||
(context.pyopencv_path, context.opensfm_path, tree.opensfm))
|
||||
if args.use_opensfm_pointcloud:
|
||||
system.run('PYTHONPATH=%s %s/bin/opensfm export_visualsfm %s' %
|
||||
(context.pyopencv_path, context.opensfm_path, tree.opensfm))
|
||||
system.run('PYTHONPATH=%s %s/bin/opensfm undistort %s' %
|
||||
(context.pyopencv_path, context.opensfm_path, tree.opensfm))
|
||||
system.run('PYTHONPATH=%s %s/bin/opensfm compute_depthmaps %s' %
|
||||
(context.pyopencv_path, context.opensfm_path, tree.opensfm))
|
||||
else:
|
||||
log.ODM_WARNING('Found a valid OpenSfM file in: %s' %
|
||||
tree.opensfm_reconstruction)
|
||||
|
||||
# check if reconstruction was exported to bundler before
|
||||
|
||||
if not io.file_exists(tree.opensfm_bundle_list) or rerun_cell:
|
||||
# convert back to bundler's format
|
||||
system.run('PYTHONPATH=%s %s/bin/export_bundler %s' %
|
||||
|
@ -91,17 +101,17 @@ class ODMOpenSfMCell(ecto.Cell):
|
|||
log.ODM_WARNING('Found a valid Bundler file in: %s' %
|
||||
tree.opensfm_reconstruction)
|
||||
|
||||
# check if reconstruction was exported to pmvs before
|
||||
if not args.use_opensfm_pointcloud:
|
||||
# check if reconstruction was exported to pmvs before
|
||||
if not io.file_exists(tree.pmvs_visdat) or rerun_cell:
|
||||
# run PMVS converter
|
||||
system.run('PYTHONPATH=%s %s/bin/export_pmvs %s --output %s' %
|
||||
(context.pyopencv_path, context.opensfm_path, tree.opensfm, tree.pmvs))
|
||||
else:
|
||||
log.ODM_WARNING('Found a valid CMVS file in: %s' % tree.pmvs_visdat)
|
||||
|
||||
if not io.file_exists(tree.pmvs_visdat) or rerun_cell:
|
||||
# run PMVS converter
|
||||
system.run('PYTHONPATH=%s %s/bin/export_pmvs %s --output %s' %
|
||||
(context.pyopencv_path, context.opensfm_path, tree.opensfm, tree.pmvs))
|
||||
else:
|
||||
log.ODM_WARNING('Found a valid CMVS file in: %s' % tree.pmvs_visdat)
|
||||
|
||||
if args.time:
|
||||
system.benchmark(start_time, tree.benchmarking, 'OpenSfM')
|
||||
if args.time:
|
||||
system.benchmark(start_time, tree.benchmarking, 'OpenSfM')
|
||||
|
||||
log.ODM_INFO('Running ODM OpenSfM Cell - Finished')
|
||||
return ecto.OK if args.end_with != 'opensfm' else ecto.QUIT
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
import os
|
||||
import numpy as np
|
||||
|
||||
from opendm import log
|
||||
|
||||
# Go from QR-factorizatoin to corresponding RQ-factorization.
|
||||
def rq(A):
|
||||
Q,R = np.linalg.qr(np.flipud(A).T)
|
||||
R = np.flipud(R.T)
|
||||
Q = Q.T
|
||||
return R[:,::-1],Q[::-1,:]
|
||||
|
||||
# Create a unit quaternion from rotation matrix.
|
||||
def rot2quat(R):
|
||||
|
||||
# Float epsilon (use square root to be well with the stable region).
|
||||
eps = np.sqrt(np.finfo(float).eps)
|
||||
|
||||
# If the determinant is not 1, it's not a rotation matrix
|
||||
if np.abs(np.linalg.det(R) - 1.0) > eps:
|
||||
log.ODM_ERROR('Matrix passed to rot2quat was not a rotation matrix, det != 1.0')
|
||||
|
||||
tr = np.trace(R)
|
||||
|
||||
quat = np.zeros((1,4))
|
||||
|
||||
# Is trace big enough be computationally stable?
|
||||
if tr > eps:
|
||||
S = 0.5 / np.sqrt(tr + 1.0)
|
||||
quat[0,0] = 0.25 / S
|
||||
quat[0,1] = (R[2,1] - R[1,2]) * S
|
||||
quat[0,2] = (R[0,2] - R[2,0]) * S
|
||||
quat[0,3] = (R[1,0] - R[0,1]) * S
|
||||
else: # It's not, use the largest diagonal.
|
||||
if R[0,0] > R[1,1] and R[0,0] > R[2,2]:
|
||||
S = np.sqrt(1.0 + R[0,0] - R[1,1] - R[2,2]) * 2.0
|
||||
quat[0,0] = (R[2,1] - R[1,2]) / S
|
||||
quat[0,1] = 0.25 * S
|
||||
quat[0,2] = (R[0,1] + R[1,0]) / S
|
||||
quat[0,3] = (R[0,2] + R[2,0]) / S
|
||||
elif R[1,1] > R[2,2]:
|
||||
S = np.sqrt(1.0 - R[0,0] + R[1,1] - R[2,2]) * 2.0
|
||||
quat[0,0] = (R[0,2] - R[2,0]) / S
|
||||
quat[0,1] = (R[0,1] + R[1,0]) / S
|
||||
quat[0,2] = 0.25 * S
|
||||
quat[0,3] = (R[1,2] + R[2,1]) / S
|
||||
else:
|
||||
S = np.sqrt(1.0 - R[0,0] - R[1,1] + R[2,2]) * 2.0
|
||||
quat[0,0] = (R[1,0] - R[0,1]) / S
|
||||
quat[0,1] = (R[0,2] + R[2,0]) / S
|
||||
quat[0,2] = (R[1,2] + R[2,1]) / S
|
||||
quat[0,3] = 0.25 * S
|
||||
|
||||
return quat
|
||||
|
||||
# Decompose a projection matrix into parts
|
||||
# (Intrinsic projection, Rotation, Camera position)
|
||||
def decomposeProjection(projectionMatrix):
|
||||
|
||||
# Check input:
|
||||
if projectionMatrix.shape != (3,4):
|
||||
log.ODM_ERROR('Unable to decompose projection matrix, shape != (3,4)')
|
||||
|
||||
RQ = rq(projectionMatrix[:,:3])
|
||||
|
||||
# Fix sign, since we know K is upper triangular and has a positive diagonal.
|
||||
signMat = np.diag(np.diag(np.sign(RQ[0])))
|
||||
K = signMat*RQ[0]
|
||||
R = signMat*RQ[1]
|
||||
|
||||
# Calculate camera position from translation vector.
|
||||
t = np.linalg.inv(-1.0*projectionMatrix[:,:3])*projectionMatrix[:,3]
|
||||
|
||||
return K, R, t
|
||||
|
||||
# Parses pvms contour file.
|
||||
def parseContourFile(filePath):
|
||||
|
||||
with open(filePath, 'r') as contourFile:
|
||||
if (contourFile.readline().strip() != "CONTOUR"):
|
||||
return np.array([])
|
||||
else:
|
||||
pMatData = np.loadtxt(contourFile, float, '#', None, None, 0)
|
||||
if pMatData.shape == (3,4):
|
||||
return pMatData
|
||||
return np.array([])
|
||||
|
||||
|
||||
|
||||
# Creates a .nvm camera file in the pmvs folder.
|
||||
def run(pmvsFolder, outputFile):
|
||||
|
||||
projectionFolder = pmvsFolder + "/txt"
|
||||
imageFolder = pmvsFolder + "/visualize"
|
||||
|
||||
pMatrices = []
|
||||
imageFileNames = []
|
||||
|
||||
# for all files in the visualize folder:
|
||||
for imageFileName in os.listdir(imageFolder):
|
||||
fileNameNoExt = os.path.splitext(imageFileName)[0]
|
||||
|
||||
# look for corresponding projection matrix txt file
|
||||
projectionFilePath = os.path.join(projectionFolder, fileNameNoExt)
|
||||
projectionFilePath += ".txt"
|
||||
if os.path.isfile(projectionFilePath):
|
||||
pMatData = parseContourFile(projectionFilePath)
|
||||
if pMatData.size == 0:
|
||||
log.ODM_WARNING('Unable to parse contour file, skipping: %s'
|
||||
% projectionFilePath)
|
||||
else:
|
||||
pMatrices.append(np.matrix(pMatData))
|
||||
imageFileNames.append(imageFileName)
|
||||
|
||||
|
||||
# Decompose projection matrices
|
||||
focals = []
|
||||
rotations = []
|
||||
translations = []
|
||||
for projection in pMatrices:
|
||||
KRt = decomposeProjection(projection)
|
||||
focals.append(KRt[0][0,0])
|
||||
rotations.append(rot2quat(KRt[1]))
|
||||
translations.append(KRt[2])
|
||||
|
||||
# Create .nvm file
|
||||
with open (outputFile, 'w') as nvmFile:
|
||||
nvmFile.write("NVM_V3\n\n")
|
||||
nvmFile.write('%d' % len(rotations) + "\n")
|
||||
|
||||
for idx, imageFileName in enumerate(imageFileNames):
|
||||
nvmFile.write(os.path.join("visualize", imageFileName))
|
||||
nvmFile.write(" " + '%f' % focals[idx])
|
||||
nvmFile.write(" " + '%f' % rotations[idx][0,0] +
|
||||
" " + '%f' % rotations[idx][0,1] +
|
||||
" " + '%f' % rotations[idx][0,2] +
|
||||
" " + '%f' % rotations[idx][0,3])
|
||||
nvmFile.write(" " + '%f' % translations[idx][0] +
|
||||
" " + '%f' % translations[idx][1] +
|
||||
" " + '%f' % translations[idx][2])
|
||||
nvmFile.write(" 0 0\n")
|
||||
nvmFile.write("0\n\n")
|
||||
nvmFile.write("0\n\n")
|
||||
nvmFile.write("0")
|
|
@ -2,12 +2,61 @@ import ecto
|
|||
import cv2
|
||||
import pyexiv2
|
||||
|
||||
from functools import partial
|
||||
from multiprocessing import Pool
|
||||
from opendm import log
|
||||
from opendm import system
|
||||
from opendm import io
|
||||
from opendm import types
|
||||
|
||||
|
||||
def resize(src_dir, target_dir, resize_to, rerun_cell, photo):
|
||||
# define image paths
|
||||
path_file = photo.path_file
|
||||
new_path_file = io.join_paths(target_dir, photo.filename)
|
||||
# set raw image path in case we want to rerun cell
|
||||
if io.file_exists(new_path_file) and rerun_cell:
|
||||
path_file = io.join_paths(src_dir, photo.filename)
|
||||
|
||||
if not io.file_exists(new_path_file) or rerun_cell:
|
||||
# open and resize image with opencv
|
||||
img = cv2.imread(path_file)
|
||||
# compute new size
|
||||
max_side = max(img.shape[0], img.shape[1])
|
||||
if max_side <= resize_to:
|
||||
log.ODM_WARNING('Resize Parameter is greater than the largest side of the image')
|
||||
ratio = float(resize_to) / float(max_side)
|
||||
img_r = cv2.resize(img, None, fx=ratio, fy=ratio)
|
||||
# write image with opencv
|
||||
cv2.imwrite(new_path_file, img_r)
|
||||
# read metadata with pyexiv2
|
||||
old_meta = pyexiv2.ImageMetadata(path_file)
|
||||
new_meta = pyexiv2.ImageMetadata(new_path_file)
|
||||
old_meta.read()
|
||||
new_meta.read()
|
||||
# copy metadata
|
||||
old_meta.copy(new_meta)
|
||||
# update metadata size
|
||||
new_meta['Exif.Photo.PixelXDimension'] = img_r.shape[0]
|
||||
new_meta['Exif.Photo.PixelYDimension'] = img_r.shape[1]
|
||||
new_meta.write()
|
||||
# update photos array with new values
|
||||
photo.path_file = new_path_file
|
||||
photo.width = img_r.shape[0]
|
||||
photo.height = img_r.shape[1]
|
||||
photo.update_focal()
|
||||
|
||||
# log message
|
||||
log.ODM_DEBUG('Resized %s | dimensions: %s' %
|
||||
(photo.filename, img_r.shape))
|
||||
else:
|
||||
# log message
|
||||
log.ODM_WARNING('Already resized %s | dimensions: %s x %s' %
|
||||
(photo.filename, photo.width, photo.height))
|
||||
|
||||
return photo
|
||||
|
||||
|
||||
class ODMResizeCell(ecto.Cell):
|
||||
def declare_params(self, params):
|
||||
params.declare("resize_to", "resizes images by the largest side", 2400)
|
||||
|
@ -51,49 +100,14 @@ class ODMResizeCell(ecto.Cell):
|
|||
'resize' in args.rerun_from)
|
||||
|
||||
# loop over photos
|
||||
for photo in photos:
|
||||
# define image paths
|
||||
path_file = photo.path_file
|
||||
new_path_file = io.join_paths(tree.dataset_resize, photo.filename)
|
||||
# set raw image path in case we want to rerun cell
|
||||
if io.file_exists(new_path_file) and rerun_cell:
|
||||
path_file = io.join_paths(tree.dataset_raw, photo.filename)
|
||||
|
||||
if not io.file_exists(new_path_file) or rerun_cell:
|
||||
# open and resize image with opencv
|
||||
img = cv2.imread(path_file)
|
||||
# compute new size
|
||||
max_side = max(img.shape[0], img.shape[1])
|
||||
if max_side <= self.params.resize_to:
|
||||
log.ODM_WARNING('Resize Parameter is greater than the largest side of the image')
|
||||
ratio = float(self.params.resize_to) / float(max_side)
|
||||
img_r = cv2.resize(img, None, fx=ratio, fy=ratio)
|
||||
# write image with opencv
|
||||
cv2.imwrite(new_path_file, img_r)
|
||||
# read metadata with pyexiv2
|
||||
old_meta = pyexiv2.ImageMetadata(path_file)
|
||||
new_meta = pyexiv2.ImageMetadata(new_path_file)
|
||||
old_meta.read()
|
||||
new_meta.read()
|
||||
# copy metadata
|
||||
old_meta.copy(new_meta)
|
||||
# update metadata size
|
||||
new_meta['Exif.Photo.PixelXDimension'] = img_r.shape[0]
|
||||
new_meta['Exif.Photo.PixelYDimension'] = img_r.shape[1]
|
||||
new_meta.write()
|
||||
# update photos array with new values
|
||||
photo.path_file = new_path_file
|
||||
photo.width = img_r.shape[0]
|
||||
photo.height = img_r.shape[1]
|
||||
photo.update_focal()
|
||||
|
||||
# log message
|
||||
log.ODM_DEBUG('Resized %s | dimensions: %s' %
|
||||
(photo.filename, img_r.shape))
|
||||
else:
|
||||
# log message
|
||||
log.ODM_WARNING('Already resized %s | dimensions: %s x %s' %
|
||||
(photo.filename, photo.width, photo.height))
|
||||
photos = Pool().map(
|
||||
partial(resize,
|
||||
tree.dataset_raw,
|
||||
tree.dataset_resize,
|
||||
self.params.resize_to,
|
||||
rerun_cell),
|
||||
photos
|
||||
)
|
||||
|
||||
log.ODM_INFO('Resized %s images' % len(photos))
|
||||
|
||||
|
@ -105,4 +119,3 @@ class ODMResizeCell(ecto.Cell):
|
|||
|
||||
log.ODM_INFO('Running ODM Resize Cell - Finished')
|
||||
return ecto.OK if args.end_with != 'resize' else ecto.QUIT
|
||||
|
||||
|
|
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 193 KiB |
Ładowanie…
Reference in New Issue