GIMP 似乎在 2 个目录中查找插件。一个是C:\Program Files\GIMP 2\lib\gimp\2.0\plug-ins
C:\Users\Sam\AppData\Roaming\GIMP\2.10\plug-ins
但是,当我查看plug-ins
文件夹时,所有内容都是独立的可执行文件,没有 Python 脚本。
我找到了一个我想运行的 Python 脚本。作者说只需将 Python 源代码文件粘贴到plug-ins
文件夹中,然后就可以通过菜单在 GUI 中访问该脚本。考虑到文件夹Xtns/Utils
中已有的其他内容,这似乎不对plug-ins
'''GIMP plug-in to stitch two images together into a panorama.'''
abort = False
# These should all be standard modules
import sys
import os
import copy
import math
import struct
import time
import gimp
import gimpplugin
from gimpenums import *
import pygtk
pygtk.require('2.0')
import gtk
import cPickle as pickle
#------------ MAIN PLUGIN CLASS
class stitch_plugin(gimpplugin.plugin):
'''The main plugin class defines and installs the stitch_panorama function.'''
version = '0.9.6'
def query(self):
gimp.install_procedure("stitch_panorama",
"Stitch two images together to make a panorama",
"Stitch two images together to make a panorama (ver. " + \
stitch_plugin.version+")",
"Thomas R. Metcalf",
"Thomas R. Metcalf",
"2005",
"<Toolbox>/Xtns/Utils/Stitch _panorama",
"RGB*, GRAY*",EXTENSION,
[(PDB_INT32, "run-mode", "interactive/noninteractive"),
],
[])
# stitch_panorama is the main routine where all the work is done.
def stitch_panorama(self, mode, image_list=None, control_points=None):
'''Stitch together two images into a panorama.
First get a set of "control points" which define matching
locations in the two images. Then use these control points to
balance the color and warp the images into a third, panoramic
image.'''
if not abort:
if not image_list: image_list = gimp.image_list()
# Select which image is the reference and which is transformed.
image_list=select_images(image_list,mode)
if check_image_list_ok(image_list,mode):
image_list[0].disable_undo()
image_list[1].disable_undo()
# fire up the user interface which does all the work.
panorama = stitch_control_panel(control_points,image_list,mode)
# clean up a bit
for img in image_list:
if img:
img.clean_all()
img.enable_undo()
update_image_layers(img) # is this necessary?
gimp.pdb.gimp_displays_flush()
return panorama
# Pau.
#------------ SUPPORTING CLASS DEFINITIONS
class control_point(object):
'''Each control point gives matching locations in two images.'''
def __init__(self,x1,y1,x2,y2,correlation=None,colorbalance=True):
self.xy = (float(x1),float(y1),float(x2),float(y2))
self.correlation = correlation
self.colorbalance = colorbalance
def x1(self): return self.xy[0]
def y1(self): return self.xy[1]
def x2(self): return self.xy[2]
def y2(self): return self.xy[3]
def cb(self):
try:
colorbalance = self.colorbalance
except AttributeError:
colorbalance = True
return colorbalance
def invert(self):
try:
colorbalance = self.colorbalance
except AttributeError:
colorbalance = True
return control_point(self.x2(),self.y2(),self.x1(),self.y1(),
self.correlation,colorbalance)
minradius = 20.0 # min radius for color averaging
class stitchable(object):
'''Two images and their control points for stitching.'''
def __init__(self,mode,rimage,timage,control_points=None):
self.mode = mode # Mode: interactive/noninteractive
self.rimage = rimage # the reference image object
self.timage = timage # the transformed image object
self.cimage = None # temporary image for correlation
self.dimage = None # temporary image for undistorted image
self.rimglayer = None # main image layer in reference image
self.timglayer = None # main image layer in transformed image
self.rcplayer = None # the reference control point display layer
self.tcplayer = None # the transform control point display layer
self.control_points = control_points # the warping control points
self.panorama = None # the resulting panoramic image
self.rlayer = None # the reference layer in self.panorama
self.tlayer = None # the transformed layer in self.panorama
self.rmask = None # the reference layer mask
self.tmask = None # the transformed layer mask
self.rxy = None # x,y of reference corners [x1,y1,x2,y2]
self.txy = None # x,y of transformed corners [x1,y1,x2,y2]
self.interpolation = INTERPOLATION_CUBIC
self.supersample = 1
self.cpcorrelate = True # correlate control points?
self.recursion_level = 5
self.clip_result = 1 # this must be 1 or gimp will crash (segmentation fault)
self.colorbalance = True # color balance?
self.colorradius = minradius # color radius
self.blend = True # blend edges?
self.blend_fraction = 0.25 # size of blend along edges (fraction of image size)
self.rmdistortion = True # remove distortion?
self.condition_number = None # the condition number of the transform
self.progressbar = None # the progress bar widget
self.update()
def __getitem__(self,index):
'''Make the stitchable class indexable over the control points.'''
return self.control_points[index]
def update(self):
if self.control_points:
self.npoints = len(self.control_points)
rarray,tarray = self.arrays()
self.transform = compute_transform_matrix(rarray,tarray,self)
self.errors = compute_control_point_errors(self)
else:
self.npoints = 0
self.transform = None
self.errors = None
def set_control_points(self,control_points):
'''Se the whole control point list.'''
self.control_points = control_points
self.update()
def add_control_point(self,cp):
'''Add a control point to the control_points list.
The control_point parameter should be of the control_point
class.'''
assert cp.__class__ is control_point, \
'control_point parameter is not an instance of the control_point class.'
if self.control_points:
self.control_points.append(cp)
else:
self.control_points = [cp]
self.update()
def delete_control_point(self,index):
'''Delete a control point from the control point list.'''
if self.control_points:
self.control_points.pop(index)
self.update()
def replace_control_point(self,cp,index):
'''Replace a control point in the control point list.'''
if self.control_points:
if index < len(self.control_points):
self.control_points[index] = cp
self.update()
def move_control_point_up(self,index):
if self.control_points:
if index > 0 and index < self.npoints:
cp1 = self.control_points[index]
cp2 = self.control_points[index-1]
self.control_points[index] = cp2
self.control_points[index-1] = cp1
self.update()
def move_control_point_down(self,index):
if self.control_points:
if index >=0 and index <self.npoints-1:
cp1 = self.control_points[index]
cp2 = self.control_points[index+1]
self.control_points[index] = cp2
self.control_points[index+1] = cp1
self.update()
def inverse_control_points(self):
'''Invert the control point list and return the inverse.'''
inverse = []
for c in self.control_points:
inverse.append(c.invert())
return inverse
def arrays(self):
'''Get the reference and transformed control points as lists.'''
rarray = []
tarray = []
for i in range(self.npoints):
rarray.append([self.control_points[i].x1(),self.control_points[i].y1(),1.0])
tarray.append([self.control_points[i].x2(),self.control_points[i].y2(),1.0])
return (rarray,tarray)
def color(self,control_point,radius=minradius):
'''Get the color values at a control point in each image.
The return value is a two-element tuple in which each entry
is a color tuple.'''
assert control_point in self.control_points,'Bad control point'
rnx = self.rimage.width # the dimensions of the images
rny = self.rimage.height
tnx = self.timage.width
tny = self.timage.height
# Make sure that the radius is not so large that the
# average circle extends beyond the edge.
if radius > control_point.x1():
radius = max(control_point.x1(),1.0)
if radius > control_point.y1():
radius = max(control_point.y1(),1.0)
if control_point.x1()+radius > rnx-1:
radius = max(rnx-control_point.x1()-1,1.0)
if control_point.y1()+radius > rny-1:
radius = max(rny-control_point.y1()-1,1.0)
#if __debug__: print 'radius: ',radius,control_point.x1(),control_point.y1(),rnx,rny
# the scale of the transformed image may be different from the scale of the
# reference image. So, the radius should be scaled as well.
if self.transform:
(sscale,srotation) = transform2rs(self.transform)
tradius = max(radius/sscale,1.0)
else:
tradius = radius
# Check size of tradius
if tradius > control_point.x2():
tradius = max(control_point.x2(),1.0)
if self.transform: radius = max(tradius*sscale,1.0)
if tradius > control_point.y2():
tradius = max(control_point.y2(),1.0)
if self.transform: radius = max(tradius*sscale,1.0)
if control_point.x2()+tradius > tnx-1:
tradius = max(tnx-control_point.x2()-1,1.0)
if self.transform: radius = max(tradius*sscale,1.0)
if control_point.y2()+tradius > tny-1:
tradius = max(tny-control_point.y2()-1,1.0)
if self.transform: radius = max(tradius*sscale,1.0)
#if __debug__: print 'radius: ',tradius,control_point.x2(),control_point.y2(),tnx,tny
##if __debug__: print 'color radii are ',radius,tradius
##if __debug__:
## print 'using a color radius of ',radius,tradius
return ( gimp.pdb.gimp_image_pick_color(self.rimage,
self.rimglayer,
control_point.x1(),
control_point.y1(),
0, # use the composite image, ignore the drawable
1,radius),
gimp.pdb.gimp_image_pick_color(self.timage,
self.timglayer,
control_point.x2(),
control_point.y2(),
0, # use the composite image, ignore the drawable
1,tradius)
)
def cbtest(self,control_point):
'''Get the color balance flag for a control point.'''
assert control_point in self.control_points,'Bad control point'
return control_point.cb()
def cbtests(self):
'''Get flag to determine if a control point will be used in the color balancing.'''
return [self.cbtest(self.control_points[c])
for c in range(self.npoints)] # iterates over self.control_points
def colors(self):
'''Get the color values at all the control points.'''
if self.errors:
return [self.color(self.control_points[c],self.colorradius)
for c in range(self.npoints)] # iterates over self.control_points
else:
return [self.color(c) for c in self] # iterates over self.control_points
def brightness(self,control_point,radius=minradius):
'''Compute the brightness of a control point in each image.
The return value is a two-element tuple in which the entries
are the brightness of the two images in the stitchable object.'''
c = self.color(control_point,radius)
brightness1 = 0
brightness2 = 0
n = 0.0
for b1,b2 in zip(c[0],c[1]): # iterate over both image colors simultaneously
brightness1 += b1
brightness2 += b2
n += 1.0
# the brightness is the mean of the values
return (int(round(brightness1/n)),int(round(brightness2/n)))
def brightnesses(self):
'''Get the brightness values at all the control points.'''
if self.errors:
return [self.brightness(self.control_points[c],self.colorradius)
for c in range(self.npoints)] # iterates over self.control_points
else:
return [self.brightness(c) for c in self] # iterates over self.control_points
def value(self,control_point,radius=minradius):
'''Compute the value of a control point in each image.
The return value is a two-element tuple in which the entries
are the value of the two images in the stitchable object.'''
c = self.color(control_point,radius)
# the value is the max of the color channels
return ( max(c[0]), max(c[1]) )
def values(self):
'''Get the values at all the control points.'''
if self.errors:
return [self.value(self.control_points[c],self.colorradius)
for c in range(self.npoints)]
else:
return [self.value(c) for c in self] # iterates over self.control_points
答案1
在 Windows 上,Gimp 自 2.8 版起就内置了 Python 支持。要检查它是否正常工作,请执行以下操作:
- 你应该有菜单
Filters>Python-fu>Console
- 它应该打开一个 Python 控制台。
- 你还应该滤镜>装饰>雾(2.10)或文件管理器>渲染>云>雾(2.8,根据记忆)。
另一方面,您的过滤器似乎非常老旧(2005 年,因此与 Gimp 2.2 是同一时期的)。上面的代码不完整,完整代码超过 3800 行(检索到的这里)。
此完整代码已正确注册,但 Gimp 不再允许菜单位置,因此实际菜单位置是滤镜>实用工具>拼接全景图。
该插件从 Gimp 2.10 开始,但我没有进一步测试。这个插件在 2005 年可能很有用,但现在全景拼接用胡金。