On Tuesday, July 23, 2019 at 6:54:09 AM UTC+2, Tduell wrote: > > Hello Kay, > > On Mon, 22 Jul 2019 01:40:57 +1000, 'Kay F. Jahnke' via hugin and other > free panoramic software <[email protected] <javascript:>> wrote: > > [snip] > > > What I mean is that the shebang is irrelevant for plugins. I think it's > > only the build system complaining: it sees a python script, looks into > > it and notices that the shebang is ambiguous. But since the python > > interpreter linked into hugin processes the script, this is a non-issue. > > Your patch satisfies the fedora build system, but it makes no difference > > apart from that. > > > I attach a patch removing the shebangs and the api-min and api-max. I > > did the patch with hg diff --git, hope that's all right. This patch > > should fix both the api-check issue and the shebang problem, and removes > > crop_cp.py which I think is no longer useful. It does not address the > > enforcement of python3-only. > > I've removed all my local changes from my repo and applied your patch and > > attempted to rpmbuild in Fedora 30, with the following message when the > build failed... > > Bytecompiling .py files below > /home/terry/rpmbuild/BUILDROOT/hugin-2019.1.0-0.2.20190723hg.fc30.x86_64/usr/lib/debug/usr/lib64/python2.7 > > > using /usr/bin/python2.7 > Bytecompiling .py files below > /home/terry/rpmbuild/BUILDROOT/hugin-2019.1.0-0.2.20190723hg.fc30.x86_64/usr/lib64/python2.7 > > > using /usr/bin/python2.7 > + /usr/lib/rpm/brp-python-hardlink > + /usr/lib/rpm/redhat/brp-mangle-shebangs > *** WARNING: ./usr/share/hugin/data/plugins/shooting_pattern.py is > executable but has empty or no shebang, removing executable bit > *** WARNING: ./usr/share/hugin/data/plugins/top_five.py is executable but > > has empty or no shebang, removing executable bit > *** WARNING: ./usr/share/hugin/data/plugins/woa.py is executable but has > empty or no shebang, removing executable bit > *** WARNING: ./usr/share/hugin/data/plugins-templates/plugin_skeleton.py > is executable but has empty or no shebang, removing executable bit > *** WARNING: ./usr/share/hugin/data/plugins-templates/dual_use.py is > executable but has empty or no shebang, removing executable bit >
The warnings are okay if the scripts are executable, but you can get rid of the warnings by clearing the execurable flag (chmod -x) *** ERROR: ambiguous python shebang in > /usr/lib64/python2.7/site-packages/hpi.py: #!/usr/bin/env python. Change > it to python3 (or python2) explicitly. > I did not remove the shebang from this one, sorry, may mistake. Please just remove the line with #! /usr/bin/env python It's just the same as with the other python scripts. Also remove the executable flaf with chmod. error: Bad exit status from /var/tmp/rpm-tmp.wFPWQa (%install) > > Looks like that patch isn't all that is needed for a build in Fedora 30, > assuming I applied the patch correctly. You applied it correctly, but there were bits missing . I did the changes I mentioned above here and made a new patch - not sure if the removal of the excutable flag shows in the diff, if not, just do chmod a-x on offending files. I'm off on a lengthy holiday now, so I hope this is enough for now. with regards Kay -- A list of frequently asked questions is available at: http://wiki.panotools.org/Hugin_FAQ --- You received this message because you are subscribed to the Google Groups "hugin and other free panoramic software" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion on the web visit https://groups.google.com/d/msgid/hugin-ptx/340bf43c-15f3-4305-8d1a-f2602a3be4d5%40googlegroups.com.
diff --git a/src/hugin_script_interface/plugins-dev/dual_use.py b/src/hugin_script_interface/plugins-dev/dual_use.py --- a/src/hugin_script_interface/plugins-dev/dual_use.py +++ b/src/hugin_script_interface/plugins-dev/dual_use.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - from __future__ import print_function gpl = r""" @@ -23,8 +21,6 @@ # @category Examples # @name Dual Use Plugin -# @api-min 2011.1 -# @api-max 2012.0 # dual_use will function as a hugin plugin and as a standalone # Python script. diff --git a/src/hugin_script_interface/plugins-dev/plugin_skeleton.py b/src/hugin_script_interface/plugins-dev/plugin_skeleton.py --- a/src/hugin_script_interface/plugins-dev/plugin_skeleton.py +++ b/src/hugin_script_interface/plugins-dev/plugin_skeleton.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - # plugin_skeleton.py - a skeleton for a hugin plugin # Copyright (C) 2011 Kay F. Jahnke @@ -18,8 +16,6 @@ # @category Examples # @name Skeleton Plugin -# @api-min 2011.1 -# @api-max 2012.0 # we use modern type python print statements: diff --git a/src/hugin_script_interface/plugins/crop_cp.py b/src/hugin_script_interface/plugins/crop_cp.py deleted file mode 100755 --- a/src/hugin_script_interface/plugins/crop_cp.py +++ /dev/null @@ -1,337 +0,0 @@ -#!/usr/bin/env python - -from __future__ import print_function - -gpl = r""" - crop_cp.py - remove CPs from parts of the panorama - - Copyright (C) 2011 Kay F. Jahnke - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -""" - -# @category Control Points -# @name Crop Control Points -# @api-min 2011.1 -# @api-max 2015.1 - -# crop_cps will function as a hugin plugin and as a standalone -# Python script. It can also serve as a template for a slightly -# more involved plugin than the ones in the Examples section. - -# Please note that this script tries to use wxPython. This should -# work on Linux systems that have wxPython istalled, but as far as -# I know currently it won't work on Windows, and certainly not on -# Mac OS, where there is currently no hsi/hpi suport. If you want -# to switch off the use of wxPython, set the default parameter in -# the entry function to a value different from None, i.e. change -# def entry ( pano , inside = None ) : -# to -# def entry ( pano , inside = False ) : - -import os -import sys -import hsi -import math - -# we make an exception for run time errors in this script: - -class CropCpError ( Exception ) : - - def __init__ ( self , text ) : - self.text = text - - def __str__ ( self ) : - return self.text - -# some global variables to reduce overhead - we just reuse the same -# FDiff2D objects every time we call the transforms - -transform_in = hsi.FDiff2D(0,0) -transform_out = hsi.FDiff2D(0,0) - -# transform point coordinates using panotools transforms -# and the global variables for source and target - -class to_pano : - - def __init__ ( self , pano , image ) : - - pano_options = pano.getOptions() - img = pano.getImage ( image ) - w = img.getWidth() - h = img.getHeight() - self.wh = w / 2.0 - self.hh = h / 2.0 - - # image to pano - - self.tf = hsi.Transform() - self.tf.createInvTransform ( img , pano_options ) - - def transform ( self , p ) : - - # pixel coordinates come in, but have to use center-origin - - transform_in.x = p[0] - self.wh - transform_in.y = p[1] - self.hh - - if self.tf.transform ( transform_out , transform_in ) : - return ( transform_out.x , transform_out.y ) - - # KFJ 2011-06-19 I've noticed the transform failing - # with images that have X, Y or Z values different from 0. - # I'm not sure if this is a bug in the transform. - - print ( 'transform failed for %s' % str ( p ) ) - return None - -# the crop_cps routine is the workhorse of this script. It does the -# actual work of looking at the CPs and removing the unwanted ones. - -def crop_cps ( pano , inside ) : - - pano_options = pano.getOptions() - - size = pano_options.getSize() - - # we need these offsets because pano coordinates are center-origin - - wh = size.width() / 2.0 - hh = size.height() / 2.0 - - # that's the ROI of the panorama, in corner-origin - - roi = pano_options.getROI() - - # out limits, now in center-origin - - left = roi.left() - wh - right = roi.right() - wh - top = roi.top() - hh - bottom = roi.bottom() - hh - - ni = pano.getNrOfImages() - tflist = [] - - # we get a list of CPs in pano coordinates. Note that the pano - # coordinates are taken from the coordinates in the left image only. - - for img in range ( ni ) : - tflist.append ( to_pano ( pano , img ) ) - - # we take the CPVector: - - cpv = pano.getCtrlPoints() - print ( 'found %d CPs' % len ( cpv ) ) - - # We want to put the remaining CPs into this CPVector: - - ncpv = hsi.CPVector() - - for cp in cpv : - p = ( cp.x1 , cp.y1 ) - img = cp.image1Nr - tfp = tflist[img].transform ( p ) - # KFJ 2011-06-17 If the transform fails, keep the CP - # - best to be defensive. - if tfp is None : - ncpv.append ( cp ) - elif inside : # remove CPs inside ROI (or, keep those outside) - if not ( ( left <= tfp[0] <= right ) and ( top <= tfp[1] <= bottom ) ) : - ncpv.append ( cp ) - else : - if ( left <= tfp[0] <= right ) and ( top <= tfp[1] <= bottom ) : - ncpv.append ( cp ) - - pano.setCtrlPoints ( ncpv ) - print ( 'removed %d CPs' % ( len ( cpv ) - len ( ncpv ) ) ) - - return 0 - -# the remainder of the script is administration and deals with running -# it either as a plugin from hugin or as a standalone CLI program. - -# first is the entry routine which is called when this script is used -# as a plugin from hugin. If the inside parameter is not passed -# (or passed as 'None') - a dialog will ask whether to remove CPs -# inside or outside of the ROI. If inside is passed as True, CPs -# inside the ROI are removed, otherwise those outside. -# Note that the 'GUI' is primitive - it offers no help or about info, -# no tool tips etc. - the help could be made from the docstrings of -# the CLI version below. - -def entry ( pano , inside = None ) : - - if inside is None: - - # if we've been called as a plugin (we're not main) and inside is - # None, the default, we assume that we're supposed to ask the user - # whether to remove the CPs from inside or outside the ROI. If - # inside is passed as True or False, we execute without further ado. - - import wx - - # we need a simple dialog box for this purpose which displays - # a radio button for the inside/outside choice, plus Okay and Cancel. - - class crop_cp_dialog ( wx.Dialog ) : - - # excuse my clumsy wxPython ;-) - - def __init__(self, parent, title): - - wx.Dialog.__init__(self, parent, -1, title, size = ( 200 , 150 ) ) - choices = [ 'outside ROI' , 'inside ROI' ] - # default choice is to remove CPs outdide ROI - self.choice = 0 - - # we create and position the UI elements - sizer = wx.BoxSizer ( wx.VERTICAL ) - rb = wx.RadioBox( - self, -1, "", wx.DefaultPosition, wx.DefaultSize, - choices, 2, wx.RA_SPECIFY_COLS - ) - self.Bind(wx.EVT_RADIOBOX, self.EvtRadioBox, rb) - sizer.Add(rb, 0, wx.ALL, 20) - btnsizer = wx.StdDialogButtonSizer() - btn = wx.Button(self, wx.ID_OK) - btn.SetDefault() - btnsizer.AddButton(btn) - btn = wx.Button(self, wx.ID_CANCEL) - btnsizer.AddButton(btn) - btnsizer.Realize() - sizer.Add(btnsizer, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) - self.SetSizer(sizer) - - # what to do when the radio button is operated: - def EvtRadioBox(self, event): - self.choice = event.GetInt() - - # make the dialog and show it modally: - ccd = crop_cp_dialog ( None , 'Crop CPs' ) - retval = ccd.ShowModal() - # only if Okay was clicked the state of the radio button is used - if retval == wx.ID_OK : - if ccd.choice == 0 : - inside = False - else : - inside = True - # otherwise we cancel the operation and return. - else : - return 0 - - # now the preliminaries are sorted, we can call the workhorse routine. - - return crop_cps ( pano , inside ) - -# the main routine is what's called if this script has been called from -# the command line. In this case we do the CLI thing, so we parse the -# argument vector and act accordingly. - -def main() : - - # when called from the command line, we import the argparse module. - # This may not be part of the standard library on your system. - - import argparse - - # and create an argument parser. - - parser = argparse.ArgumentParser ( - formatter_class=argparse.RawDescriptionHelpFormatter , - description = gpl + ''' - crop_cp will remove all CPs which are either outside the panorama's ROI - or inside it's ROI if the inside flag is set - ''' ) - - parser.add_argument('-o', '--output', - metavar='<output file>', - default = None, - type=str, - help='write output to a different file') - - parser.add_argument('-i', '--inside', - action='store_true', - default = False, - help='remove CPs inside ROI') - - parser.add_argument('input' , - metavar = '<pto file>' , - type = str , - help = 'pto file to be processed' ) - - if len ( sys.argv ) < 2 : - parser.print_help() - return - - # we parse the arguments into a global variable so we can access - # them from everywhere without having to pass them around - - global args - - args = parser.parse_args() - - if args.output : - print ( 'output: ' , args.output ) - print ( 'ptofile:' , args.input ) - - # first we see if we can open the input file - - ifs = hsi.ifstream ( args.input ) - if not ifs.good() : - raise CropCpError ( 'cannot open input file %s' % args.input ) - - pano = hsi.Panorama() - success = pano.readData ( ifs ) - del ifs - if success != hsi.DocumentData.SUCCESSFUL : - raise CropCpError ( 'input file %s contains invalid data' % args.input ) - - # if a separate output file was chosen, we open it now to avoid - # later disappointments - - if args.output: - ofs = hsi.ofstream ( args.output ) - if not ofs.good() : - raise CropCpError ( 'cannot open output file %s' % args.output ) - - # we've checked the args, now we call crop_cps() - - crop_cps ( pano , args.inside ) - - # if no different output file was specified, overwrite the input - - if not args.output : - ofs = hsi.ofstream ( args.input ) - if not ofs.good() : - raise CropCpError ( 'cannot open file %s for output' % args.input ) - - success = pano.writeData ( ofs ) - del ofs - if success != hsi.DocumentData.SUCCESSFUL : - raise CropCpError ( 'error writing pano to %s' % args.input ) - - # done. - - return 0 - -# are we main? This means we've been called from the command line -# as a standalone program, so we have to go through the main() function. - -if __name__ == "__main__": - try: - main() - except CropCpError as e : - print ( 'Run Time Error: %s' % e ) diff --git a/src/hugin_script_interface/plugins/shooting_pattern.py b/src/hugin_script_interface/plugins/shooting_pattern.py --- a/src/hugin_script_interface/plugins/shooting_pattern.py +++ b/src/hugin_script_interface/plugins/shooting_pattern.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - from __future__ import print_function # shooting_pattern.py - apply a 6-1-1 shooting pattern to a panorama @@ -41,8 +39,6 @@ # @name 6-1-1 Shooting Pattern # @description apply a shooting pattern (6 around, 1 up 1 down) # @sys win nix mac -# @api-max 2016.1 -# @api-min 2011.1 # the position routine will apply yaw, pitch to an image diff --git a/src/hugin_script_interface/plugins/top_five.py b/src/hugin_script_interface/plugins/top_five.py --- a/src/hugin_script_interface/plugins/top_five.py +++ b/src/hugin_script_interface/plugins/top_five.py @@ -1,11 +1,7 @@ -#!/usr/bin/env python - from __future__ import print_function # @category Control Points # @name keep 5 CPs per image pair -# @api-min 2011.1 -# @api-max 2016.1 # top_five.py - keep the five best CPs for each image pair @@ -66,7 +62,10 @@ # finally we attach the new CP vector to the panorama - print ( 'keeping %d control points' % len ( cpv ) ) + print ( 'keeping %d control points:' % len ( cpv ) ) + for cp in cpv: + print ( "%d %d %d %d" % ( cp.x1 , cp.y1 , cp.x2 , cp.y2 ) ) + pano.setCtrlPoints ( cpv ) return 0 diff --git a/src/hugin_script_interface/plugins/woa.py b/src/hugin_script_interface/plugins/woa.py --- a/src/hugin_script_interface/plugins/woa.py +++ b/src/hugin_script_interface/plugins/woa.py @@ -1,4 +1,3 @@ -#! /usr/bin/env python # -*- coding: utf-8 -*- from __future__ import print_function @@ -24,8 +23,6 @@ """ # @category Control Points # @name Warped Overlap Analysis -# @api-min 2014.0 -# @api-max 2016.3 # note that if you want to read the script, it's written bottom-up, so the # higher-level routines are towards the end. diff --git a/src/hugin_base/panodata/PanoramaOptions.h b/src/hugin_base/panodata/PanoramaOptions.h --- a/src/hugin_base/panodata/PanoramaOptions.h +++ b/src/hugin_base/panodata/PanoramaOptions.h @@ -240,7 +240,7 @@ panoProjectionFeaturesQuery(m_projectionFormat, &m_projFeatures); resetProjectionParameters(); - outputStacksMinOverlap = 0.7; + outputStacksMinOverlap = 0.85; outputLayersExposureDiff = 0.5; } diff --git a/src/hugin_script_interface/hpi.py b/src/hugin_script_interface/hpi.py old mode 100755 new mode 100644 diff --git a/src/hugin_script_interface/plugins-dev/dual_use.py b/src/hugin_script_interface/plugins-dev/dual_use.py old mode 100755 new mode 100644 --- a/src/hugin_script_interface/plugins-dev/dual_use.py +++ b/src/hugin_script_interface/plugins-dev/dual_use.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - from __future__ import print_function gpl = r""" @@ -23,8 +21,6 @@ # @category Examples # @name Dual Use Plugin -# @api-min 2011.1 -# @api-max 2012.0 # dual_use will function as a hugin plugin and as a standalone # Python script. diff --git a/src/hugin_script_interface/plugins-dev/plugin_skeleton.py b/src/hugin_script_interface/plugins-dev/plugin_skeleton.py old mode 100755 new mode 100644 --- a/src/hugin_script_interface/plugins-dev/plugin_skeleton.py +++ b/src/hugin_script_interface/plugins-dev/plugin_skeleton.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - # plugin_skeleton.py - a skeleton for a hugin plugin # Copyright (C) 2011 Kay F. Jahnke @@ -18,8 +16,6 @@ # @category Examples # @name Skeleton Plugin -# @api-min 2011.1 -# @api-max 2012.0 # we use modern type python print statements: diff --git a/src/hugin_script_interface/plugins/crop_cp.py b/src/hugin_script_interface/plugins/crop_cp.py deleted file mode 100755 --- a/src/hugin_script_interface/plugins/crop_cp.py +++ /dev/null @@ -1,337 +0,0 @@ -#!/usr/bin/env python - -from __future__ import print_function - -gpl = r""" - crop_cp.py - remove CPs from parts of the panorama - - Copyright (C) 2011 Kay F. Jahnke - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -""" - -# @category Control Points -# @name Crop Control Points -# @api-min 2011.1 -# @api-max 2015.1 - -# crop_cps will function as a hugin plugin and as a standalone -# Python script. It can also serve as a template for a slightly -# more involved plugin than the ones in the Examples section. - -# Please note that this script tries to use wxPython. This should -# work on Linux systems that have wxPython istalled, but as far as -# I know currently it won't work on Windows, and certainly not on -# Mac OS, where there is currently no hsi/hpi suport. If you want -# to switch off the use of wxPython, set the default parameter in -# the entry function to a value different from None, i.e. change -# def entry ( pano , inside = None ) : -# to -# def entry ( pano , inside = False ) : - -import os -import sys -import hsi -import math - -# we make an exception for run time errors in this script: - -class CropCpError ( Exception ) : - - def __init__ ( self , text ) : - self.text = text - - def __str__ ( self ) : - return self.text - -# some global variables to reduce overhead - we just reuse the same -# FDiff2D objects every time we call the transforms - -transform_in = hsi.FDiff2D(0,0) -transform_out = hsi.FDiff2D(0,0) - -# transform point coordinates using panotools transforms -# and the global variables for source and target - -class to_pano : - - def __init__ ( self , pano , image ) : - - pano_options = pano.getOptions() - img = pano.getImage ( image ) - w = img.getWidth() - h = img.getHeight() - self.wh = w / 2.0 - self.hh = h / 2.0 - - # image to pano - - self.tf = hsi.Transform() - self.tf.createInvTransform ( img , pano_options ) - - def transform ( self , p ) : - - # pixel coordinates come in, but have to use center-origin - - transform_in.x = p[0] - self.wh - transform_in.y = p[1] - self.hh - - if self.tf.transform ( transform_out , transform_in ) : - return ( transform_out.x , transform_out.y ) - - # KFJ 2011-06-19 I've noticed the transform failing - # with images that have X, Y or Z values different from 0. - # I'm not sure if this is a bug in the transform. - - print ( 'transform failed for %s' % str ( p ) ) - return None - -# the crop_cps routine is the workhorse of this script. It does the -# actual work of looking at the CPs and removing the unwanted ones. - -def crop_cps ( pano , inside ) : - - pano_options = pano.getOptions() - - size = pano_options.getSize() - - # we need these offsets because pano coordinates are center-origin - - wh = size.width() / 2.0 - hh = size.height() / 2.0 - - # that's the ROI of the panorama, in corner-origin - - roi = pano_options.getROI() - - # out limits, now in center-origin - - left = roi.left() - wh - right = roi.right() - wh - top = roi.top() - hh - bottom = roi.bottom() - hh - - ni = pano.getNrOfImages() - tflist = [] - - # we get a list of CPs in pano coordinates. Note that the pano - # coordinates are taken from the coordinates in the left image only. - - for img in range ( ni ) : - tflist.append ( to_pano ( pano , img ) ) - - # we take the CPVector: - - cpv = pano.getCtrlPoints() - print ( 'found %d CPs' % len ( cpv ) ) - - # We want to put the remaining CPs into this CPVector: - - ncpv = hsi.CPVector() - - for cp in cpv : - p = ( cp.x1 , cp.y1 ) - img = cp.image1Nr - tfp = tflist[img].transform ( p ) - # KFJ 2011-06-17 If the transform fails, keep the CP - # - best to be defensive. - if tfp is None : - ncpv.append ( cp ) - elif inside : # remove CPs inside ROI (or, keep those outside) - if not ( ( left <= tfp[0] <= right ) and ( top <= tfp[1] <= bottom ) ) : - ncpv.append ( cp ) - else : - if ( left <= tfp[0] <= right ) and ( top <= tfp[1] <= bottom ) : - ncpv.append ( cp ) - - pano.setCtrlPoints ( ncpv ) - print ( 'removed %d CPs' % ( len ( cpv ) - len ( ncpv ) ) ) - - return 0 - -# the remainder of the script is administration and deals with running -# it either as a plugin from hugin or as a standalone CLI program. - -# first is the entry routine which is called when this script is used -# as a plugin from hugin. If the inside parameter is not passed -# (or passed as 'None') - a dialog will ask whether to remove CPs -# inside or outside of the ROI. If inside is passed as True, CPs -# inside the ROI are removed, otherwise those outside. -# Note that the 'GUI' is primitive - it offers no help or about info, -# no tool tips etc. - the help could be made from the docstrings of -# the CLI version below. - -def entry ( pano , inside = None ) : - - if inside is None: - - # if we've been called as a plugin (we're not main) and inside is - # None, the default, we assume that we're supposed to ask the user - # whether to remove the CPs from inside or outside the ROI. If - # inside is passed as True or False, we execute without further ado. - - import wx - - # we need a simple dialog box for this purpose which displays - # a radio button for the inside/outside choice, plus Okay and Cancel. - - class crop_cp_dialog ( wx.Dialog ) : - - # excuse my clumsy wxPython ;-) - - def __init__(self, parent, title): - - wx.Dialog.__init__(self, parent, -1, title, size = ( 200 , 150 ) ) - choices = [ 'outside ROI' , 'inside ROI' ] - # default choice is to remove CPs outdide ROI - self.choice = 0 - - # we create and position the UI elements - sizer = wx.BoxSizer ( wx.VERTICAL ) - rb = wx.RadioBox( - self, -1, "", wx.DefaultPosition, wx.DefaultSize, - choices, 2, wx.RA_SPECIFY_COLS - ) - self.Bind(wx.EVT_RADIOBOX, self.EvtRadioBox, rb) - sizer.Add(rb, 0, wx.ALL, 20) - btnsizer = wx.StdDialogButtonSizer() - btn = wx.Button(self, wx.ID_OK) - btn.SetDefault() - btnsizer.AddButton(btn) - btn = wx.Button(self, wx.ID_CANCEL) - btnsizer.AddButton(btn) - btnsizer.Realize() - sizer.Add(btnsizer, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) - self.SetSizer(sizer) - - # what to do when the radio button is operated: - def EvtRadioBox(self, event): - self.choice = event.GetInt() - - # make the dialog and show it modally: - ccd = crop_cp_dialog ( None , 'Crop CPs' ) - retval = ccd.ShowModal() - # only if Okay was clicked the state of the radio button is used - if retval == wx.ID_OK : - if ccd.choice == 0 : - inside = False - else : - inside = True - # otherwise we cancel the operation and return. - else : - return 0 - - # now the preliminaries are sorted, we can call the workhorse routine. - - return crop_cps ( pano , inside ) - -# the main routine is what's called if this script has been called from -# the command line. In this case we do the CLI thing, so we parse the -# argument vector and act accordingly. - -def main() : - - # when called from the command line, we import the argparse module. - # This may not be part of the standard library on your system. - - import argparse - - # and create an argument parser. - - parser = argparse.ArgumentParser ( - formatter_class=argparse.RawDescriptionHelpFormatter , - description = gpl + ''' - crop_cp will remove all CPs which are either outside the panorama's ROI - or inside it's ROI if the inside flag is set - ''' ) - - parser.add_argument('-o', '--output', - metavar='<output file>', - default = None, - type=str, - help='write output to a different file') - - parser.add_argument('-i', '--inside', - action='store_true', - default = False, - help='remove CPs inside ROI') - - parser.add_argument('input' , - metavar = '<pto file>' , - type = str , - help = 'pto file to be processed' ) - - if len ( sys.argv ) < 2 : - parser.print_help() - return - - # we parse the arguments into a global variable so we can access - # them from everywhere without having to pass them around - - global args - - args = parser.parse_args() - - if args.output : - print ( 'output: ' , args.output ) - print ( 'ptofile:' , args.input ) - - # first we see if we can open the input file - - ifs = hsi.ifstream ( args.input ) - if not ifs.good() : - raise CropCpError ( 'cannot open input file %s' % args.input ) - - pano = hsi.Panorama() - success = pano.readData ( ifs ) - del ifs - if success != hsi.DocumentData.SUCCESSFUL : - raise CropCpError ( 'input file %s contains invalid data' % args.input ) - - # if a separate output file was chosen, we open it now to avoid - # later disappointments - - if args.output: - ofs = hsi.ofstream ( args.output ) - if not ofs.good() : - raise CropCpError ( 'cannot open output file %s' % args.output ) - - # we've checked the args, now we call crop_cps() - - crop_cps ( pano , args.inside ) - - # if no different output file was specified, overwrite the input - - if not args.output : - ofs = hsi.ofstream ( args.input ) - if not ofs.good() : - raise CropCpError ( 'cannot open file %s for output' % args.input ) - - success = pano.writeData ( ofs ) - del ofs - if success != hsi.DocumentData.SUCCESSFUL : - raise CropCpError ( 'error writing pano to %s' % args.input ) - - # done. - - return 0 - -# are we main? This means we've been called from the command line -# as a standalone program, so we have to go through the main() function. - -if __name__ == "__main__": - try: - main() - except CropCpError as e : - print ( 'Run Time Error: %s' % e ) diff --git a/src/hugin_script_interface/plugins/shooting_pattern.py b/src/hugin_script_interface/plugins/shooting_pattern.py old mode 100755 new mode 100644 --- a/src/hugin_script_interface/plugins/shooting_pattern.py +++ b/src/hugin_script_interface/plugins/shooting_pattern.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - from __future__ import print_function # shooting_pattern.py - apply a 6-1-1 shooting pattern to a panorama @@ -41,8 +39,6 @@ # @name 6-1-1 Shooting Pattern # @description apply a shooting pattern (6 around, 1 up 1 down) # @sys win nix mac -# @api-max 2016.1 -# @api-min 2011.1 # the position routine will apply yaw, pitch to an image diff --git a/src/hugin_script_interface/plugins/top_five.py b/src/hugin_script_interface/plugins/top_five.py old mode 100755 new mode 100644 --- a/src/hugin_script_interface/plugins/top_five.py +++ b/src/hugin_script_interface/plugins/top_five.py @@ -1,11 +1,7 @@ -#!/usr/bin/env python - from __future__ import print_function # @category Control Points # @name keep 5 CPs per image pair -# @api-min 2011.1 -# @api-max 2016.1 # top_five.py - keep the five best CPs for each image pair @@ -66,7 +62,10 @@ # finally we attach the new CP vector to the panorama - print ( 'keeping %d control points' % len ( cpv ) ) + print ( 'keeping %d control points:' % len ( cpv ) ) + for cp in cpv: + print ( "%d %d %d %d" % ( cp.x1 , cp.y1 , cp.x2 , cp.y2 ) ) + pano.setCtrlPoints ( cpv ) return 0 diff --git a/src/hugin_script_interface/plugins/woa.py b/src/hugin_script_interface/plugins/woa.py old mode 100755 new mode 100644 --- a/src/hugin_script_interface/plugins/woa.py +++ b/src/hugin_script_interface/plugins/woa.py @@ -1,4 +1,3 @@ -#! /usr/bin/env python # -*- coding: utf-8 -*- from __future__ import print_function @@ -24,8 +23,6 @@ """ # @category Control Points # @name Warped Overlap Analysis -# @api-min 2014.0 -# @api-max 2016.3 # note that if you want to read the script, it's written bottom-up, so the # higher-level routines are towards the end. diff --git a/src/hugin_base/panodata/PanoramaOptions.h b/src/hugin_base/panodata/PanoramaOptions.h --- a/src/hugin_base/panodata/PanoramaOptions.h +++ b/src/hugin_base/panodata/PanoramaOptions.h @@ -240,7 +240,7 @@ panoProjectionFeaturesQuery(m_projectionFormat, &m_projFeatures); resetProjectionParameters(); - outputStacksMinOverlap = 0.7; + outputStacksMinOverlap = 0.85; outputLayersExposureDiff = 0.5; } diff --git a/src/hugin_script_interface/hpi.py b/src/hugin_script_interface/hpi.py old mode 100755 new mode 100644 --- a/src/hugin_script_interface/hpi.py +++ b/src/hugin_script_interface/hpi.py @@ -1,5 +1,3 @@ -#! /usr/bin/env python - from __future__ import print_function # hpi.py - dispatcher for hugin plugins diff --git a/src/hugin_script_interface/plugins-dev/dual_use.py b/src/hugin_script_interface/plugins-dev/dual_use.py old mode 100755 new mode 100644 --- a/src/hugin_script_interface/plugins-dev/dual_use.py +++ b/src/hugin_script_interface/plugins-dev/dual_use.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - from __future__ import print_function gpl = r""" @@ -23,8 +21,6 @@ # @category Examples # @name Dual Use Plugin -# @api-min 2011.1 -# @api-max 2012.0 # dual_use will function as a hugin plugin and as a standalone # Python script. diff --git a/src/hugin_script_interface/plugins-dev/plugin_skeleton.py b/src/hugin_script_interface/plugins-dev/plugin_skeleton.py old mode 100755 new mode 100644 --- a/src/hugin_script_interface/plugins-dev/plugin_skeleton.py +++ b/src/hugin_script_interface/plugins-dev/plugin_skeleton.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - # plugin_skeleton.py - a skeleton for a hugin plugin # Copyright (C) 2011 Kay F. Jahnke @@ -18,8 +16,6 @@ # @category Examples # @name Skeleton Plugin -# @api-min 2011.1 -# @api-max 2012.0 # we use modern type python print statements: diff --git a/src/hugin_script_interface/plugins/crop_cp.py b/src/hugin_script_interface/plugins/crop_cp.py deleted file mode 100755 --- a/src/hugin_script_interface/plugins/crop_cp.py +++ /dev/null @@ -1,337 +0,0 @@ -#!/usr/bin/env python - -from __future__ import print_function - -gpl = r""" - crop_cp.py - remove CPs from parts of the panorama - - Copyright (C) 2011 Kay F. Jahnke - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -""" - -# @category Control Points -# @name Crop Control Points -# @api-min 2011.1 -# @api-max 2015.1 - -# crop_cps will function as a hugin plugin and as a standalone -# Python script. It can also serve as a template for a slightly -# more involved plugin than the ones in the Examples section. - -# Please note that this script tries to use wxPython. This should -# work on Linux systems that have wxPython istalled, but as far as -# I know currently it won't work on Windows, and certainly not on -# Mac OS, where there is currently no hsi/hpi suport. If you want -# to switch off the use of wxPython, set the default parameter in -# the entry function to a value different from None, i.e. change -# def entry ( pano , inside = None ) : -# to -# def entry ( pano , inside = False ) : - -import os -import sys -import hsi -import math - -# we make an exception for run time errors in this script: - -class CropCpError ( Exception ) : - - def __init__ ( self , text ) : - self.text = text - - def __str__ ( self ) : - return self.text - -# some global variables to reduce overhead - we just reuse the same -# FDiff2D objects every time we call the transforms - -transform_in = hsi.FDiff2D(0,0) -transform_out = hsi.FDiff2D(0,0) - -# transform point coordinates using panotools transforms -# and the global variables for source and target - -class to_pano : - - def __init__ ( self , pano , image ) : - - pano_options = pano.getOptions() - img = pano.getImage ( image ) - w = img.getWidth() - h = img.getHeight() - self.wh = w / 2.0 - self.hh = h / 2.0 - - # image to pano - - self.tf = hsi.Transform() - self.tf.createInvTransform ( img , pano_options ) - - def transform ( self , p ) : - - # pixel coordinates come in, but have to use center-origin - - transform_in.x = p[0] - self.wh - transform_in.y = p[1] - self.hh - - if self.tf.transform ( transform_out , transform_in ) : - return ( transform_out.x , transform_out.y ) - - # KFJ 2011-06-19 I've noticed the transform failing - # with images that have X, Y or Z values different from 0. - # I'm not sure if this is a bug in the transform. - - print ( 'transform failed for %s' % str ( p ) ) - return None - -# the crop_cps routine is the workhorse of this script. It does the -# actual work of looking at the CPs and removing the unwanted ones. - -def crop_cps ( pano , inside ) : - - pano_options = pano.getOptions() - - size = pano_options.getSize() - - # we need these offsets because pano coordinates are center-origin - - wh = size.width() / 2.0 - hh = size.height() / 2.0 - - # that's the ROI of the panorama, in corner-origin - - roi = pano_options.getROI() - - # out limits, now in center-origin - - left = roi.left() - wh - right = roi.right() - wh - top = roi.top() - hh - bottom = roi.bottom() - hh - - ni = pano.getNrOfImages() - tflist = [] - - # we get a list of CPs in pano coordinates. Note that the pano - # coordinates are taken from the coordinates in the left image only. - - for img in range ( ni ) : - tflist.append ( to_pano ( pano , img ) ) - - # we take the CPVector: - - cpv = pano.getCtrlPoints() - print ( 'found %d CPs' % len ( cpv ) ) - - # We want to put the remaining CPs into this CPVector: - - ncpv = hsi.CPVector() - - for cp in cpv : - p = ( cp.x1 , cp.y1 ) - img = cp.image1Nr - tfp = tflist[img].transform ( p ) - # KFJ 2011-06-17 If the transform fails, keep the CP - # - best to be defensive. - if tfp is None : - ncpv.append ( cp ) - elif inside : # remove CPs inside ROI (or, keep those outside) - if not ( ( left <= tfp[0] <= right ) and ( top <= tfp[1] <= bottom ) ) : - ncpv.append ( cp ) - else : - if ( left <= tfp[0] <= right ) and ( top <= tfp[1] <= bottom ) : - ncpv.append ( cp ) - - pano.setCtrlPoints ( ncpv ) - print ( 'removed %d CPs' % ( len ( cpv ) - len ( ncpv ) ) ) - - return 0 - -# the remainder of the script is administration and deals with running -# it either as a plugin from hugin or as a standalone CLI program. - -# first is the entry routine which is called when this script is used -# as a plugin from hugin. If the inside parameter is not passed -# (or passed as 'None') - a dialog will ask whether to remove CPs -# inside or outside of the ROI. If inside is passed as True, CPs -# inside the ROI are removed, otherwise those outside. -# Note that the 'GUI' is primitive - it offers no help or about info, -# no tool tips etc. - the help could be made from the docstrings of -# the CLI version below. - -def entry ( pano , inside = None ) : - - if inside is None: - - # if we've been called as a plugin (we're not main) and inside is - # None, the default, we assume that we're supposed to ask the user - # whether to remove the CPs from inside or outside the ROI. If - # inside is passed as True or False, we execute without further ado. - - import wx - - # we need a simple dialog box for this purpose which displays - # a radio button for the inside/outside choice, plus Okay and Cancel. - - class crop_cp_dialog ( wx.Dialog ) : - - # excuse my clumsy wxPython ;-) - - def __init__(self, parent, title): - - wx.Dialog.__init__(self, parent, -1, title, size = ( 200 , 150 ) ) - choices = [ 'outside ROI' , 'inside ROI' ] - # default choice is to remove CPs outdide ROI - self.choice = 0 - - # we create and position the UI elements - sizer = wx.BoxSizer ( wx.VERTICAL ) - rb = wx.RadioBox( - self, -1, "", wx.DefaultPosition, wx.DefaultSize, - choices, 2, wx.RA_SPECIFY_COLS - ) - self.Bind(wx.EVT_RADIOBOX, self.EvtRadioBox, rb) - sizer.Add(rb, 0, wx.ALL, 20) - btnsizer = wx.StdDialogButtonSizer() - btn = wx.Button(self, wx.ID_OK) - btn.SetDefault() - btnsizer.AddButton(btn) - btn = wx.Button(self, wx.ID_CANCEL) - btnsizer.AddButton(btn) - btnsizer.Realize() - sizer.Add(btnsizer, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) - self.SetSizer(sizer) - - # what to do when the radio button is operated: - def EvtRadioBox(self, event): - self.choice = event.GetInt() - - # make the dialog and show it modally: - ccd = crop_cp_dialog ( None , 'Crop CPs' ) - retval = ccd.ShowModal() - # only if Okay was clicked the state of the radio button is used - if retval == wx.ID_OK : - if ccd.choice == 0 : - inside = False - else : - inside = True - # otherwise we cancel the operation and return. - else : - return 0 - - # now the preliminaries are sorted, we can call the workhorse routine. - - return crop_cps ( pano , inside ) - -# the main routine is what's called if this script has been called from -# the command line. In this case we do the CLI thing, so we parse the -# argument vector and act accordingly. - -def main() : - - # when called from the command line, we import the argparse module. - # This may not be part of the standard library on your system. - - import argparse - - # and create an argument parser. - - parser = argparse.ArgumentParser ( - formatter_class=argparse.RawDescriptionHelpFormatter , - description = gpl + ''' - crop_cp will remove all CPs which are either outside the panorama's ROI - or inside it's ROI if the inside flag is set - ''' ) - - parser.add_argument('-o', '--output', - metavar='<output file>', - default = None, - type=str, - help='write output to a different file') - - parser.add_argument('-i', '--inside', - action='store_true', - default = False, - help='remove CPs inside ROI') - - parser.add_argument('input' , - metavar = '<pto file>' , - type = str , - help = 'pto file to be processed' ) - - if len ( sys.argv ) < 2 : - parser.print_help() - return - - # we parse the arguments into a global variable so we can access - # them from everywhere without having to pass them around - - global args - - args = parser.parse_args() - - if args.output : - print ( 'output: ' , args.output ) - print ( 'ptofile:' , args.input ) - - # first we see if we can open the input file - - ifs = hsi.ifstream ( args.input ) - if not ifs.good() : - raise CropCpError ( 'cannot open input file %s' % args.input ) - - pano = hsi.Panorama() - success = pano.readData ( ifs ) - del ifs - if success != hsi.DocumentData.SUCCESSFUL : - raise CropCpError ( 'input file %s contains invalid data' % args.input ) - - # if a separate output file was chosen, we open it now to avoid - # later disappointments - - if args.output: - ofs = hsi.ofstream ( args.output ) - if not ofs.good() : - raise CropCpError ( 'cannot open output file %s' % args.output ) - - # we've checked the args, now we call crop_cps() - - crop_cps ( pano , args.inside ) - - # if no different output file was specified, overwrite the input - - if not args.output : - ofs = hsi.ofstream ( args.input ) - if not ofs.good() : - raise CropCpError ( 'cannot open file %s for output' % args.input ) - - success = pano.writeData ( ofs ) - del ofs - if success != hsi.DocumentData.SUCCESSFUL : - raise CropCpError ( 'error writing pano to %s' % args.input ) - - # done. - - return 0 - -# are we main? This means we've been called from the command line -# as a standalone program, so we have to go through the main() function. - -if __name__ == "__main__": - try: - main() - except CropCpError as e : - print ( 'Run Time Error: %s' % e ) diff --git a/src/hugin_script_interface/plugins/shooting_pattern.py b/src/hugin_script_interface/plugins/shooting_pattern.py old mode 100755 new mode 100644 --- a/src/hugin_script_interface/plugins/shooting_pattern.py +++ b/src/hugin_script_interface/plugins/shooting_pattern.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - from __future__ import print_function # shooting_pattern.py - apply a 6-1-1 shooting pattern to a panorama @@ -41,8 +39,6 @@ # @name 6-1-1 Shooting Pattern # @description apply a shooting pattern (6 around, 1 up 1 down) # @sys win nix mac -# @api-max 2016.1 -# @api-min 2011.1 # the position routine will apply yaw, pitch to an image diff --git a/src/hugin_script_interface/plugins/top_five.py b/src/hugin_script_interface/plugins/top_five.py old mode 100755 new mode 100644 --- a/src/hugin_script_interface/plugins/top_five.py +++ b/src/hugin_script_interface/plugins/top_five.py @@ -1,11 +1,7 @@ -#!/usr/bin/env python - from __future__ import print_function # @category Control Points # @name keep 5 CPs per image pair -# @api-min 2011.1 -# @api-max 2016.1 # top_five.py - keep the five best CPs for each image pair @@ -66,7 +62,10 @@ # finally we attach the new CP vector to the panorama - print ( 'keeping %d control points' % len ( cpv ) ) + print ( 'keeping %d control points:' % len ( cpv ) ) + for cp in cpv: + print ( "%d %d %d %d" % ( cp.x1 , cp.y1 , cp.x2 , cp.y2 ) ) + pano.setCtrlPoints ( cpv ) return 0 diff --git a/src/hugin_script_interface/plugins/woa.py b/src/hugin_script_interface/plugins/woa.py old mode 100755 new mode 100644 --- a/src/hugin_script_interface/plugins/woa.py +++ b/src/hugin_script_interface/plugins/woa.py @@ -1,4 +1,3 @@ -#! /usr/bin/env python # -*- coding: utf-8 -*- from __future__ import print_function @@ -24,8 +23,6 @@ """ # @category Control Points # @name Warped Overlap Analysis -# @api-min 2014.0 -# @api-max 2016.3 # note that if you want to read the script, it's written bottom-up, so the # higher-level routines are towards the end.
