826 lines
30 KiB
Python
826 lines
30 KiB
Python
# AutoFTG - Scripts for Agisoft Metashape Pro
|
|
#
|
|
# This is an assembly of python scripts for process automation, and some existing scripts from other users
|
|
#
|
|
# Scripts were written for use in work process on project 2TDK, construction of railroad tunnels in Slovenia,
|
|
# but were later modified to support any kind of project where lots of processing is needed.
|
|
#
|
|
# Author: Boris Bilc
|
|
#
|
|
# Script repository (GitHub):
|
|
# ---------------------------
|
|
# URL: https://github.com/bilkos/AutoFTG-Scripts_Metashape-Pro
|
|
#
|
|
#
|
|
# References:
|
|
# -----------
|
|
#
|
|
# Copy Bounding Box Script:
|
|
# - https://github.com/agisoft-llc/metashape-scripts/blob/master/src/copy_bounding_box_dialog.py
|
|
# Copies bounding boxes from chunk to other chunks.
|
|
#
|
|
#
|
|
# If you add or change resorces (icons, images, etc... in qtresorces.qrc), then you need to re-compile qtresorces.qrc file.
|
|
# To do that you need to navigate to script folder and run following command:
|
|
#
|
|
# pyside2-rcc -o resource.py qtresources.qrc
|
|
#
|
|
# If you are using VSCode that you can modify and compile UI elements with PySide2-VSC extension.
|
|
#
|
|
# Scripts are free to use for commercial or non-commercial use.
|
|
#
|
|
# Please use 'Issues' page in GitHub repository to report any bugs, and suggestions for improvements.
|
|
# Link to issues page: https://github.com/bilkos/AutoFTG-Scripts_Metashape-Pro/issues
|
|
|
|
|
|
|
|
import os
|
|
import pydoc
|
|
import shutil
|
|
import sys
|
|
import time
|
|
from configparser import ConfigParser
|
|
from datetime import datetime
|
|
from os import path
|
|
|
|
import Metashape
|
|
from PySide2 import QtCore, QtGui, QtWidgets
|
|
from PySide2.QtCore import *
|
|
from PySide2.QtGui import *
|
|
from PySide2.QtWidgets import *
|
|
|
|
import AutoFTG.autoftg_batch
|
|
import AutoFTG.autoftg_chunkquickadd
|
|
import AutoFTG.autoftg_copyregion
|
|
import AutoFTG.autoftg_settingscamedit
|
|
import AutoFTG.autoftg_settingschunk
|
|
import AutoFTG.autoftg_settingsmain
|
|
import AutoFTG.qtresources_rc2
|
|
from AutoFTG.autoftg_batch import *
|
|
from AutoFTG.autoftg_chunkquickadd import *
|
|
from AutoFTG.autoftg_copyregion import *
|
|
from AutoFTG.autoftg_settingscamedit import *
|
|
from AutoFTG.autoftg_settingschunk import *
|
|
from AutoFTG.autoftg_settingsmain import *
|
|
from AutoFTG.qtresources_rc2 import *
|
|
|
|
# App info
|
|
app_name = "AutoFTG"
|
|
app_ver = "2.6.4"
|
|
appsettings_ver = "6"
|
|
app_author = "Author: Boris Bilc\n\n"
|
|
app_repo = "Repository URL:\nhttps://github.com/bilkos/AutoFTG-Scripts_Metashape-Pro"
|
|
ref_repo = "Agisoft GitHub repository:\nhttps://github.com/agisoft-llc/metashape-scripts"
|
|
ref_scripts = "Copy Bounding Box Script:\nhttps://github.com/agisoft-llc/metashape-scripts/blob/master/src/copy_bounding_box_dialog.py"
|
|
app_about = "Scripts for process automation in Agisoft Metashape Pro\n\nThis is an assembly of existing scripts from other users,\nand some additional scripts written for use in work process at project 2TIR, tunnel T8-KP in Slovenia."
|
|
|
|
|
|
# Check compatibility with Metashape
|
|
compatible_major_version = "2.0"
|
|
found_major_version = ".".join(Metashape.app.version.split('.')[:2])
|
|
if found_major_version != compatible_major_version:
|
|
raise Exception("Incompatible Metashape version: {} != {}".format(found_major_version, compatible_major_version))
|
|
|
|
|
|
projectOpened = False
|
|
settingsRebuild = False
|
|
selected_data_folder = ''
|
|
selected_camera = "No Calibration - Frame (Default)"
|
|
selected_pre = ''
|
|
selected_suf = ''
|
|
selected_menu = ''
|
|
|
|
|
|
# Load icons settings
|
|
icoCfg = ConfigParser()
|
|
icoCfgFile = 'settings_icons.ini'
|
|
icoCfgPath = os.path.expanduser('~\AppData\Local\Agisoft\Metashape Pro\scripts\AutoFTG\\').replace("\\", "/")
|
|
icoCfgFilePath = icoCfgPath + icoCfgFile
|
|
icons_list = []
|
|
|
|
|
|
def loadIcoSettings():
|
|
global icons_list
|
|
icoCfgFileExists = os.path.isfile(icoCfgFilePath) # Check if settings file exists
|
|
if icoCfgFileExists == False:
|
|
icoCfg.add_section("ICONS")
|
|
icoCfg.set("ICONS", "ico-0", "icons8-xbox-cross-96.png")
|
|
icoCfg.set("ICONS", "ico-1", "icons8-product-documents-50.png")
|
|
icoCfg.set("ICONS", "ico-2", "icons8-documents-folder-50-2.png")
|
|
icoCfg.set("ICONS", "ico-3", "icons8-documents-folder-50.png")
|
|
icoCfg.set("ICONS", "ico-4", "icons8-dossier-50.png")
|
|
icoCfg.set("ICONS", "ico-5", "icons8-pictures-folder-50-2.png")
|
|
icoCfg.set("ICONS", "ico-6", "icons8-folded-booklet-50.png")
|
|
icoCfg.set("ICONS", "ico-7", "icons8-images-folder-50.png")
|
|
icoCfg.set("ICONS", "ico-8", "icons8-full-image-50.png")
|
|
icoCfg.set("ICONS", "ico-9", "icons8-video-folder-50.png")
|
|
icoCfg.set("ICONS", "ico-10", "icons8-ftp-50.png")
|
|
icoCfg.set("ICONS", "ico-11", "icons8-web-camera-50.png")
|
|
icoCfg.set("ICONS", "ico-12", "icons8-camera-on-tripod-96.png")
|
|
icoCfg.set("ICONS", "ico-13", "icons8-sd-50.png")
|
|
icoCfg.set("ICONS", "ico-14", "icons8-quadcopter-50.png")
|
|
icoCfg.set("ICONS", "ico-15", "icons8-plane-48.png")
|
|
icoCfg.set("ICONS", "ico-16", "icons8-national-park-48.png")
|
|
icoCfg.set("ICONS", "ico-17", "icons8-ground-48.png")
|
|
icoCfg.set("ICONS", "ico-18", "icons8-country-48.png")
|
|
icoCfg.set("ICONS", "ico-19", "icons8-subway-50.png")
|
|
icoCfg.set("ICONS", "ico-20", "icons8-underground-50.png")
|
|
icoCfg.set("ICONS", "ico-21", "icons8-land-surveying-48.png")
|
|
icoCfg.set("ICONS", "ico-22", "icons8-drawing-compass-48.png")
|
|
icoCfg.set("ICONS", "ico-23", "icons8-camera-50.png")
|
|
icoCfg.set("ICONS", "ico-24", "menu_kalota-modra.png")
|
|
icoCfg.set("ICONS", "ico-25", "menu_kalota-oranzna.png")
|
|
icoCfg.set("ICONS", "ico-26", "template_kalota-modra.png")
|
|
icoCfg.set("ICONS", "ico-27", "template_kalota-oranzna.png")
|
|
icoCfg.set("ICONS", "ico-28", "template_kalota-rdeca.png")
|
|
icoCfg.set("ICONS", "ico-29", "template_kalota-vijola.png")
|
|
icoCfg.set("ICONS", "ico-30", "template_kalota-zelena.png")
|
|
icoCfg.set("ICONS", "ico-31", "template_stopnica-modra.png")
|
|
icoCfg.set("ICONS", "ico-32", "template_stopnica-oranzna.png")
|
|
icoCfg.set("ICONS", "ico-33", "template_stopnica-rdeca.png")
|
|
icoCfg.set("ICONS", "ico-34", "template_stopnica-vijola.png")
|
|
icoCfg.set("ICONS", "ico-35", "template_stopnica-zelena.png")
|
|
|
|
with open(icoCfgFilePath, 'w') as icoconfig:
|
|
camCfg.write(icoconfig)
|
|
|
|
icoCfgFileExists = True
|
|
|
|
icoCfg.read(icoCfgFilePath)
|
|
icons_list = icoCfg.options("ICONS")
|
|
print("Loading chunk definition icons... OK.")
|
|
|
|
|
|
# Load main app settings
|
|
appCfg = ConfigParser()
|
|
appCfgFile = 'settings_autoftg.ini'
|
|
appCfgPath = os.path.expanduser('~\AppData\Local\Agisoft\Metashape Pro\scripts\AutoFTG\\').replace("\\", "/")
|
|
appCfgFilePath = appCfgPath + appCfgFile
|
|
appCfgFileExists = os.path.isfile(appCfgFilePath) # Check if settings file exists
|
|
|
|
|
|
# Load custom menu settings
|
|
menuCfg = ConfigParser()
|
|
menuCfgFile = "settings_newchunk.ini"
|
|
menuCfgPath = os.path.expanduser('~\AppData\Local\Agisoft\Metashape Pro\scripts\AutoFTG').replace("\\", "/")
|
|
menuCfgFilePath = menuCfgPath + "/" + menuCfgFile
|
|
menuCfgFilePathExists = os.path.isfile(menuCfgFilePath)
|
|
chunk_sections = []
|
|
|
|
|
|
# Init configparser for camera settings. Set empty camera variables for global use.
|
|
camCfg = ConfigParser()
|
|
cam_name = ''
|
|
cam_desc = ''
|
|
cam_type = ''
|
|
cam_subtype = ''
|
|
cam_res = ''
|
|
cam_file = ''
|
|
|
|
cameraXmlSource = ''
|
|
cameraXmlDest = ''
|
|
|
|
camCfgFile = "settings_cam.ini"
|
|
camCfgPath = os.path.expanduser('~\AppData\Local\Agisoft\Metashape Pro\scripts\AutoFTG\cameras\\').replace("\\", "/")
|
|
camCfgFilePath = camCfgPath + camCfgFile
|
|
camCfgFilePathExists = os.path.isfile(camCfgFilePath)
|
|
cam_list = []
|
|
|
|
projCfg = ConfigParser() # INICALIZACIJA NASTAVITEV
|
|
|
|
|
|
def camCfgLoad():
|
|
global cam_list
|
|
|
|
if camCfgFilePathExists == False:
|
|
defcam_name = 'No Calibration - Frame (Default)'
|
|
defcam_description = 'Default Metashape camera settings for type FRAME. Calibration is calculated On-The-Fly.'
|
|
defcam_type = 'Frame'
|
|
defcam_subtype = 'Standard'
|
|
defcam_resolution = '0'
|
|
defcam_file = 'None'
|
|
|
|
defcam2_name = 'No Calibration - Fisheye'
|
|
defcam2_description = 'Default Metashape camera settings for type FISHEYE. Calibration is calculated On-The-Fly.'
|
|
defcam2_type = 'Fisheye'
|
|
defcam2_subtype = 'Standard'
|
|
defcam2_resolution = '0'
|
|
defcam2_file = 'None'
|
|
|
|
camCfg.add_section(defcam_name)
|
|
camCfg.set(defcam_name, "description", defcam_description)
|
|
camCfg.set(defcam_name, "type", defcam_type)
|
|
camCfg.set(defcam_name, "subtype", defcam_subtype)
|
|
camCfg.set(defcam_name, "resolution", defcam_resolution)
|
|
camCfg.set(defcam_name, "file", defcam_file)
|
|
camCfg.add_section(defcam2_name)
|
|
camCfg.set(defcam2_name, "description", defcam2_description)
|
|
camCfg.set(defcam2_name, "type", defcam2_type)
|
|
camCfg.set(defcam2_name, "subtype", defcam2_subtype)
|
|
camCfg.set(defcam2_name, "resolution", defcam2_resolution)
|
|
camCfg.set(defcam2_name, "file", defcam2_file)
|
|
|
|
with open(camCfgFilePath, 'w') as camconfigfile:
|
|
camCfg.write(camconfigfile)
|
|
|
|
camCfg.read(camCfgFilePath)
|
|
cam_list = camCfg.sections()
|
|
print("Camera settings loaded...")
|
|
|
|
|
|
# Read camera settings from INI config file
|
|
def readCameraSettings(cam_section):
|
|
global cam_name
|
|
global cam_desc
|
|
global cam_type
|
|
global cam_subtype
|
|
global cam_res
|
|
global cam_file
|
|
|
|
# Read settings for requested camera
|
|
cam_name = cam_section
|
|
cam_desc = camCfg.get(cam_section, "Description")
|
|
cam_type = camCfg.get(cam_section, "Type")
|
|
cam_subtype = camCfg.get(cam_section, "SubType")
|
|
cam_res = camCfg.get(cam_section, "Resolution")
|
|
cam_file = camCfg.get(cam_section, "File")
|
|
print("Using camera\n" + "Name: " + cam_name + "\nDesc.: " + cam_desc + "\nType: " + cam_type + "\nSubType: " + cam_subtype + "\nResolution: " + cam_res + "\nFile: " + cam_file)
|
|
|
|
|
|
# Called to apply camera settings when creating new chunk
|
|
def useCameraSettings():
|
|
# Init document
|
|
doc = Metashape.app.document
|
|
chunk = doc.chunk
|
|
# readCameraSettings(settings.defaultCamera)
|
|
camera_path = camCfgPath + cam_file
|
|
|
|
# Sensor to which we will apply settings
|
|
chunk_sensor = chunk.sensors[0]
|
|
|
|
# Set sensor type from camera
|
|
if cam_type == "Fisheye":
|
|
chunk_sensor.type = Metashape.Sensor.Type.Fisheye
|
|
elif cam_type == "Frame":
|
|
chunk_sensor.type = Metashape.Sensor.Type.Frame
|
|
elif cam_type == "Spherical":
|
|
chunk_sensor.type = Metashape.Sensor.Type.Spherical
|
|
elif cam_type == "Cylindrical":
|
|
chunk_sensor.type = Metashape.Sensor.Type.Cylindrical
|
|
elif cam_type == "RPC":
|
|
chunk_sensor.type = Metashape.Sensor.Type.RPC
|
|
else:
|
|
Metashape.app.messagBox("Camera Type not recognized.\nPlease check camera settings...")
|
|
|
|
# Init calibration and import settings from camera calibration file (Metashape XML)
|
|
chunk_calib = Metashape.Calibration()
|
|
if cam_file != "None":
|
|
chunk_calib.load(path=camera_path, format=Metashape.CalibrationFormatXML)
|
|
chunk_sensor.user_calib = chunk_calib
|
|
|
|
# Save document and show message with applied settings
|
|
doc.save()
|
|
# Metashape.app.messageBox("Camera settings applied.\n\nCamera: " + cam_name + "\nType: " + cam_type + "\nFilename: " + cam_file)
|
|
Metashape.app.update()
|
|
|
|
|
|
def selectCamChunk():
|
|
camCfgLoad()
|
|
diaSelectCamera()
|
|
if selected_camera == None:
|
|
print("No camera chosen. Using default camera.")
|
|
else:
|
|
readCameraSettings(selected_camera)
|
|
useCameraSettings()
|
|
print("\n\nApplied custom camera: " + selected_camera)
|
|
|
|
|
|
# Routine for adding/editing camera configuration
|
|
def saveCamConfig(camorig, camname, camdesc, camtype, camsub, camres, camfile):
|
|
if camCfg.has_section(camorig) == True:
|
|
camCfg.remove_section(camorig)
|
|
camCfg.add_section(camname)
|
|
camCfg.set(camname, "Description", camdesc)
|
|
camCfg.set(camname, "Type", camtype)
|
|
camCfg.set(camname, "SubType", camsub)
|
|
camCfg.set(camname, "Resolution", camres)
|
|
camCfg.set(camname, "File", camfile)
|
|
Metashape.app.messageBox("Camera Updated\n\n" + "Name: " + camname + "\nDesc.: " + camdesc + "\nType: " + camtype + "\nSubType: " + camsub + "\nRes.:: " + camres + " MP\nFile: " + camfile)
|
|
else:
|
|
camCfg.add_section(camname)
|
|
camCfg.set(camname, "Description", camdesc)
|
|
camCfg.set(camname, "Type", camtype)
|
|
camCfg.set(camname, "SubType", camsub)
|
|
camCfg.set(camname, "Resolution", camres)
|
|
camCfg.set(camname, "File", camfile)
|
|
Metashape.app.messageBox("Camera Created\n\n" + "Name: " + camname + "\nDesc.: " + camdesc + "\nType: " + camtype + "\nSubType: " + camsub + "\nRes.:: " + camres + " MP\nFile: " + camfile)
|
|
|
|
with open(camCfgFilePath, 'w') as configfile:
|
|
camCfg.write(configfile)
|
|
|
|
camCfgLoad()
|
|
|
|
|
|
# Routine for adding/editing camera configuration
|
|
def removeCamConfig(camname):
|
|
cam_xmlmsg = ''
|
|
if camCfg.has_section(camname) == True:
|
|
if camCfg.get(camname, "File") != "":
|
|
cameraXml = camCfgPath + camCfg.get(camname, "File")
|
|
if os.path.isfile(cameraXml):
|
|
os.remove(cameraXml)
|
|
cam_xmlmsg = "\nXML file " + cameraXml + " deleted."
|
|
|
|
camCfg.remove_section(camname)
|
|
cam_secmsg = "Camera [" + camname + "] removed from settings." + cam_xmlmsg
|
|
|
|
with open(camCfgFilePath, 'w') as configfile:
|
|
camCfg.write(configfile)
|
|
|
|
camCfgLoad()
|
|
|
|
Metashape.app.messageBox(cam_secmsg)
|
|
else:
|
|
Metashape.app.messageBox("Error! No camera named (" + str(camname) + ") was found.\n\nDid you manualy edit comaera configuration?")
|
|
|
|
|
|
def chunksCfgLoad():
|
|
global chunk_sections
|
|
if menuCfgFilePathExists == False:
|
|
menu_section_m = "GENERAL"
|
|
menuCfg.add_section(menu_section_m)
|
|
menuCfg.set(menu_section_m, "menu_icon", "ico-0")
|
|
menuCfg.set(menu_section_m, "chunk_name_format", "metashape")
|
|
menuCfg.set(menu_section_m, "chunk_name_prefix", "")
|
|
menuCfg.set(menu_section_m, "chunk_name_suffix", "")
|
|
menuCfg.set(menu_section_m, "work_folder", str(Metashape.app.getExistingDirectory("Project data folder (batch)")))
|
|
menuCfg.set(menu_section_m, "export_folder", str(Metashape.app.getExistingDirectory("Project data export folder (batch)")))
|
|
|
|
with open(menuCfgFilePath, 'w') as menuconfig:
|
|
menuCfg.write(menuconfig)
|
|
|
|
menuCfg.read(menuCfgFilePath)
|
|
chunk_sections = menuCfg.sections()
|
|
print("Custom chunk settings loaded...\nFile: " + menuCfgFile)
|
|
|
|
|
|
def appCfgLoad():
|
|
global selected_data_folder
|
|
global selected_camera
|
|
appCfgFileExists = os.path.isfile(appCfgFilePath) # Check if settings file exists
|
|
if appCfgFileExists == False:
|
|
print("\nSettings initialization...\nPlease choose data folder, and default camera.")
|
|
Metashape.app.messageBox("Settings file not found...\nPlease choose default data folder, and default camera.")
|
|
foldeData = str(Metashape.app.getExistingDirectory("Working data folder"))
|
|
#selectCamDefault()
|
|
diaSelectCamera()
|
|
appCfg.add_section('APP SETTINGS')
|
|
appCfg.set('APP SETTINGS', 'settings_version', appsettings_ver)
|
|
appCfg.set('APP SETTINGS', 'folder_data', foldeData)
|
|
appCfg.set('APP SETTINGS', 'default_camera', selected_camera)
|
|
appCfg.set('APP SETTINGS', 'default_chunk_def', "GENERAL")
|
|
|
|
# Writing our configuration file to 'example.cfg'
|
|
with open(appCfgFilePath, 'w') as configfile:
|
|
appCfg.write(configfile)
|
|
|
|
appCfg.read(appCfgFilePath)
|
|
checkSettingsVer()
|
|
selected_data_folder = appCfg.get('APP SETTINGS', 'folder_data')
|
|
selected_camera = appCfg.get('APP SETTINGS', 'default_camera')
|
|
|
|
|
|
# Project settings initialization (used when .psx project is loaded)
|
|
def projCfgLoad():
|
|
global settingsRebuild
|
|
global projDoc
|
|
global projCfgFilePath
|
|
global projCfgFilePathExists
|
|
global projCfg
|
|
global projectOpened
|
|
global selected_camera
|
|
global selected_data_folder
|
|
|
|
projDoc = Metashape.app.document
|
|
projDocFile = str(projDoc).replace("<Document '", "").replace("'>", "")
|
|
projCfgFilePath = projDocFile.replace(".psx", "_settings.ini") # Datoteka z nastavitvami projekta
|
|
projCfgFilePathExists = os.path.isfile(projCfgFilePath) # Preveri, če datoteka z projektom obstaja
|
|
|
|
if projCfgFilePathExists == False:
|
|
print("\nProject settings initialization...\nPlease choose data folder, and default camera.")
|
|
Metashape.app.messageBox("Settings initialization...\nPlease choose data folder, and default camera.")
|
|
proj_data = Metashape.app.getExistingDirectory("Project data folder")
|
|
diaSelectCamera()
|
|
if projCfg.has_section('PROJECT SETTINGS') == True:
|
|
projCfg.remove_section('PROJECT SETTINGS')
|
|
|
|
projCfg.add_section('PROJECT SETTINGS')
|
|
projCfg.set('PROJECT SETTINGS', 'settings_version', appsettings_ver)
|
|
projCfg.set('PROJECT SETTINGS', 'folder_data', str(proj_data))
|
|
projCfg.set('PROJECT SETTINGS', 'default_camera', selected_camera)
|
|
projCfg.set('PROJECT SETTINGS', 'default_chunk_def', "GENERAL")
|
|
|
|
# Writing our configuration file to 'example.cfg'
|
|
with open(projCfgFilePath, 'w') as configfile:
|
|
projCfg.write(configfile)
|
|
|
|
projCfg.read(projCfgFilePath)
|
|
checkSettingsVer()
|
|
selected_data_folder = projCfg.get('PROJECT SETTINGS', 'folder_data')
|
|
selected_camera = projCfg.get('PROJECT SETTINGS', 'default_camera')
|
|
selected_chunk_def = projCfg.get('PROJECT SETTINGS', 'default_chunk_def')
|
|
readCameraSettings(selected_camera)
|
|
projectOpened = True
|
|
Metashape.app.messageBox("Project settings loaded.\n\n"
|
|
+ "Data Folder: " + str(selected_data_folder) + "\n"
|
|
+ "Default Camera: " + str(selected_camera) + "\n"
|
|
+ "Def. Chunk Definition: " + str(selected_chunk_def))
|
|
|
|
|
|
# Check settings version
|
|
def checkSettingsVer():
|
|
global settingsRebuild
|
|
if appCfg.get("APP SETTINGS", "settings_version") != appsettings_ver:
|
|
settingsReset()
|
|
|
|
|
|
# Reset settings
|
|
def settingsReset():
|
|
if projectOpened == True:
|
|
os.remove(projCfgFilePath)
|
|
projCfgLoad()
|
|
else:
|
|
os.remove(appCfgFilePath)
|
|
appCfgLoad()
|
|
|
|
|
|
# Routine to check if project exists before initializing settings
|
|
def projectOpenedCheck():
|
|
global projectOpened
|
|
doc = Metashape.app.document
|
|
#fileDoc = str(doc).replace("<Document '", "").replace("'>", "")
|
|
|
|
if doc == "<Document ''>" or doc == None:
|
|
projectOpened = False
|
|
appCfgLoad()
|
|
Metashape.app.messageBox("Empty project?\n\nSave project first, or open an existing project. (*.psx).")
|
|
else:
|
|
projectOpened = True
|
|
projCfgLoad()
|
|
|
|
|
|
# Show progress of processing
|
|
def progress_print(p):
|
|
print('Completed: {:.2f}%'.format(p))
|
|
|
|
|
|
# Detect markers and import coords
|
|
def marker_targets():
|
|
doc = Metashape.app.document
|
|
chunk = doc.chunk
|
|
netpath = Metashape.app.document.path
|
|
netroot = path.dirname(netpath)
|
|
nadaljujem = Metashape.app.getBool("Start marker detection?")
|
|
|
|
if nadaljujem == True:
|
|
chunk.detectMarkers(target_type=Metashape.CircularTarget12bit, tolerance=98)
|
|
Metashape.app.messageBox("Marker detection finished.\n\nIn next step choose file containing marker coordinates.\n\nFile must contain header, coordinates are expected starting at line 7.")
|
|
path_ref = Metashape.app.getOpenFileName("Import coordinates " + chunk.label, netroot, "Text file (*.txt)")
|
|
chunk.importReference(path_ref, format=Metashape.ReferenceFormatCSV, columns='nxyz', delimiter=',', skip_rows=6, create_markers=True)
|
|
chunk.updateTransform()
|
|
Metashape.app.messageBox("Target coordinates imported.\n\nNext step: Workflow > Align Photos")
|
|
Metashape.app.update()
|
|
doc.save()
|
|
|
|
|
|
# Routine for finding files - used when creating new chunk
|
|
def find_files(folder, types):
|
|
return [entry.path for entry in os.scandir(folder) if (entry.is_file() and os.path.splitext(entry.name)[1].lower() in types)]
|
|
|
|
|
|
def newchunk_manual(name_prefix, name_suffix, work_folder):
|
|
global projectOpened
|
|
if projectOpened == True:
|
|
doc = Metashape.app.document
|
|
# netroot = path.dirname(netpath)
|
|
netroot = work_folder
|
|
try:
|
|
image_folder = Metashape.app.getExistingDirectory("Select data folder", netroot)
|
|
|
|
except:
|
|
print("Add Chunk cancelled...")
|
|
|
|
else:
|
|
photos = find_files(image_folder, [".jpg", ".jpeg", ".png", ".tif", ".tiff"])
|
|
chunk = doc.addChunk()
|
|
chunk_nameraw = os.path.basename(image_folder)
|
|
chunk_name = name_prefix + chunk_nameraw + name_suffix
|
|
chunk.label = Metashape.app.getString("Chunk Name", chunk_name)
|
|
chunk.addPhotos(photos)
|
|
doc.chunk = chunk
|
|
doc.save()
|
|
Metashape.app.messageBox("New chunk added!\n\nChunk Name: " + chunk_name)
|
|
addcalib = Metashape.app.getBool("Confirm to import default camera calibration.\n\nDefault Camera: " + cam_name)
|
|
if addcalib == True:
|
|
readCameraSettings(selected_camera)
|
|
useCameraSettings()
|
|
doc.save()
|
|
else:
|
|
diaSelectCamera()
|
|
readCameraSettings(selected_camera)
|
|
useCameraSettings()
|
|
nadaljujem = Metashape.app.getBool("Camera set...\nUsing Camera: " + str(selected_camera) + "\n\nContinue with marker detection and coordinates import?")
|
|
if nadaljujem == True:
|
|
chunk.detectMarkers(target_type=Metashape.CircularTarget12bit, tolerance=98)
|
|
Metashape.app.messageBox("Marker Detection complete!\n\nNext step: Choose file with target coordinates.\nPoint file must have header.\nImport starts at line 7.")
|
|
path_ref = Metashape.app.getOpenFileName("Import Target Coordinates", image_folder, "Text file (*.txt)")
|
|
chunk.importReference(path_ref, format=Metashape.ReferenceFormatCSV, columns='nxyz', delimiter=',', skip_rows=6, create_markers=True)
|
|
chunk.updateTransform()
|
|
Metashape.app.messageBox("Target coordinates imported.\n\nNext step: Workflow > Align Photos")
|
|
Metashape.app.update()
|
|
doc.save()
|
|
else:
|
|
projectOpenedCheck()
|
|
|
|
|
|
# Create chunk AUTO - automaticaly use predefined options
|
|
def newchunk_auto(name_prefix, name_suffix, work_folder):
|
|
global projectOpened
|
|
if projectOpened == True:
|
|
doc = Metashape.app.document
|
|
netpath = Metashape.app.document.path
|
|
netroot = work_folder
|
|
try:
|
|
image_folder = Metashape.app.getExistingDirectory("Select data folder", netroot)
|
|
|
|
except:
|
|
print("Add Chunk cancelled...")
|
|
|
|
else:
|
|
photos = find_files(image_folder, [".jpg", ".jpeg", ".png", ".tif", ".tiff"])
|
|
chunk = doc.addChunk()
|
|
chunk.addPhotos(photos)
|
|
chunk_nameraw = os.path.basename(image_folder)
|
|
chunk_name = name_prefix + chunk_nameraw + name_suffix
|
|
# chunk.label = Metashape.app.getString("Chunk Name", chunk_name)
|
|
chunk.label = chunk_name
|
|
doc.chunk = chunk
|
|
doc.save(netpath)
|
|
Metashape.app.update()
|
|
# Metashape.app.messageBox("Nalaganje slik...")
|
|
# time.sleep(3)
|
|
readCameraSettings(selected_camera)
|
|
useCameraSettings()
|
|
chunk.detectMarkers(target_type=Metashape.CircularTarget12bit, tolerance=98)
|
|
# path_ref = Metashape.app.getOpenFileName("Import marker coordinates", image_folder, "Text file (*.txt)")
|
|
points_file = image_folder + "/" + chunk_nameraw + ".txt"
|
|
chunk.importReference(points_file, format=Metashape.ReferenceFormatCSV, columns='nxyz', delimiter=',', skip_rows=6, create_markers=True)
|
|
chunk.updateTransform()
|
|
doc.save(netpath)
|
|
Metashape.app.update()
|
|
else:
|
|
projectOpenedCheck()
|
|
|
|
|
|
# Choose default camera routine
|
|
def selectCamDefault():
|
|
camCfgLoad()
|
|
diaSelectCamera()
|
|
if selected_camera == None:
|
|
Metashape.app.messageBox("No camera selected. Nothing has changed...")
|
|
else:
|
|
if projectOpened == True:
|
|
projCfg.set('PROJECT SETTINGS', 'default_camera', selected_camera)
|
|
with open(projCfgFilePath, 'w') as configfile:
|
|
projCfg.write(configfile)
|
|
projCfg.read(projCfgFilePath)
|
|
else:
|
|
appCfg.set('APP SETTINGS', 'default_camera', selected_camera)
|
|
with open(appCfgFilePath, 'w') as configfile:
|
|
appCfg.write(configfile)
|
|
appCfg.read(appCfgFilePath)
|
|
|
|
print("Default camera settings saved.\nDefault Camera: " + selected_camera)
|
|
|
|
|
|
# Create About message dialog
|
|
def appAbout():
|
|
app_aboutmsg = app_name + "\n\nVersion: " + app_ver + "\n" + app_author + "\n" + app_about + "\n\n" + app_repo + "\n\nReferences:\n" + ref_repo + "\n\n" + ref_scripts + "\n"
|
|
message_box = QMessageBox()
|
|
message_box.setMinimumSize(600,500)
|
|
message_box.setTextFormat(Qt.PlainText)
|
|
message_box.setText(app_aboutmsg)
|
|
|
|
message_box.exec_()
|
|
|
|
|
|
def prazno():
|
|
print("Prazna vrstica")
|
|
|
|
|
|
# Routine for calling Edit Settings UI - called when user want's to edit settings
|
|
def editSettings():
|
|
app = QtWidgets.QApplication.instance()
|
|
parent = app.activeWindow()
|
|
editDialog = Ui_settingsDialog(parent)
|
|
|
|
|
|
# Routine for calling Edit Settings UI - called when user want's to edit settings
|
|
def addCameraDialog(camnew, camname):
|
|
app = QtWidgets.QApplication.instance()
|
|
parent = app.activeWindow()
|
|
if camnew == False:
|
|
dia = Ui_DialogAddEditCam(parent, camnew, camname)
|
|
else:
|
|
dia = Ui_DialogAddEditCam(parent, camnew, camname="")
|
|
|
|
|
|
# Routine for calling Edit Settings UI - called when user want's to edit settings
|
|
def diaSelectCamera():
|
|
app = QtWidgets.QApplication.instance()
|
|
parent = app.activeWindow()
|
|
diaChCam = Ui_dialogChooseCamera(parent)
|
|
|
|
|
|
# Routine for calling Edit Settings UI - called when user want's to edit settings
|
|
def camerasEditor():
|
|
app = QtWidgets.QApplication.instance()
|
|
parent = app.activeWindow()
|
|
camEditDialog = Ui_dialogCamGui(parent)
|
|
|
|
|
|
# Routine for calling Copy Regions UI
|
|
def copy_bbox():
|
|
app = QtWidgets.QApplication.instance()
|
|
parent = app.activeWindow()
|
|
dlg = CopyBoundingBoxDlg(parent)
|
|
|
|
|
|
def diaAddChunkSingle():
|
|
app = QtWidgets.QApplication.instance()
|
|
parent = app.activeWindow()
|
|
dia = Ui_DialogAddChunkQuick(parent)
|
|
|
|
|
|
def diaAddChunkBatch():
|
|
projectOpenedCheck()
|
|
app = QtWidgets.QApplication.instance()
|
|
parent = app.activeWindow()
|
|
dia2 = Ui_DialogBatchChunk(parent)
|
|
|
|
|
|
def diaChunkSettings():
|
|
app = QtWidgets.QApplication.instance()
|
|
parent = app.activeWindow()
|
|
dia_cs = Ui_DialogChunkSettings(parent)
|
|
|
|
|
|
icon_app = ":/icons/AutoFTG-appicon.png"
|
|
icon_app2 = ":/icons/AUTOFTG-V2.png"
|
|
icon0 = ":/icons/icons8-about-50.png"
|
|
iconadd = ":/icons/icons8-add-50.png"
|
|
icon2 = ":/icons/icons8-add-camera-50.png"
|
|
icon3 = ":/icons/icons8-add-list-50.png"
|
|
icon4 = ":/icons/icons8-add-new-50.png"
|
|
icon5 = ":/icons/icons8-aperture-50.png"
|
|
icon6 = ":/icons/icons8-apps-tab-50.png"
|
|
icon7 = ":/icons/icons8-bursts-50.png"
|
|
icon8 = ":/icons/icons8-camera-50.png"
|
|
icon9 = ":/icons/icons8-cameras-50.png"
|
|
icon10 = ":/icons/icons8-cancel-50.png"
|
|
icon11 = ":/icons/icons8-christmas-star-50.png"
|
|
icon12 = ":/icons/icons8-close-50.png"
|
|
icon13 = ":/icons/icons8-close-window-50.png"
|
|
icon14 = ":/icons/icons8-design-50.png"
|
|
icon15 = ":/icons/icons8-drag-and-drop-50.png"
|
|
icon16 = ":/icons/icons8-edit-50.png"
|
|
icon17 = ":/icons/icons8-full-page-view-50.png"
|
|
icon18 = ":/icons/icons8-images-folder-50.png"
|
|
icon19 = ":/icons/icons8-ios-application-placeholder-50.png"
|
|
icon20 = ":/icons/icons8-my-location-50.png"
|
|
icon21 = ":/icons/icons8-opened-folder-50.png"
|
|
icon22 = ":/icons/icons8-overscan-settings-50.png"
|
|
icon23 = ":/icons/icons8-panorama-50.png"
|
|
icon24 = ":/icons/icons8-quadcopter-50.png"
|
|
icon25 = ":/icons/icons8-restore-50.png"
|
|
icon26 = ":/icons/icons8-save-50.png"
|
|
icon27 = ":/icons/icons8-save-as-50.png"
|
|
icon28 = ":/icons/icons8-services-50.png"
|
|
icon29 = ":/icons/icons8-settings-50.png"
|
|
icon30 = ":/icons/icons8-slr-back-side-50.png"
|
|
icon31 = ":/icons/icons8-slr-camera-50.png"
|
|
icon32 = ":/icons/icons8-tools-50.png"
|
|
icon33 = ":/icons/icons8-video-stabilization-50.png"
|
|
icon34 = ":/icons/icons8-video-wall-50.png"
|
|
icon35 = ":/icons/icons8-vintage-camera-50.png"
|
|
icon36 = ":/icons/icons8-wallpaper-50.png"
|
|
icon37 = ":/icons/icons8-web-camera-50.png"
|
|
icon38 = ":/icons/icons8-map-marker-50.png"
|
|
icon40 = ":/icons/icons8-toolbox-50.png"
|
|
iconaddc = ":/icons/icons8-plus-50.png"
|
|
iconloads = ":/icons/icons8-share-50.png"
|
|
iconimg28 = ":/icons/kalota.png"
|
|
iconimg29 = ":/icons/kalota_m.png"
|
|
iconimg59 = ":/icons/stopnca_o.png"
|
|
iconimg60 = ":/icons/stopnca_s.png"
|
|
|
|
|
|
# Add Main Menu to start with
|
|
labelmenu= "About Auto FTG"
|
|
Metashape.app.addMenuItem(labelmenu, appAbout, icon=icon_app)
|
|
|
|
labelAddChSingle = "AutoFTG/Add Chunk (Single)"
|
|
Metashape.app.addMenuItem(label=labelAddChSingle, func=diaAddChunkSingle, shortcut="Ctrl++", icon=iconadd)
|
|
|
|
labelAddChBatch = "AutoFTG/Batch Chunk Creator"
|
|
Metashape.app.addMenuItem(label=labelAddChBatch, func=diaAddChunkBatch, shortcut="Ctrl+*", icon=icon4)
|
|
|
|
labelChSet = "AutoFTG/Chunk Definition Settings"
|
|
Metashape.app.addMenuItem(label=labelChSet, func=diaChunkSettings, icon=":/icons/icons8-content-50.png")
|
|
|
|
labelsep1a = "AutoFTG/--------------------"
|
|
Metashape.app.addMenuItem(labelsep1a, prazno)
|
|
|
|
label3a = "AutoFTG/Detect markers && Import coordinates"
|
|
Metashape.app.addMenuItem(label3a, marker_targets, icon=icon38)
|
|
|
|
label4 = "AutoFTG/Copy Region (Bounding Box)"
|
|
Metashape.app.addMenuItem(label4, copy_bbox, icon=icon15)
|
|
|
|
labelsep1 = "AutoFTG/--------------------"
|
|
Metashape.app.addMenuItem(labelsep1, prazno)
|
|
|
|
label2 = "AutoFTG/Change Camera (Chunk)"
|
|
Metashape.app.addMenuItem(label2, selectCamChunk, icon=icon8)
|
|
|
|
label2aaa = "AutoFTG/Set Default Camera (Project)"
|
|
Metashape.app.addMenuItem(label2aaa, selectCamDefault, icon=icon35)
|
|
|
|
label2cccc = "AutoFTG/Cameras Editor"
|
|
Metashape.app.addMenuItem(label2cccc, camerasEditor, icon=icon9)
|
|
|
|
labelsep55 = "AutoFTG/--------------------"
|
|
Metashape.app.addMenuItem(labelsep55, prazno)
|
|
|
|
labelset2 = "AutoFTG/Load Project Settings"
|
|
Metashape.app.addMenuItem(labelset2, projectOpenedCheck, icon=icon40)
|
|
|
|
labelset4 = "AutoFTG/Edit Loaded Settings"
|
|
Metashape.app.addMenuItem(labelset4, editSettings, icon=icon32)
|
|
|
|
labelset2i = "Load Project Settings"
|
|
Metashape.app.addMenuItem(labelset2i, projectOpenedCheck, icon=icon40)
|
|
|
|
labelAddChQui = "Add Chunk (Single)"
|
|
Metashape.app.addMenuItem(label=labelAddChQui, func=diaAddChunkSingle, icon=iconadd)
|
|
|
|
labelAddChBat = "Add Chunk (Multi)"
|
|
Metashape.app.addMenuItem(label=labelAddChBat, func=diaAddChunkBatch, icon=icon4)
|
|
|
|
labelCopyReg = "Copy Region"
|
|
Metashape.app.addMenuItem(labelCopyReg, copy_bbox, icon=icon15)
|
|
|
|
# labelsep3 = "AutoFTG/New Chunk (2TIR)/--------------------"
|
|
# Metashape.app.addMenuItem(labelsep3, prazno)
|
|
#
|
|
# labelNewStIz = "AutoFTG/New Chunk (2TIR)/STOPNICA (IZKOP) Pefix\Suffix"
|
|
# Metashape.app.addMenuSeparator(labelNewStIz)
|
|
#
|
|
# label0c = "AutoFTG/New Chunk (2TIR)/STOPNICA (IZKOP) Pefix\Suffix/< Modify prefix"
|
|
# Metashape.app.addMenuItem(label0c, changeNameStIzPre)
|
|
#
|
|
# label0d = "AutoFTG/New Chunk (2TIR)/STOPNICA (IZKOP) Pefix\Suffix/> Modify suffix"
|
|
# Metashape.app.addMenuItem(label0d, changeNameStIzSuf)
|
|
#
|
|
# labelNewStBb = "AutoFTG/New Chunk (2TIR)/STOPNICA (B.BET.) Pefix\Suffix"
|
|
# Metashape.app.addMenuSeparator(labelNewStBb)
|
|
#
|
|
# label0f = "AutoFTG/New Chunk (2TIR)/STOPNICA (B.BET.) Pefix\Suffix/< Modify prefix"
|
|
# Metashape.app.addMenuItem(label0f, changeNameStBbPre)
|
|
#
|
|
# label0g = "AutoFTG/New Chunk (2TIR)/STOPNICA (B.BET.) Pefix\Suffix/> Modify suffix"
|
|
# Metashape.app.addMenuItem(label0g, changeNameStBbSuf)
|
|
|
|
# changeChunkAppend(setting_name, append_type)
|
|
|
|
# labelsep2 = "Cameras"
|
|
# Metashape.app.addMenuSeparator(labelsep2)
|
|
|
|
# labelsep5 = "AutoFTG/--------------------"
|
|
# Metashape.app.addMenuItem(labelsep5, prazno)
|
|
|
|
# Initialize AutoFTG
|
|
def loadAutoftg():
|
|
global projectOpened
|
|
camCfgLoad()
|
|
chunksCfgLoad()
|
|
loadIcoSettings()
|
|
projectOpenedCheck()
|
|
checkSettingsVer()
|
|
print("\n\nAutoFTG initialized...\nApp Version: " + str(app_ver) + "\nSettings Version: " + str(appCfg.get('APP SETTINGS', 'settings_version')))
|
|
print(str(app_author))
|
|
|
|
# Run
|
|
loadAutoftg()
|
|
|