-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmodifier.py
More file actions
235 lines (214 loc) · 7.78 KB
/
Copy pathmodifier.py
File metadata and controls
235 lines (214 loc) · 7.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# -*- coding: utf-8 -*-
"""
This is a modifier layer such as blur, sharpen, posterize, etc
"""
from PIL import ImageFilter, ImageOps, ImageEnhance
from imageTools import *
from smartimage.layer import *
from smartimage.errors import SmartimageError
class Modifier(Layer):
"""
This is a modifier layer such as blur, sharpen, posterize, etc
"""
def __init__(self,parent:Layer,xml:str):
Layer.__init__(self,parent,xml)
@property
def filterType(self)->str:
"""
supported types:
brightness,contrast,rotate
"""
return self._getProperty('type','?')
@property
def amount(self):
"""
supported types:
brightness,contrast,rotate,sharpen,unsharp_mask
"""
return self._getPropertyPercent('amount',1.0)
@property
def edge(self)->str:
"""
treatment for edges
options: mirror,repeat,clamp,[color]
useful for:
convolve, expand_border, others?
"""
return self._getProperty('edge','mirror')
@property
def add(self)->float:
"""
add a constant to the convolved value
useful, for instance, in shifting negative emboss values
useful for:
convolve
"""
return float(self._getProperty('add',0))
@property
def divide(self)->float:
"""
divide the convolved value by a constant
useful for:
convolve
"""
divide=float(self._getProperty('divide',0))
if divide==0:
divide=None
return divide
@property
def matrix(self)->list:
"""
a 3x3 or 5x5 convolution matrix
useful for:
convolve
"""
ret=[]
matrix=self._getProperty('matrix','[[0,0,0],[0,1,0],[0,0,0]]').replace(' ','')
if matrix.startswith('[['):
matrix=matrix[1:-1]
else:
matrix=matrix
matrix=matrix.split('[')[1:]
size=None
for row in matrix:
row=row.split(']',1)[0].split(',')
if (size is None and len(row) not in [5,3]) or size!=len(row):
size=len(row)
info=(matrix,len(row))
raise SmartimageError(self,'Convolution matrix "%s" size %d not 3x3 or 5x5!'%info)
ret.append([float(v) for v in row])
if len(ret)!=size:
info=(matrix,len(ret))
raise SmartimageError(self,'Convolution matrix "%s" size %d not 3x3 or 5x5!'%info)
return ret
@property
def channels(self)->str:
"""
only apply the effect to the given channels
useful for:
convolve,others?
"""
return self._getProperty('channels','rgba').lower()
@property
def modifierOpacity(self)->float:
"""
the opacity of the modifier alone, for instance drop shadows
"""
return self._getPropertyPercent('modifierOpacity',1.0)
@property
def blurRadius(self)->float:
"""
supported types:
gaussian_blur,box_blur,shadow,unsharp_mask
"""
return float(self._getProperty('blurRadius',0))
@property
def threshold(self)->float:
"""
supported types:
unsharp_mask
"""
return float(self._getProperty('threshold',0))
def roi(self,image:PilPlusImage)->Union[PilPlusImage,None]:
"""
the region of interest
"""
# only certain transforms change the region of interest
if self.filterType in ['shadow','blur','flip','mirror']:
image=self._transform(image)
return image
def _transform(self,img):
"""
For a list of more transformations available, see:
http://pillow.readthedocs.io/en/latest/reference/ImageFilter.html
"""
filterType=self.filterType
if filterType=='shadow':
offsX=10
offsY=10
blurRadius=self.blurRadius
shadow=img.copy()
control=ImageEnhance.Brightness(shadow)
shadow=control.enhance(0)
final=Image.new("RGBA",(img.width+abs(offsX),img.height+abs(offsX)))
final.putalpha(int(self.modifierOpacity*255))
dest=(int(max(offsX-blurRadius,0)),int(max(offsY-blurRadius,0)))
final.alpha_composite(shadow,dest=dest)
if blurRadius>0:
final=final.filter(ImageFilter.GaussianBlur(radius=blurRadius))
final.alpha_composite(img,dest=(max(-offsX,0),max(-offsY,0)))
img=final
elif filterType=='convolve':
# TODO: make use of separable convoutions, fourier domain convolutions,
# and other speed-ups
matrix=self.matrix
ImageFilter.Kernel((len(matrix),len(matrix)),matrix,scale=self.divide,offset=self.add)
elif filterType=='autocrop':
# the idea is you cut off as many rows from each side that are all alpha=0
raise NotImplementedError()
elif filterType=='brightness':
control=ImageEnhance.Brightness(img)
control.amount=self.amount
elif filterType=='contrast':
control=ImageEnhance.Contrast(img)
control.amount=self.amount
elif filterType=='saturation':
control=ImageEnhance.Color(img)
control.amount=self.amount
elif filterType=='blur':
img=img.filter(ImageFilter.BLUR)#,self.amount)
elif filterType=='gaussian_blur':
blurRadius=self.blurRadius
if blurRadius>0:
img=img.filter(ImageFilter.GaussianBlur(radius=blurRadius))
elif filterType=='box_blur':
blurRadius=self.blurRadius
if blurRadius>0:
img.filter(ImageFilter.BoxBlur(radius=blurRadius))
elif filterType=='unsharp_mask':
ImageFilter.UnsharpMask(radius=self.blurRadius,
percent=self.amount*100,threshold=self.threshold)
elif filterType=='contour':
img=img.filter(ImageFilter.CONTOUR,self.amount)
elif filterType=='detail':
img=img.filter(ImageFilter.DETAIL,self.amount)
elif filterType=='edge_enhance':
img=img.filter(ImageFilter.EDGE_ENHANCE,self.amount)
elif filterType=='edge_enhance_more':
img=img.filter(ImageFilter.EDGE_ENHANCE_MORE,self.amount)
elif filterType=='emboss':
img=img.filter(ImageFilter.EMBOSS,self.amount)
elif filterType=='edge_detect':
img=img.filter(ImageFilter.FIND_EDGES,self.amount)
elif filterType=='smooth':
img=img.filter(ImageFilter.SMOOTH,self.amount)
elif filterType=='smooth_more':
img=img.filter(ImageFilter.SMOOTH_MORE,self.amount)
elif filterType=='sharpen':
img=img.filter(ImageFilter.SHARPEN,self.amount)
elif filterType=='invert':
img=ImageOps.invert(img)
elif filterType=='flip':
img=ImageOps.flip(img)
elif filterType=='mirror':
img=ImageOps.mirror(img)
elif filterType=='posterize':
img=ImageOps.posterize(img)
elif filterType=='solarize':
img=ImageOps.solarize(img)
else:
raise SmartimageError(self,'Unknown modifier "%s"'%filterType)
return img
def renderImage(self,renderContext=None)->Union[PilPlusImage,None]:
"""
WARNING: Do not modify the image without doing a .copy() first!
"""
opacity=self.opacity
if opacity<=0.0 or not self.visible:
return None
image=Layer.renderImage(self,renderContext)
if image is not None:
image=self._transform(image.copy())
if self.opacity<1.0:
adjustOpacity(image,opacity)
return image