2015-12-02 14:24:38 +00:00
|
|
|
import cv2
|
2015-11-23 15:43:29 +00:00
|
|
|
import pyexiv2
|
2015-12-11 21:26:04 +00:00
|
|
|
import re
|
|
|
|
from fractions import Fraction
|
2016-03-04 19:35:35 +00:00
|
|
|
from opensfm.exif import sensor_string
|
2018-01-26 19:38:26 +00:00
|
|
|
from pyproj import Proj
|
2015-11-17 17:17:56 +00:00
|
|
|
|
|
|
|
import log
|
2015-11-26 12:15:02 +00:00
|
|
|
import io
|
2015-11-18 16:39:38 +00:00
|
|
|
import system
|
2015-12-11 21:26:04 +00:00
|
|
|
import context
|
2015-11-17 17:17:56 +00:00
|
|
|
|
2016-02-26 18:50:12 +00:00
|
|
|
|
2015-12-10 17:17:39 +00:00
|
|
|
class ODM_Photo:
|
2015-11-17 17:17:56 +00:00
|
|
|
""" ODMPhoto - a class for ODMPhotos
|
|
|
|
"""
|
2016-02-26 18:50:12 +00:00
|
|
|
|
2015-12-10 12:35:52 +00:00
|
|
|
def __init__(self, path_file, force_focal, force_ccd):
|
2015-11-17 17:17:56 +00:00
|
|
|
# general purpose
|
2015-11-23 15:43:29 +00:00
|
|
|
self.path_file = path_file
|
2015-11-27 16:48:15 +00:00
|
|
|
self.filename = io.extract_file_from_path_file(path_file)
|
2015-11-20 10:00:43 +00:00
|
|
|
# useful attibutes
|
2015-11-19 12:01:15 +00:00
|
|
|
self.width = None
|
|
|
|
self.height = None
|
2015-11-20 10:00:43 +00:00
|
|
|
self.ccd_width = None
|
|
|
|
self.focal_length = None
|
|
|
|
self.focal_length_px = None
|
2015-11-19 12:01:15 +00:00
|
|
|
# other attributes
|
2016-07-27 14:27:34 +00:00
|
|
|
self.camera_make = ''
|
|
|
|
self.camera_model = ''
|
|
|
|
self.make_model = ''
|
2017-06-23 15:49:24 +00:00
|
|
|
self.latitude = None
|
|
|
|
self.longitude = None
|
|
|
|
self.altitude = None
|
2015-11-27 10:00:08 +00:00
|
|
|
# parse values from metadata
|
2015-12-10 12:35:52 +00:00
|
|
|
self.parse_pyexiv2_values(self.path_file, force_focal, force_ccd)
|
2016-02-26 18:50:12 +00:00
|
|
|
# compute focal length into pixels
|
2015-11-27 16:48:15 +00:00
|
|
|
self.update_focal()
|
2015-11-17 17:17:56 +00:00
|
|
|
|
2015-11-27 16:48:15 +00:00
|
|
|
# print log message
|
2017-06-23 15:49:24 +00:00
|
|
|
log.ODM_DEBUG('Loaded {} | camera: {} | dimensions: {} x {} | focal: {} | ccd: {} | lat: {} | lon: {} | alt: {}'
|
|
|
|
.format(self.filename, self.make_model, self.width, self.height, self.focal_length,
|
|
|
|
self.ccd_width, self.latitude, self.longitude, self.altitude))
|
2015-11-27 10:00:08 +00:00
|
|
|
|
2015-11-27 16:48:15 +00:00
|
|
|
def update_focal(self):
|
2015-11-27 10:00:08 +00:00
|
|
|
# compute focal length in pixels
|
|
|
|
if self.focal_length and self.ccd_width:
|
|
|
|
# take width or height as reference
|
2015-11-17 17:17:56 +00:00
|
|
|
if self.width > self.height:
|
2015-11-27 10:00:08 +00:00
|
|
|
# f(px) = w(px) * f(mm) / ccd(mm)
|
2015-11-17 17:17:56 +00:00
|
|
|
self.focal_length_px = \
|
|
|
|
self.width * (self.focal_length / self.ccd_width)
|
|
|
|
else:
|
2015-11-27 10:00:08 +00:00
|
|
|
# f(px) = h(px) * f(mm) / ccd(mm)
|
2015-11-17 17:17:56 +00:00
|
|
|
self.focal_length_px = \
|
|
|
|
self.height * (self.focal_length / self.ccd_width)
|
2015-11-23 15:43:29 +00:00
|
|
|
|
2015-12-10 12:35:52 +00:00
|
|
|
def parse_pyexiv2_values(self, _path_file, _force_focal, _force_ccd):
|
2015-11-23 15:43:29 +00:00
|
|
|
# read image metadata
|
|
|
|
metadata = pyexiv2.ImageMetadata(_path_file)
|
|
|
|
metadata.read()
|
|
|
|
# loop over image tags
|
|
|
|
for key in metadata:
|
2015-12-10 11:01:41 +00:00
|
|
|
# try/catch tag value due to weird bug in pyexiv2
|
2015-12-04 14:12:40 +00:00
|
|
|
# ValueError: invalid literal for int() with base 10: ''
|
2017-06-23 15:49:24 +00:00
|
|
|
GPS = 'Exif.GPSInfo.GPS'
|
2015-12-04 14:12:40 +00:00
|
|
|
try:
|
|
|
|
# parse tag names
|
2016-02-26 18:50:12 +00:00
|
|
|
if key == 'Exif.Image.Make':
|
Avoid crash in pyexiv2 with some image files.
Resolves https://github.com/OpenDroneMap/OpenDroneMap/issues/580
Issue was caused by attempting to read data in ImageUniqueID tag,
even though data is populated.
By only reading the values we need from metadata we avoid the issue
in pyexiv2.
Should also improve performance.
stacktrace of error:
.#0 strlen () at ../sysdeps/x86_64/strlen.S:106
.#1 0x00007fffdb154ce7 in exiv2wrapper::ExifTag::ExifTag(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, Exiv2::Exifdatum*, Exiv2::ExifData*, Exiv2::ByteOrder) () from /usr/lib/python2.7/dist-packages/libexiv2python.so
.#2 0x00007fffdb154eb4 in exiv2wrapper::Image::getExifTag(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) ()
from /usr/lib/python2.7/dist-packages/libexiv2python.so
.#3 0x00007fffdb15f3e6 in boost::python::objects::caller_py_function_impl<boost::python::detail::caller<exiv2wrapper::ExifTag const (exiv2wrapper::Image::*)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >), boost::python::default_call_policies, boost::mpl::vector3<exiv2wrapper::ExifTag const, exiv2wrapper::Image&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >::operator()(_object*, _object*) ()
from /usr/lib/python2.7/dist-packages/libexiv2python.so
.#4 0x00007ffff52845cd in boost::python::objects::function::call(_object*, _object*) const () from /usr/lib/x86_64-linux-gnu/libboost_python-py27.so.1.58.0
.#5 0x00007ffff52847c8 in ?? () from /usr/lib/x86_64-linux-gnu/libboost_python-py27.so.1.58.0
.#6 0x00007ffff528c823 in boost::python::detail::exception_handler::operator()(boost::function0<void> const&) const ()
from /usr/lib/x86_64-linux-gnu/libboost_python-py27.so.1.58.0
.#7 0x00007fffdb15dd63 in boost::detail::function::function_obj_invoker2<boost::_bi::bind_t<bool, boost::python::detail::translate_exception<Exiv2::BasicError<char>, void (*)(Exiv2::BasicError<char> const&)>, boost::_bi::list3<boost::arg<1>, boost::arg<2>, boost::_bi::value<void (*)(Exiv2::BasicError<char> const&)> > >, bool, boost::python::detail::exception_handler const&, boost::function0<void> const&>::invoke(boost::detail::function::function_buffer&, boost::python::detail::exception_handler const&, boost::function0<void> const&) () from /usr/lib/python2.7/dist-packages/libexiv2python.so
.#8 0x00007ffff528c7f8 in boost::python::detail::exception_handler::operator()(boost::function0<void> const&) const ()
from /usr/lib/x86_64-linux-gnu/libboost_python-py27.so.1.58.0
.#9 0x00007ffff5d01ab8 in boost::python::detail::translate_exception<ecto::except::NullTendril, void (*)(ecto::except::NullTendril const&)>::operator()(boost::python::detail::exception_handler const&, boost::function0<void> const&, void (*)(ecto::except::NullTendril const&)) const ()
from /home/mribbons/OpenDroneMap170517/SuperBuild/install/lib/python2.7/dist-packages/ecto/ecto_main.so
.#10 0x00007ffff5d0099f in bool boost::_bi::list3<boost::arg<1>, boost::arg<2>, boost::_bi::value<void (*)(ecto::except::NullTendril const&)> >::operator()<bool, boost::python::detail::translate_exception<ecto::except::NullTendril, void (*)(ecto::except::NullTendril const&)>, boost::_bi::list2<boost::python::detail::exception_handler const&, boost::function0<void> const&> >(boost::_bi::type<bool>, boost::python::detail::translate_exception<ecto::except::NullTendril, void (*)(ecto::except::NullTendril const&)>&, boost::_bi::list2<boost::python::detail::exception_handler const&, boost::function0<void> const&>&, long) ()
from /home/mribbons/OpenDroneMap170517/SuperBuild/install/lib/python2.7/dist-packages/ecto/ecto_main.so
.#11 0x00007ffff5cffbe7 in bool boost::_bi::bind_t<bool, boost::python::detail::translate_exception<ecto::except::NullTendril, void (*)(ecto::except::NullTendril const&)>, boost::_bi::list3<boost::arg<1>, boost::arg<2>, boost::_bi::value<void (*)(ecto::except::NullTendril const&)> > >::operator()<boost::python::detail::exception_handler, boost::function0<void> >(boost::python::detail::exception_handler const&, boost::function0<void> const&) ()
from /home/mribbons/OpenDroneMap170517/SuperBuild/install/lib/python2.7/dist-packages/ecto/ecto_main.so
.#12 0x00007ffff5cfeb54 in boost::detail::function::function_obj_invoker2<boost::_bi::bind_t<bool, boost::python::detail::translate_exception<ecto::except::NullTendril, void (*)(ecto::except::NullTendril const&)>, boost::_bi::list3<boost::arg<1>, boost::arg<2>, boost::_bi::value<void (*)(ecto::except::NullTendril const&)> > >, bool, boost::python::detail::exception_handler const&, boost::function0<void> const&>::invoke(boost::detail::function::function_buffer&, boost::python::detail::exception_handler const&, boost::function0<void> const&) ()
from /home/mribbons/OpenDroneMap170517/SuperBuild/install/lib/python2.7/dist-packages/ecto/ecto_main.so
.#13 0x00007ffff528c7f8 in boost::python::detail::exception_handler::operator()(boost::function0<void> const&) const ()
from /usr/lib/x86_64-linux-gnu/libboost_python-py27.so.1.58.0
...
2017-05-19 01:45:33 +00:00
|
|
|
self.camera_make = metadata[key].value
|
2016-02-26 18:50:12 +00:00
|
|
|
elif key == 'Exif.Image.Model':
|
Avoid crash in pyexiv2 with some image files.
Resolves https://github.com/OpenDroneMap/OpenDroneMap/issues/580
Issue was caused by attempting to read data in ImageUniqueID tag,
even though data is populated.
By only reading the values we need from metadata we avoid the issue
in pyexiv2.
Should also improve performance.
stacktrace of error:
.#0 strlen () at ../sysdeps/x86_64/strlen.S:106
.#1 0x00007fffdb154ce7 in exiv2wrapper::ExifTag::ExifTag(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, Exiv2::Exifdatum*, Exiv2::ExifData*, Exiv2::ByteOrder) () from /usr/lib/python2.7/dist-packages/libexiv2python.so
.#2 0x00007fffdb154eb4 in exiv2wrapper::Image::getExifTag(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) ()
from /usr/lib/python2.7/dist-packages/libexiv2python.so
.#3 0x00007fffdb15f3e6 in boost::python::objects::caller_py_function_impl<boost::python::detail::caller<exiv2wrapper::ExifTag const (exiv2wrapper::Image::*)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >), boost::python::default_call_policies, boost::mpl::vector3<exiv2wrapper::ExifTag const, exiv2wrapper::Image&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >::operator()(_object*, _object*) ()
from /usr/lib/python2.7/dist-packages/libexiv2python.so
.#4 0x00007ffff52845cd in boost::python::objects::function::call(_object*, _object*) const () from /usr/lib/x86_64-linux-gnu/libboost_python-py27.so.1.58.0
.#5 0x00007ffff52847c8 in ?? () from /usr/lib/x86_64-linux-gnu/libboost_python-py27.so.1.58.0
.#6 0x00007ffff528c823 in boost::python::detail::exception_handler::operator()(boost::function0<void> const&) const ()
from /usr/lib/x86_64-linux-gnu/libboost_python-py27.so.1.58.0
.#7 0x00007fffdb15dd63 in boost::detail::function::function_obj_invoker2<boost::_bi::bind_t<bool, boost::python::detail::translate_exception<Exiv2::BasicError<char>, void (*)(Exiv2::BasicError<char> const&)>, boost::_bi::list3<boost::arg<1>, boost::arg<2>, boost::_bi::value<void (*)(Exiv2::BasicError<char> const&)> > >, bool, boost::python::detail::exception_handler const&, boost::function0<void> const&>::invoke(boost::detail::function::function_buffer&, boost::python::detail::exception_handler const&, boost::function0<void> const&) () from /usr/lib/python2.7/dist-packages/libexiv2python.so
.#8 0x00007ffff528c7f8 in boost::python::detail::exception_handler::operator()(boost::function0<void> const&) const ()
from /usr/lib/x86_64-linux-gnu/libboost_python-py27.so.1.58.0
.#9 0x00007ffff5d01ab8 in boost::python::detail::translate_exception<ecto::except::NullTendril, void (*)(ecto::except::NullTendril const&)>::operator()(boost::python::detail::exception_handler const&, boost::function0<void> const&, void (*)(ecto::except::NullTendril const&)) const ()
from /home/mribbons/OpenDroneMap170517/SuperBuild/install/lib/python2.7/dist-packages/ecto/ecto_main.so
.#10 0x00007ffff5d0099f in bool boost::_bi::list3<boost::arg<1>, boost::arg<2>, boost::_bi::value<void (*)(ecto::except::NullTendril const&)> >::operator()<bool, boost::python::detail::translate_exception<ecto::except::NullTendril, void (*)(ecto::except::NullTendril const&)>, boost::_bi::list2<boost::python::detail::exception_handler const&, boost::function0<void> const&> >(boost::_bi::type<bool>, boost::python::detail::translate_exception<ecto::except::NullTendril, void (*)(ecto::except::NullTendril const&)>&, boost::_bi::list2<boost::python::detail::exception_handler const&, boost::function0<void> const&>&, long) ()
from /home/mribbons/OpenDroneMap170517/SuperBuild/install/lib/python2.7/dist-packages/ecto/ecto_main.so
.#11 0x00007ffff5cffbe7 in bool boost::_bi::bind_t<bool, boost::python::detail::translate_exception<ecto::except::NullTendril, void (*)(ecto::except::NullTendril const&)>, boost::_bi::list3<boost::arg<1>, boost::arg<2>, boost::_bi::value<void (*)(ecto::except::NullTendril const&)> > >::operator()<boost::python::detail::exception_handler, boost::function0<void> >(boost::python::detail::exception_handler const&, boost::function0<void> const&) ()
from /home/mribbons/OpenDroneMap170517/SuperBuild/install/lib/python2.7/dist-packages/ecto/ecto_main.so
.#12 0x00007ffff5cfeb54 in boost::detail::function::function_obj_invoker2<boost::_bi::bind_t<bool, boost::python::detail::translate_exception<ecto::except::NullTendril, void (*)(ecto::except::NullTendril const&)>, boost::_bi::list3<boost::arg<1>, boost::arg<2>, boost::_bi::value<void (*)(ecto::except::NullTendril const&)> > >, bool, boost::python::detail::exception_handler const&, boost::function0<void> const&>::invoke(boost::detail::function::function_buffer&, boost::python::detail::exception_handler const&, boost::function0<void> const&) ()
from /home/mribbons/OpenDroneMap170517/SuperBuild/install/lib/python2.7/dist-packages/ecto/ecto_main.so
.#13 0x00007ffff528c7f8 in boost::python::detail::exception_handler::operator()(boost::function0<void> const&) const ()
from /usr/lib/x86_64-linux-gnu/libboost_python-py27.so.1.58.0
...
2017-05-19 01:45:33 +00:00
|
|
|
self.camera_model = metadata[key].value
|
2016-02-26 18:50:12 +00:00
|
|
|
elif key == 'Exif.Photo.FocalLength':
|
Avoid crash in pyexiv2 with some image files.
Resolves https://github.com/OpenDroneMap/OpenDroneMap/issues/580
Issue was caused by attempting to read data in ImageUniqueID tag,
even though data is populated.
By only reading the values we need from metadata we avoid the issue
in pyexiv2.
Should also improve performance.
stacktrace of error:
.#0 strlen () at ../sysdeps/x86_64/strlen.S:106
.#1 0x00007fffdb154ce7 in exiv2wrapper::ExifTag::ExifTag(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, Exiv2::Exifdatum*, Exiv2::ExifData*, Exiv2::ByteOrder) () from /usr/lib/python2.7/dist-packages/libexiv2python.so
.#2 0x00007fffdb154eb4 in exiv2wrapper::Image::getExifTag(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) ()
from /usr/lib/python2.7/dist-packages/libexiv2python.so
.#3 0x00007fffdb15f3e6 in boost::python::objects::caller_py_function_impl<boost::python::detail::caller<exiv2wrapper::ExifTag const (exiv2wrapper::Image::*)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >), boost::python::default_call_policies, boost::mpl::vector3<exiv2wrapper::ExifTag const, exiv2wrapper::Image&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >::operator()(_object*, _object*) ()
from /usr/lib/python2.7/dist-packages/libexiv2python.so
.#4 0x00007ffff52845cd in boost::python::objects::function::call(_object*, _object*) const () from /usr/lib/x86_64-linux-gnu/libboost_python-py27.so.1.58.0
.#5 0x00007ffff52847c8 in ?? () from /usr/lib/x86_64-linux-gnu/libboost_python-py27.so.1.58.0
.#6 0x00007ffff528c823 in boost::python::detail::exception_handler::operator()(boost::function0<void> const&) const ()
from /usr/lib/x86_64-linux-gnu/libboost_python-py27.so.1.58.0
.#7 0x00007fffdb15dd63 in boost::detail::function::function_obj_invoker2<boost::_bi::bind_t<bool, boost::python::detail::translate_exception<Exiv2::BasicError<char>, void (*)(Exiv2::BasicError<char> const&)>, boost::_bi::list3<boost::arg<1>, boost::arg<2>, boost::_bi::value<void (*)(Exiv2::BasicError<char> const&)> > >, bool, boost::python::detail::exception_handler const&, boost::function0<void> const&>::invoke(boost::detail::function::function_buffer&, boost::python::detail::exception_handler const&, boost::function0<void> const&) () from /usr/lib/python2.7/dist-packages/libexiv2python.so
.#8 0x00007ffff528c7f8 in boost::python::detail::exception_handler::operator()(boost::function0<void> const&) const ()
from /usr/lib/x86_64-linux-gnu/libboost_python-py27.so.1.58.0
.#9 0x00007ffff5d01ab8 in boost::python::detail::translate_exception<ecto::except::NullTendril, void (*)(ecto::except::NullTendril const&)>::operator()(boost::python::detail::exception_handler const&, boost::function0<void> const&, void (*)(ecto::except::NullTendril const&)) const ()
from /home/mribbons/OpenDroneMap170517/SuperBuild/install/lib/python2.7/dist-packages/ecto/ecto_main.so
.#10 0x00007ffff5d0099f in bool boost::_bi::list3<boost::arg<1>, boost::arg<2>, boost::_bi::value<void (*)(ecto::except::NullTendril const&)> >::operator()<bool, boost::python::detail::translate_exception<ecto::except::NullTendril, void (*)(ecto::except::NullTendril const&)>, boost::_bi::list2<boost::python::detail::exception_handler const&, boost::function0<void> const&> >(boost::_bi::type<bool>, boost::python::detail::translate_exception<ecto::except::NullTendril, void (*)(ecto::except::NullTendril const&)>&, boost::_bi::list2<boost::python::detail::exception_handler const&, boost::function0<void> const&>&, long) ()
from /home/mribbons/OpenDroneMap170517/SuperBuild/install/lib/python2.7/dist-packages/ecto/ecto_main.so
.#11 0x00007ffff5cffbe7 in bool boost::_bi::bind_t<bool, boost::python::detail::translate_exception<ecto::except::NullTendril, void (*)(ecto::except::NullTendril const&)>, boost::_bi::list3<boost::arg<1>, boost::arg<2>, boost::_bi::value<void (*)(ecto::except::NullTendril const&)> > >::operator()<boost::python::detail::exception_handler, boost::function0<void> >(boost::python::detail::exception_handler const&, boost::function0<void> const&) ()
from /home/mribbons/OpenDroneMap170517/SuperBuild/install/lib/python2.7/dist-packages/ecto/ecto_main.so
.#12 0x00007ffff5cfeb54 in boost::detail::function::function_obj_invoker2<boost::_bi::bind_t<bool, boost::python::detail::translate_exception<ecto::except::NullTendril, void (*)(ecto::except::NullTendril const&)>, boost::_bi::list3<boost::arg<1>, boost::arg<2>, boost::_bi::value<void (*)(ecto::except::NullTendril const&)> > >, bool, boost::python::detail::exception_handler const&, boost::function0<void> const&>::invoke(boost::detail::function::function_buffer&, boost::python::detail::exception_handler const&, boost::function0<void> const&) ()
from /home/mribbons/OpenDroneMap170517/SuperBuild/install/lib/python2.7/dist-packages/ecto/ecto_main.so
.#13 0x00007ffff528c7f8 in boost::python::detail::exception_handler::operator()(boost::function0<void> const&) const ()
from /usr/lib/x86_64-linux-gnu/libboost_python-py27.so.1.58.0
...
2017-05-19 01:45:33 +00:00
|
|
|
self.focal_length = float(metadata[key].value)
|
2017-06-23 15:49:24 +00:00
|
|
|
elif key == GPS + 'Latitude':
|
|
|
|
self.latitude = self.dms_to_decimal(*metadata[key].value +
|
|
|
|
[metadata[GPS + 'LatitudeRef'].value])
|
|
|
|
elif key == GPS + 'Longitude':
|
|
|
|
self.longitude = self.dms_to_decimal(*metadata[key].value +
|
|
|
|
[metadata[GPS + 'LongitudeRef'].value])
|
|
|
|
elif key == GPS + 'Altitude':
|
|
|
|
self.altitude = float(metadata[key].value)
|
2017-07-31 16:52:25 +00:00
|
|
|
if metadata[GPS + 'AltitudeRef'] and int(metadata[GPS + 'AltitudeRef'].value) > 0:
|
|
|
|
self.altitude *= -1.
|
2016-05-07 16:27:52 +00:00
|
|
|
except (pyexiv2.ExifValueError, ValueError) as e:
|
2015-12-04 14:12:40 +00:00
|
|
|
pass
|
2017-08-24 19:19:51 +00:00
|
|
|
except KeyError as e:
|
|
|
|
log.ODM_DEBUG('Tag not set')
|
2016-09-15 13:15:50 +00:00
|
|
|
except NotImplementedError as e:
|
|
|
|
pass
|
2016-02-26 18:50:12 +00:00
|
|
|
|
2016-09-15 13:15:50 +00:00
|
|
|
if self.camera_make and self.camera_model:
|
|
|
|
self.make_model = sensor_string(self.camera_make, self.camera_model)
|
2016-03-04 19:35:35 +00:00
|
|
|
|
2015-12-02 14:24:38 +00:00
|
|
|
# needed to do that since sometimes metadata contains wrong data
|
|
|
|
img = cv2.imread(_path_file)
|
2015-12-04 14:12:40 +00:00
|
|
|
self.width = img.shape[1]
|
|
|
|
self.height = img.shape[0]
|
2015-12-02 14:24:38 +00:00
|
|
|
|
2015-12-04 14:12:40 +00:00
|
|
|
# force focal and ccd_width with user parameter
|
2016-03-04 19:35:35 +00:00
|
|
|
if _force_focal:
|
|
|
|
self.focal_length = _force_focal
|
|
|
|
if _force_ccd:
|
|
|
|
self.ccd_width = _force_ccd
|
2015-11-26 12:15:02 +00:00
|
|
|
|
2015-11-23 15:43:29 +00:00
|
|
|
# find ccd_width from file if needed
|
2015-11-26 12:15:02 +00:00
|
|
|
if self.ccd_width is None and self.camera_model is not None:
|
2015-11-23 15:43:29 +00:00
|
|
|
# load ccd_widths from file
|
|
|
|
ccd_widths = system.get_ccd_widths()
|
2015-11-26 12:15:02 +00:00
|
|
|
# search ccd by camera model
|
2016-03-04 19:35:35 +00:00
|
|
|
key = [x for x in ccd_widths.keys() if self.make_model in x]
|
2015-11-26 12:15:02 +00:00
|
|
|
# convert to float if found
|
2016-02-26 18:50:12 +00:00
|
|
|
if key:
|
|
|
|
self.ccd_width = float(ccd_widths[key[0]])
|
2016-03-04 19:35:35 +00:00
|
|
|
else:
|
|
|
|
log.ODM_WARNING('Could not find ccd_width in file. Use --force-ccd or edit the sensor_data.json '
|
|
|
|
'file to manually input ccd width')
|
2015-11-27 10:00:08 +00:00
|
|
|
|
2017-06-23 15:49:24 +00:00
|
|
|
def dms_to_decimal(self, degrees, minutes, seconds, sign=' '):
|
|
|
|
"""Converts dms coords to decimal degrees"""
|
|
|
|
return (-1 if sign[0] in 'SWsw' else 1) * (
|
|
|
|
float(degrees) +
|
|
|
|
float(minutes) / 60 +
|
|
|
|
float(seconds) / 3600
|
|
|
|
)
|
|
|
|
|
2015-11-27 10:00:08 +00:00
|
|
|
|
|
|
|
# TODO: finish this class
|
2015-12-10 17:17:39 +00:00
|
|
|
class ODM_Reconstruction(object):
|
|
|
|
"""docstring for ODMReconstruction"""
|
2016-02-26 18:50:12 +00:00
|
|
|
|
2018-01-26 19:38:26 +00:00
|
|
|
def __init__(self, photos, projstring = None, coords_file = None):
|
|
|
|
self.photos = photos # list of ODM_Photos
|
|
|
|
self.projection = None # Projection system the whole project will be in
|
|
|
|
if projstring:
|
|
|
|
self.projection = self.set_projection(projstring)
|
|
|
|
self.georef = ODM_GeoRef(self.projection)
|
|
|
|
else:
|
|
|
|
self.projection = self.parse_coordinate_system(coords_file)
|
|
|
|
self.georef = ODM_GeoRef(self.projection)
|
|
|
|
|
|
|
|
def parse_coordinate_system(self, _file):
|
|
|
|
"""Write attributes to jobOptions from coord file"""
|
|
|
|
# check for coordinate file existence
|
|
|
|
if not io.file_exists(_file):
|
|
|
|
log.ODM_ERROR('Could not find file %s' % _file)
|
|
|
|
return
|
|
|
|
|
|
|
|
with open(_file) as f:
|
|
|
|
# extract reference system and utm zone from first line.
|
|
|
|
# We will assume the following format:
|
|
|
|
# 'WGS84 UTM 17N' or 'WGS84 UTM 17N \n'
|
|
|
|
line = f.readline().rstrip()
|
|
|
|
log.ODM_DEBUG('Line: %s' % line)
|
|
|
|
ref = line.split(' ')
|
|
|
|
# match_wgs_utm = re.search('WGS84 UTM (\d{1,2})(N|S)', line, re.I)
|
|
|
|
if ref[0] == 'WGS84' and ref[1] == 'UTM': # match_wgs_utm:
|
|
|
|
datum = ref[0]
|
|
|
|
utm_pole = ref[2][len(ref[2]) - 1]
|
|
|
|
utm_zone = int(ref[2][:len(ref[2]) - 1])
|
|
|
|
|
|
|
|
return Proj(proj="utm", zone=utm_zone, datum=datum, no_defs=True)
|
|
|
|
elif '+proj' in line:
|
|
|
|
return Proj(line.strip('\''))
|
|
|
|
else:
|
|
|
|
log.ODM_ERROR('Could not parse coordinates. Bad CRS supplied: %s' % line)
|
|
|
|
return
|
|
|
|
|
|
|
|
def set_projection(self, projstring):
|
|
|
|
try:
|
|
|
|
return Proj(projstring)
|
|
|
|
except RuntimeError:
|
|
|
|
log.ODM_EXCEPTION('Could not set projection. Please use a proj4 string')
|
2015-12-10 17:17:39 +00:00
|
|
|
|
|
|
|
|
|
|
|
class ODM_GCPoint(object):
|
|
|
|
"""docstring for ODMPoint"""
|
2016-02-26 18:50:12 +00:00
|
|
|
|
2015-12-10 17:17:39 +00:00
|
|
|
def __init__(self, x, y, z):
|
|
|
|
self.x = x
|
|
|
|
self.y = y
|
|
|
|
self.z = z
|
|
|
|
|
|
|
|
|
|
|
|
class ODM_GeoRef(object):
|
|
|
|
"""docstring for ODMUtmZone"""
|
2016-02-26 18:50:12 +00:00
|
|
|
|
2018-01-26 19:38:26 +00:00
|
|
|
def __init__(self, projection):
|
|
|
|
self.projection = projection
|
2015-12-10 17:17:39 +00:00
|
|
|
self.datum = 'WGS84'
|
2015-12-11 21:26:04 +00:00
|
|
|
self.epsg = None
|
2015-12-10 17:17:39 +00:00
|
|
|
self.utm_zone = 0
|
|
|
|
self.utm_pole = 'N'
|
|
|
|
self.utm_east_offset = 0
|
|
|
|
self.utm_north_offset = 0
|
2018-01-26 19:38:26 +00:00
|
|
|
self.transform = []
|
2015-12-10 17:17:39 +00:00
|
|
|
self.gcps = []
|
|
|
|
|
|
|
|
def calculate_EPSG(self, _utm_zone, _pole):
|
|
|
|
"""Calculate and return the EPSG"""
|
|
|
|
if _pole == 'S':
|
|
|
|
return 32700 + _utm_zone
|
|
|
|
elif _pole == 'N':
|
|
|
|
return 32600 + _utm_zone
|
|
|
|
else:
|
2015-12-11 21:26:04 +00:00
|
|
|
log.ODM_ERROR('Unknown pole format %s' % _pole)
|
2015-12-10 17:17:39 +00:00
|
|
|
return
|
|
|
|
|
2016-04-05 20:10:02 +00:00
|
|
|
def coord_to_fractions(self, coord, refs):
|
|
|
|
deg_dec = abs(float(coord))
|
|
|
|
deg = int(deg_dec)
|
2017-04-06 17:59:26 +00:00
|
|
|
minute_dec = (deg_dec - deg) * 60
|
2016-04-05 20:10:02 +00:00
|
|
|
minute = int(minute_dec)
|
|
|
|
|
2017-04-06 17:59:26 +00:00
|
|
|
sec_dec = (minute_dec - minute) * 60
|
|
|
|
sec_dec = round(sec_dec, 3)
|
2016-04-05 20:10:02 +00:00
|
|
|
sec_denominator = 1000
|
2017-04-06 17:59:26 +00:00
|
|
|
sec_numerator = int(sec_dec * sec_denominator)
|
2016-04-05 20:10:02 +00:00
|
|
|
if float(coord) >= 0:
|
|
|
|
latRef = refs[0]
|
|
|
|
else:
|
|
|
|
latRef = refs[1]
|
|
|
|
|
|
|
|
output = str(deg) + '/1 ' + str(minute) + '/1 ' + str(sec_numerator) + '/' + str(sec_denominator)
|
|
|
|
return output, latRef
|
|
|
|
|
2017-04-06 17:59:26 +00:00
|
|
|
def convert_to_las(self, _file, _file_out, json_file):
|
2015-12-11 21:26:04 +00:00
|
|
|
|
2018-01-26 19:38:26 +00:00
|
|
|
if not self.projection.srs:
|
|
|
|
log.ODM_ERROR('Empty CRS: Could not convert to LAS')
|
2015-12-11 21:26:04 +00:00
|
|
|
return
|
|
|
|
|
2016-02-26 18:50:12 +00:00
|
|
|
kwargs = {'bin': context.pdal_path,
|
|
|
|
'f_in': _file,
|
2017-04-06 13:06:09 +00:00
|
|
|
'f_out': _file_out,
|
2016-02-26 18:50:12 +00:00
|
|
|
'east': self.utm_east_offset,
|
|
|
|
'north': self.utm_north_offset,
|
2018-01-26 19:38:26 +00:00
|
|
|
'srs': self.projection.srs,
|
2017-04-06 17:59:26 +00:00
|
|
|
'json': json_file}
|
2015-12-11 21:26:04 +00:00
|
|
|
|
2016-02-25 15:36:18 +00:00
|
|
|
# create pipeline file transform.xml to enable transformation
|
2017-04-06 17:59:26 +00:00
|
|
|
pipeline = '{{' \
|
|
|
|
' "pipeline":[' \
|
|
|
|
' "untransformed.ply",' \
|
|
|
|
' {{' \
|
|
|
|
' "type":"filters.transformation",' \
|
|
|
|
' "matrix":"1 0 0 {east} 0 1 0 {north} 0 0 1 0 0 0 0 1"' \
|
|
|
|
' }},' \
|
|
|
|
' {{' \
|
2018-01-26 19:38:26 +00:00
|
|
|
' "a_srs":"{srs}",' \
|
2017-05-08 14:50:16 +00:00
|
|
|
' "offset_x":"{east}",' \
|
|
|
|
' "offset_y":"{north}",' \
|
|
|
|
' "offset_z":"0",' \
|
2017-04-06 17:59:26 +00:00
|
|
|
' "filename":"transformed.las"' \
|
|
|
|
' }}' \
|
|
|
|
' ]' \
|
|
|
|
'}}'.format(**kwargs)
|
|
|
|
|
|
|
|
with open(json_file, 'w') as f:
|
|
|
|
f.write(pipeline)
|
2016-02-25 16:16:42 +00:00
|
|
|
|
2016-02-25 15:36:18 +00:00
|
|
|
# call pdal
|
2017-04-06 17:59:26 +00:00
|
|
|
system.run('{bin}/pdal pipeline -i {json} --readers.ply.filename={f_in} '
|
2016-02-25 19:05:18 +00:00
|
|
|
'--writers.las.filename={f_out}'.format(**kwargs))
|
2015-12-11 21:26:04 +00:00
|
|
|
|
2018-01-26 19:38:26 +00:00
|
|
|
def utm_to_latlon(self, _file, _photo, idx):
|
|
|
|
|
|
|
|
gcp = self.gcps[idx]
|
|
|
|
|
|
|
|
kwargs = {'proj': self.projection,
|
|
|
|
'file': _file,
|
|
|
|
'x': gcp.x + self.utm_east_offset,
|
|
|
|
'y': gcp.y + self.utm_north_offset,
|
|
|
|
'z': gcp.z}
|
|
|
|
|
|
|
|
latlon = system.run_and_return('echo {x} {y} {z} '.format(**kwargs),
|
|
|
|
'gdaltransform -s_srs \"{proj}\" '
|
|
|
|
'-t_srs \"EPSG:4326\"'.format(**kwargs)).split()
|
|
|
|
|
|
|
|
# Example: 83d18'16.285"W
|
|
|
|
# Example: 41d2'11.789"N
|
|
|
|
# Example: 0.998
|
|
|
|
|
|
|
|
if len(latlon) == 3:
|
|
|
|
lon_str, lat_str, alt_str = latlon
|
|
|
|
elif len(latlon) == 2:
|
|
|
|
lon_str, lat_str = latlon
|
|
|
|
alt_str = ''
|
|
|
|
else:
|
|
|
|
log.ODM_ERROR('Something went wrong %s' % latlon)
|
|
|
|
|
|
|
|
lat_frac = self.coord_to_fractions(latlon[1], ['N', 'S'])
|
|
|
|
lon_frac = self.coord_to_fractions(latlon[0], ['E', 'W'])
|
|
|
|
|
|
|
|
# read image metadata
|
|
|
|
metadata = pyexiv2.ImageMetadata(_photo.path_file)
|
|
|
|
metadata.read()
|
|
|
|
|
|
|
|
# #set values
|
|
|
|
#
|
|
|
|
# # GPS latitude
|
|
|
|
# key = 'Exif.GPSInfo.GPSLatitude'
|
|
|
|
# value = lat_frac[0].split(' ')
|
|
|
|
# log.ODM_DEBUG('lat_frac: %s %s %s' % (value[0], value[1], value[2]))
|
|
|
|
# metadata[key] = pyexiv2.ExifTag(key,
|
|
|
|
# [Fraction(value[0]),
|
|
|
|
# Fraction(value[1]),
|
|
|
|
# Fraction(value[2])])
|
|
|
|
#
|
|
|
|
# key = 'Exif.GPSInfo.GPSLatitudeRef'
|
|
|
|
# value = lat_frac[1]
|
|
|
|
# metadata[key] = pyexiv2.ExifTag(key, value)
|
|
|
|
#
|
|
|
|
# # GPS longitude
|
|
|
|
# key = 'Exif.GPSInfo.GPSLongitude'
|
|
|
|
# value = lon_frac[0].split(' ')
|
|
|
|
# metadata[key] = pyexiv2.ExifTag(key,
|
|
|
|
# [Fraction(value[0]),
|
|
|
|
# Fraction(value[1]),
|
|
|
|
# Fraction(value[2])])
|
|
|
|
#
|
|
|
|
# key = 'Exif.GPSInfo.GPSLongitudeRef'
|
|
|
|
# value = lon_frac[1]
|
|
|
|
# metadata[key] = pyexiv2.ExifTag(key, value)
|
|
|
|
#
|
|
|
|
# # GPS altitude
|
|
|
|
# altitude = abs(int(float(latlon[2]) * 100))
|
|
|
|
# key = 'Exif.GPSInfo.GPSAltitude'
|
|
|
|
# value = Fraction(altitude, 1)
|
|
|
|
# metadata[key] = pyexiv2.ExifTag(key, value)
|
|
|
|
#
|
|
|
|
# if latlon[2] >= 0:
|
|
|
|
# altref = '0'
|
|
|
|
# else:
|
|
|
|
# altref = '1'
|
|
|
|
# key = 'Exif.GPSInfo.GPSAltitudeRef'
|
|
|
|
# metadata[key] = pyexiv2.ExifTag(key, altref)
|
|
|
|
#
|
|
|
|
# # write values
|
|
|
|
# metadata.write()
|
|
|
|
|
|
|
|
def extract_offsets(self, _file):
|
2015-12-10 17:17:39 +00:00
|
|
|
if not io.file_exists(_file):
|
2016-02-23 17:47:43 +00:00
|
|
|
log.ODM_ERROR('Could not find file %s' % _file)
|
2015-12-10 17:17:39 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
with open(_file) as f:
|
2018-01-26 19:38:26 +00:00
|
|
|
offsets = f.readlines()[1].split(' ')
|
2016-04-05 20:10:02 +00:00
|
|
|
self.utm_east_offset = int(offsets[0])
|
|
|
|
self.utm_north_offset = int(offsets[1])
|
|
|
|
|
2018-01-26 19:38:26 +00:00
|
|
|
def create_gcps(self, _file):
|
|
|
|
if not io.file_exists(_file):
|
|
|
|
log.ODM_ERROR('Could not find file %s' % _file)
|
|
|
|
return
|
|
|
|
|
|
|
|
with open(_file) as f:
|
2015-12-10 17:17:39 +00:00
|
|
|
# parse coordinates
|
2018-01-26 19:38:26 +00:00
|
|
|
lines = f.readlines()[2:]
|
2015-12-10 17:17:39 +00:00
|
|
|
for l in lines:
|
2016-04-05 20:10:02 +00:00
|
|
|
xyz = l.split(' ')
|
|
|
|
if len(xyz) == 3:
|
|
|
|
x, y, z = xyz[:3]
|
|
|
|
elif len(xyz) == 2:
|
|
|
|
x, y = xyz[:2]
|
|
|
|
z = 0
|
2015-12-10 17:17:39 +00:00
|
|
|
self.gcps.append(ODM_GCPoint(float(x), float(y), float(z)))
|
2017-04-06 17:59:26 +00:00
|
|
|
# Write to json file
|
2015-12-10 17:17:39 +00:00
|
|
|
|
2018-01-26 19:38:26 +00:00
|
|
|
def parse_transformation_matrix(self, _file):
|
|
|
|
if not io.file_exists(_file):
|
|
|
|
log.ODM_ERROR('Could not find file %s' % _file)
|
|
|
|
return
|
|
|
|
|
|
|
|
# Create a nested list for the transformation matrix
|
|
|
|
with open(_file) as f:
|
|
|
|
for line in f:
|
|
|
|
self.transform += [[float(i) for i in line.split()]]
|
|
|
|
|
|
|
|
self.utm_east_offset = self.transform[0][3]
|
|
|
|
self.utm_north_offset = self.transform[1][3]
|
|
|
|
|
2016-02-25 20:02:48 +00:00
|
|
|
|
2015-12-10 17:17:39 +00:00
|
|
|
class ODM_Tree(object):
|
2016-12-09 14:51:25 +00:00
|
|
|
def __init__(self, root_path, images_path):
|
2016-02-26 18:50:12 +00:00
|
|
|
# root path to the project
|
2015-12-02 14:24:38 +00:00
|
|
|
self.root_path = io.absolute_path_file(root_path)
|
2016-12-09 14:51:25 +00:00
|
|
|
if not images_path:
|
|
|
|
self.input_images = io.join_paths(self.root_path, 'images')
|
|
|
|
else:
|
|
|
|
self.input_images = io.absolute_path_file(images_path)
|
2015-12-02 14:24:38 +00:00
|
|
|
|
2016-02-26 18:50:12 +00:00
|
|
|
# modules paths
|
2015-12-02 14:24:38 +00:00
|
|
|
|
|
|
|
# here are defined where all modules should be located in
|
|
|
|
# order to keep track all files al directories during the
|
|
|
|
# whole reconstruction process.
|
|
|
|
self.dataset_raw = io.join_paths(self.root_path, 'images')
|
|
|
|
self.opensfm = io.join_paths(self.root_path, 'opensfm')
|
|
|
|
self.pmvs = io.join_paths(self.root_path, 'pmvs')
|
|
|
|
self.odm_meshing = io.join_paths(self.root_path, 'odm_meshing')
|
|
|
|
self.odm_texturing = io.join_paths(self.root_path, 'odm_texturing')
|
2017-04-05 17:56:48 +00:00
|
|
|
self.odm_25dtexturing = io.join_paths(self.root_path, 'odm_texturing_25d')
|
2015-12-02 14:24:38 +00:00
|
|
|
self.odm_georeferencing = io.join_paths(self.root_path, 'odm_georeferencing')
|
2017-04-05 17:56:48 +00:00
|
|
|
self.odm_25dgeoreferencing = io.join_paths(self.root_path, 'odm_25dgeoreferencing')
|
2015-12-02 14:24:38 +00:00
|
|
|
self.odm_orthophoto = io.join_paths(self.root_path, 'odm_orthophoto')
|
2016-02-25 16:29:00 +00:00
|
|
|
self.odm_pdal = io.join_paths(self.root_path, 'pdal')
|
2015-12-02 14:24:38 +00:00
|
|
|
|
2016-02-26 18:50:12 +00:00
|
|
|
# important files paths
|
|
|
|
|
2016-02-29 14:45:00 +00:00
|
|
|
# benchmarking
|
|
|
|
self.benchmarking = io.join_paths(self.root_path, 'benchmark.txt')
|
2018-01-26 19:38:26 +00:00
|
|
|
self.dataset_list = io.join_paths(self.root_path, 'img_list.txt')
|
2016-02-29 14:45:00 +00:00
|
|
|
|
2015-12-02 14:24:38 +00:00
|
|
|
# opensfm
|
2017-03-22 22:22:24 +00:00
|
|
|
self.opensfm_tracks = io.join_paths(self.opensfm, 'tracks.csv')
|
2015-12-02 14:24:38 +00:00
|
|
|
self.opensfm_bundle = io.join_paths(self.opensfm, 'bundle_r000.out')
|
|
|
|
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')
|
2017-04-04 16:54:40 +00:00
|
|
|
self.opensfm_reconstruction_meshed = io.join_paths(self.opensfm, 'reconstruction.meshed.json')
|
2017-04-06 05:31:03 +00:00
|
|
|
self.opensfm_reconstruction_nvm = io.join_paths(self.opensfm, 'reconstruction.nvm')
|
2016-09-30 13:08:56 +00:00
|
|
|
self.opensfm_model = io.join_paths(self.opensfm, 'depthmaps/merged.ply')
|
2018-01-26 19:38:26 +00:00
|
|
|
self.opensfm_transformation = io.join_paths(self.opensfm, 'geocoords_transformation.txt')
|
2015-12-10 11:01:41 +00:00
|
|
|
|
2015-12-02 14:24:38 +00:00
|
|
|
# pmvs
|
2015-12-10 11:01:41 +00:00
|
|
|
self.pmvs_rec_path = io.join_paths(self.pmvs, 'recon0')
|
|
|
|
self.pmvs_bundle = io.join_paths(self.pmvs_rec_path, 'bundle.rd.out')
|
|
|
|
self.pmvs_visdat = io.join_paths(self.pmvs_rec_path, 'vis.dat')
|
|
|
|
self.pmvs_options = io.join_paths(self.pmvs_rec_path, 'pmvs_options.txt')
|
|
|
|
self.pmvs_model = io.join_paths(self.pmvs_rec_path, 'models/option-0000.ply')
|
2016-02-26 18:50:12 +00:00
|
|
|
|
2015-12-02 14:24:38 +00:00
|
|
|
# odm_meshing
|
2015-12-10 11:01:41 +00:00
|
|
|
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')
|
2017-04-05 17:56:48 +00:00
|
|
|
self.odm_25dmesh = io.join_paths(self.odm_meshing, 'odm_25dmesh.ply')
|
|
|
|
self.odm_25dmeshing_log = io.join_paths(self.odm_meshing, 'odm_25dmeshing_log.txt')
|
2016-02-26 18:50:12 +00:00
|
|
|
|
2016-03-24 17:35:29 +00:00
|
|
|
# texturing
|
2016-03-03 11:58:25 +00:00
|
|
|
self.odm_texturing_undistorted_image_path = io.join_paths(
|
|
|
|
self.odm_texturing, 'undistorted')
|
2017-04-05 17:56:48 +00:00
|
|
|
self.odm_textured_model_obj = 'odm_textured_model.obj'
|
|
|
|
self.odm_textured_model_mtl = 'odm_textured_model.mtl'
|
|
|
|
# Log is only used by old odm_texturing
|
|
|
|
self.odm_texuring_log = 'odm_texturing_log.txt'
|
2015-12-10 11:01:41 +00:00
|
|
|
|
|
|
|
# odm_georeferencing
|
2015-12-10 17:17:39 +00:00
|
|
|
self.odm_georeferencing_latlon = io.join_paths(
|
|
|
|
self.odm_georeferencing, 'latlon.txt')
|
2015-12-10 11:01:41 +00:00
|
|
|
self.odm_georeferencing_coords = io.join_paths(
|
2018-01-26 19:38:26 +00:00
|
|
|
self.root_path, 'coords.txt') # Todo put this somewhere better
|
|
|
|
self.odm_georeferencing_gcp = io.find('gcp_list.txt', self.root_path)
|
2015-12-10 11:01:41 +00:00
|
|
|
self.odm_georeferencing_utm_log = io.join_paths(
|
|
|
|
self.odm_georeferencing, 'odm_georeferencing_utm_log.txt')
|
2017-04-05 17:56:48 +00:00
|
|
|
self.odm_georeferencing_log = 'odm_georeferencing_log.txt'
|
2017-06-12 11:15:32 +00:00
|
|
|
self.odm_georeferencing_transform_file = 'odm_georeferencing_transform.txt'
|
2017-04-05 17:56:48 +00:00
|
|
|
self.odm_georeferencing_model_txt_geo = 'odm_georeferencing_model_geo.txt'
|
|
|
|
self.odm_georeferencing_model_ply_geo = 'odm_georeferenced_model.ply'
|
|
|
|
self.odm_georeferencing_model_obj_geo = 'odm_textured_model_geo.obj'
|
2016-02-25 19:51:03 +00:00
|
|
|
self.odm_georeferencing_xyz_file = io.join_paths(
|
2016-02-25 20:02:48 +00:00
|
|
|
self.odm_georeferencing, 'odm_georeferenced_model.csv')
|
2017-04-06 17:59:26 +00:00
|
|
|
self.odm_georeferencing_las_json = io.join_paths(
|
|
|
|
self.odm_georeferencing, 'las.json')
|
2017-04-06 13:06:09 +00:00
|
|
|
self.odm_georeferencing_model_las = io.join_paths(
|
|
|
|
self.odm_georeferencing, 'odm_georeferenced_model.las')
|
|
|
|
self.odm_georeferencing_dem = io.join_paths(
|
|
|
|
self.odm_georeferencing, 'odm_georeferencing_model_dem.tif')
|
2015-12-10 11:01:41 +00:00
|
|
|
|
2015-12-02 14:24:38 +00:00
|
|
|
# odm_orthophoto
|
2015-12-10 11:01:41 +00:00
|
|
|
self.odm_orthophoto_file = io.join_paths(self.odm_orthophoto, 'odm_orthophoto.png')
|
2016-02-23 17:47:43 +00:00
|
|
|
self.odm_orthophoto_tif = io.join_paths(self.odm_orthophoto, 'odm_orthophoto.tif')
|
2016-08-10 22:46:19 +00:00
|
|
|
self.odm_orthophoto_corners = io.join_paths(self.odm_orthophoto, 'odm_orthophoto_corners.txt')
|
2015-12-10 11:01:41 +00:00
|
|
|
self.odm_orthophoto_log = io.join_paths(self.odm_orthophoto, 'odm_orthophoto_log.txt')
|
2016-02-23 18:23:34 +00:00
|
|
|
self.odm_orthophoto_tif_log = io.join_paths(self.odm_orthophoto, 'gdal_translate_log.txt')
|
2017-03-31 18:53:47 +00:00
|
|
|
self.odm_orthophoto_gdaladdo_log = io.join_paths(self.odm_orthophoto, 'gdaladdo_log.txt')
|
2017-06-23 15:20:46 +00:00
|
|
|
|
2017-06-23 20:15:13 +00:00
|
|
|
def path(self, *args):
|
2017-09-03 00:32:25 +00:00
|
|
|
return io.join_paths(self.root_path, *args)
|