Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1""":mod:`wand.image` --- Image objects 

2~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

3 

4Opens and manipulates images. Image objects can be used in :keyword:`with` 

5statement, and these resources will be automatically managed (even if any 

6error happened):: 

7 

8 with Image(filename='pikachu.png') as i: 

9 print('width =', i.width) 

10 print('height =', i.height) 

11 

12""" 

13import ctypes 

14import functools 

15import numbers 

16import weakref 

17 

18from . import assertions 

19from .api import libc, libmagick, library 

20from .color import Color 

21from .compat import (abc, binary, binary_type, encode_filename, file_types, 

22 PY3, string_type, text, xrange) 

23from .exceptions import (MissingDelegateError, WandException, 

24 WandRuntimeError, WandLibraryVersionError) 

25from .font import Font 

26from .resource import DestroyedResourceError, Resource 

27from .cdefs.structures import (CCObjectInfo, ChannelFeature, GeometryInfo, 

28 PixelInfo, RectangleInfo) 

29from .version import MAGICK_VERSION_NUMBER, MAGICK_HDRI 

30 

31 

32__all__ = ('ALPHA_CHANNEL_TYPES', 'AUTO_THRESHOLD_METHODS', 'CHANNELS', 

33 'COLORSPACE_TYPES', 'COMPARE_METRICS', 'COMPOSITE_OPERATORS', 

34 'COMPRESSION_TYPES', 'DISPOSE_TYPES', 'DISTORTION_METHODS', 

35 'DITHER_METHODS', 'EVALUATE_OPS', 'FILTER_TYPES', 'FUNCTION_TYPES', 

36 'GRAVITY_TYPES', 'IMAGE_LAYER_METHOD', 'IMAGE_TYPES', 

37 'INTERLACE_TYPES', 'KERNEL_INFO_TYPES', 'MORPHOLOGY_METHODS', 

38 'NOISE_TYPES', 'ORIENTATION_TYPES', 'PIXEL_INTERPOLATE_METHODS', 

39 'RENDERING_INTENT_TYPES', 'SPARSE_COLOR_METHODS', 'STATISTIC_TYPES', 

40 'STORAGE_TYPES', 'VIRTUAL_PIXEL_METHOD', 'UNIT_TYPES', 

41 'BaseImage', 'ChannelDepthDict', 'ChannelImageDict', 

42 'ClosedImageError', 'HistogramDict', 'Image', 'ImageProperty', 

43 'Iterator', 'Metadata', 'OptionDict', 'manipulative', 

44 'ArtifactTree', 'ProfileDict', 'ConnectedComponentObject') 

45 

46 

47#: (:class:`tuple`) The list of :attr:`~wand.image.BaseImage.alpha_channel` 

48#: types. 

49#: 

50#: - ``'undefined'`` 

51#: - ``'activate'`` 

52#: - ``'background'`` 

53#: - ``'copy'`` 

54#: - ``'deactivate'`` 

55#: - ``'discrete'`` - Only available in ImageMagick-7 

56#: - ``'extract'`` 

57#: - ``'off'`` - Only available in ImageMagick-7 

58#: - ``'on'`` - Only available in ImageMagick-7 

59#: - ``'opaque'`` 

60#: - ``'reset'`` - Only available in ImageMagick-6 

61#: - ``'set'`` 

62#: - ``'shape'`` 

63#: - ``'transparent'`` 

64#: - ``'flatten'`` - Only available in ImageMagick-6 

65#: - ``'remove'`` 

66#: 

67#: .. seealso:: 

68#: `ImageMagick Image Channel`__ 

69#: Describes the SetImageAlphaChannel method which can be used 

70#: to modify alpha channel. Also describes AlphaChannelType 

71#: 

72#: __ http://www.imagemagick.org/api/channel.php#SetImageAlphaChannel 

73ALPHA_CHANNEL_TYPES = ('undefined', 'activate', 'background', 'copy', 

74 'deactivate', 'extract', 'opaque', 'reset', 'set', 

75 'shape', 'transparent', 'flatten', 'remove', 

76 'associate', 'disassociate') 

77if MAGICK_VERSION_NUMBER >= 0x700: # pragma: no cover 

78 ALPHA_CHANNEL_TYPES = ('undefined', 'activate', 'associate', 'background', 

79 'copy', 'deactivate', 'discrete', 'disassociate', 

80 'extract', 'off', 'on', 'opaque', 'remove', 'set', 

81 'shape', 'transparent') 

82 

83 

84#: (:class:`tuple`) The list of methods used by 

85#: :meth:`Image.auto_threshold() <wand.image.BaseImage.auto_threshold>` 

86#: 

87#: - ``'undefined'`` 

88#: - ``'kapur'`` 

89#: - ``'otsu'`` 

90#: - ``'triangle'`` 

91#: 

92#: .. versionadded:: 0.5.5 

93AUTO_THRESHOLD_METHODS = ('undefined', 'kapur', 'otsu', 'triangle') 

94 

95 

96#: (:class:`dict`) The dictionary of channel types. 

97#: 

98#: - ``'undefined'`` 

99#: - ``'red'`` 

100#: - ``'gray'`` 

101#: - ``'cyan'`` 

102#: - ``'green'`` 

103#: - ``'magenta'`` 

104#: - ``'blue'`` 

105#: - ``'yellow'`` 

106#: - ``'alpha'`` 

107#: - ``'opacity'`` 

108#: - ``'black'`` 

109#: - ``'index'`` 

110#: - ``'composite_channels'`` 

111#: - ``'all_channels'`` 

112#: - ``'sync_channels'`` 

113#: - ``'default_channels'`` 

114#: 

115#: .. seealso:: 

116#: 

117#: `ImageMagick Color Channels`__ 

118#: Lists the various channel types with descriptions of each 

119#: 

120#: __ http://www.imagemagick.org/Magick++/Enumerations.html#ChannelType 

121#: 

122#: .. versionchanged:: 0.5.5 

123#: Deprecated ``true_alpha``, ``rgb_channels``, and ``gray_channels`` 

124#: values in favor of MagickCore channel parser. 

125#: 

126CHANNELS = dict(undefined=0, red=1, gray=1, cyan=1, green=2, magenta=2, 

127 blue=4, yellow=4, alpha=8, opacity=8, black=32, index=32, 

128 composite_channels=47, all_channels=134217727, true_alpha=64, 

129 rgb=7, rgb_channels=7, gray_channels=1, sync_channels=256, 

130 default_channels=134217719) 

131if MAGICK_VERSION_NUMBER >= 0x700: # pragma: no cover 

132 CHANNELS = dict(undefined=0, red=1, gray=1, cyan=1, green=2, magenta=2, 

133 blue=4, yellow=4, black=8, alpha=16, opacity=16, index=32, 

134 readmask=0x0040, write_mask=128, meta=256, 

135 composite_channels=31, all_channels=134217727, 

136 true_alpha=256, rgb=7, rgb_channels=7, gray_channels=1, 

137 sync_channels=131072, default_channels=134217727) 

138 

139 

140#: (:class:`tuple`) The list of colorspaces. 

141#: 

142#: - ``'undefined'`` 

143#: - ``'rgb'`` 

144#: - ``'gray'`` 

145#: - ``'transparent'`` 

146#: - ``'ohta'`` 

147#: - ``'lab'`` 

148#: - ``'xyz'`` 

149#: - ``'ycbcr'`` 

150#: - ``'ycc'`` 

151#: - ``'yiq'`` 

152#: - ``'ypbpr'`` 

153#: - ``'yuv'`` 

154#: - ``'cmyk'`` 

155#: - ``'srgb'`` 

156#: - ``'hsb'`` 

157#: - ``'hsl'`` 

158#: - ``'hwb'`` 

159#: - ``'rec601luma'`` - Only available with ImageMagick-6 

160#: - ``'rec601ycbcr'`` 

161#: - ``'rec709luma'`` - Only available with ImageMagick-6 

162#: - ``'rec709ycbcr'`` 

163#: - ``'log'`` 

164#: - ``'cmy'`` 

165#: - ``'luv'`` 

166#: - ``'hcl'`` 

167#: - ``'lch'`` 

168#: - ``'lms'`` 

169#: - ``'lchab'`` 

170#: - ``'lchuv'`` 

171#: - ``'scrgb'`` 

172#: - ``'hsi'`` 

173#: - ``'hsv'`` 

174#: - ``'hclp'`` 

175#: - ``'xyy'`` - Only available with ImageMagick-7 

176#: - ``'ydbdr'`` 

177#: 

178#: .. seealso:: 

179#: 

180#: `ImageMagick Color Management`__ 

181#: Describes the ImageMagick color management operations 

182#: 

183#: __ http://www.imagemagick.org/script/color-management.php 

184#: 

185#: .. versionadded:: 0.3.4 

186COLORSPACE_TYPES = ('undefined', 'rgb', 'gray', 'transparent', 'ohta', 'lab', 

187 'xyz', 'ycbcr', 'ycc', 'yiq', 'ypbpr', 'yuv', 'cmyk', 

188 'srgb', 'hsb', 'hsl', 'hwb', 'rec601luma', 'rec601ycbcr', 

189 'rec709luma', 'rec709ycbcr', 'log', 'cmy', 'luv', 'hcl', 

190 'lch', 'lms', 'lchab', 'lchuv', 'scrgb', 'hsi', 'hsv', 

191 'hclp', 'ydbdr') 

192if MAGICK_VERSION_NUMBER >= 0x700: # pragma: no cover 

193 COLORSPACE_TYPES = ('undefined', 'cmy', 'cmyk', 'gray', 'hcl', 'hclp', 

194 'hsb', 'hsi', 'hsl', 'hsv', 'hwb', 'lab', 'lch', 

195 'lchab', 'lchuv', 'log', 'lms', 'luv', 'ohta', 

196 'rec601ycbcr', 'rec709ycbcr', 'rgb', 'scrgb', 'srgb', 

197 'transparent', 'xyy', 'xyz', 'ycbcr', 'ycc', 'ydbdr', 

198 'yiq', 'ypbpr', 'yuv') 

199 

200#: (:class:`tuple`) The list of compare metric types used by 

201#: :meth:`Image.compare() <wand.image.BaseImage.compare>` and 

202#: :meth:`Image.similarity() <wand.image.BaseImage.similarity>` methods. 

203#: 

204#: - ``'undefined'`` 

205#: - ``'absolute'`` 

206#: - ``'fuzz'`` 

207#: - ``'mean_absolute'`` 

208#: - ``'mean_error_per_pixel'`` 

209#: - ``'mean_squared'`` 

210#: - ``'normalized_cross_correlation'`` 

211#: - ``'peak_absolute'`` 

212#: - ``'peak_signal_to_noise_ratio'`` 

213#: - ``'perceptual_hash'`` - Available with ImageMagick-7 

214#: - ``'root_mean_square'`` 

215#: - ``'structural_similarity'`` - Available with ImageMagick-7 

216#: - ``'structural_dissimilarity'`` - Available with ImageMagick-7 

217#: 

218#: .. seealso:: 

219#: 

220#: `ImageMagick Compare Operations`__ 

221#: 

222#: __ http://www.imagemagick.org/Usage/compare/ 

223#: 

224#: .. versionadded:: 0.4.3 

225#: 

226#: .. versionchanged:: 0.5.4 - Remapped :c:data:`MetricType` enum. 

227COMPARE_METRICS = ('undefined', 'absolute', 

228 'mean_absolute', 'mean_error_per_pixel', 

229 'mean_squared', 'peak_absolute', 

230 'peak_signal_to_noise_ratio', 'root_mean_square', 

231 'normalized_cross_correlation', 'fuzz') 

232if MAGICK_VERSION_NUMBER >= 0x700: # pragma: no cover 

233 COMPARE_METRICS = ('undefined', 'absolute', 'fuzz', 'mean_absolute', 

234 'mean_error_per_pixel', 'mean_squared', 

235 'normalized_cross_correlation', 'peak_absolute', 

236 'peak_signal_to_noise_ratio', 'perceptual_hash', 

237 'root_mean_square', 'structural_similarity', 

238 'structural_dissimilarity') 

239 

240 

241#: (:class:`tuple`) The list of complex operators used by 

242#: :meth:`Image.complex() <wand.image.BaseImage.complex>`. 

243#: 

244#: - ``'undefined'`` 

245#: - ``'add'`` 

246#: - ``'conjugate'`` 

247#: - ``'divide'`` 

248#: - ``'magnitude',`` 

249#: - ``'multiply'`` 

250#: - ``'real_imaginary'`` 

251#: - ``'subtract'`` 

252#: 

253#: .. versionadded:: 0.5.5 

254COMPLEX_OPERATORS = ('undefined', 'add', 'conjugate', 'divide', 'magnitude', 

255 'multiply', 'real_imaginary', 'subtract') 

256 

257 

258#: (:class:`tuple`) The list of composition operators 

259#: 

260#: - ``'undefined'`` 

261#: - ``'alpha'`` - Only available with ImageMagick-7 

262#: - ``'atop'`` 

263#: - ``'blend'`` 

264#: - ``'blur'`` 

265#: - ``'bumpmap'`` 

266#: - ``'change_mask'`` 

267#: - ``'clear'`` 

268#: - ``'color_burn'`` 

269#: - ``'color_dodge'`` 

270#: - ``'colorize'`` 

271#: - ``'copy_black'`` 

272#: - ``'copy_blue'`` 

273#: - ``'copy'`` 

274#: - ``'copy_alpha'`` - Only available with ImageMagick-7 

275#: - ``'copy_cyan'`` 

276#: - ``'copy_green'`` 

277#: - ``'copy_magenta'`` 

278#: - ``'copy_opacity'`` - Only available with ImageMagick-6 

279#: - ``'copy_red'`` 

280#: - ``'copy_yellow'`` 

281#: - ``'darken'`` 

282#: - ``'darken_intensity'`` 

283#: - ``'difference'`` 

284#: - ``'displace'`` 

285#: - ``'dissolve'`` 

286#: - ``'distort'`` 

287#: - ``'divide_dst'`` 

288#: - ``'divide_src'`` 

289#: - ``'dst_atop'`` 

290#: - ``'dst'`` 

291#: - ``'dst_in'`` 

292#: - ``'dst_out'`` 

293#: - ``'dst_over'`` 

294#: - ``'exclusion'`` 

295#: - ``'hard_light'`` 

296#: - ``'hard_mix'`` 

297#: - ``'hue'`` 

298#: - ``'in'`` 

299#: - ``'intensity'`` - Only available with ImageMagick-7 

300#: - ``'lighten'`` 

301#: - ``'lighten_intensity'`` 

302#: - ``'linear_burn'`` 

303#: - ``'linear_dodge'`` 

304#: - ``'linear_light'`` 

305#: - ``'luminize'`` 

306#: - ``'mathematics'`` 

307#: - ``'minus_dst'`` 

308#: - ``'minus_src'`` 

309#: - ``'modulate'`` 

310#: - ``'modulus_add'`` 

311#: - ``'modulus_subtract'`` 

312#: - ``'multiply'`` 

313#: - ``'no'`` 

314#: - ``'out'`` 

315#: - ``'over'`` 

316#: - ``'overlay'`` 

317#: - ``'pegtop_light'`` 

318#: - ``'pin_light'`` 

319#: - ``'plus'`` 

320#: - ``'replace'`` 

321#: - ``'saturate'`` 

322#: - ``'screen'`` 

323#: - ``'soft_light'`` 

324#: - ``'src_atop'`` 

325#: - ``'src'`` 

326#: - ``'src_in'`` 

327#: - ``'src_out'`` 

328#: - ``'src_over'`` 

329#: - ``'threshold'`` 

330#: - ``'vivid_light'`` 

331#: - ``'xor'`` 

332#: - ``'stereo'`` 

333#: 

334#: .. versionchanged:: 0.3.0 

335#: Renamed from :const:`COMPOSITE_OPS` to :const:`COMPOSITE_OPERATORS`. 

336#: 

337#: .. versionchanged:: 0.5.6 

338#: Operators have been updated to reflect latest changes in C-API. 

339# For ImageMagick-6, ``'add'`` has been renamed to ``'modulus_add'``, 

340#: ``'subtract'`` has been renamed to ``'modulus_subtract'``, 

341#: ``'divide'`` has been split into ``'divide_dst'`` & ``'divide_src'``, and 

342#: ``'minus'`` has been split into ``'minus_dst'`` & ``'minus_src'``. 

343#: 

344#: .. seealso:: 

345#: 

346#: `Compositing Images`__ ImageMagick v6 Examples 

347#: Image composition is the technique of combining images that have, 

348#: or do not have, transparency or an alpha channel. 

349#: This is usually performed using the IM :program:`composite` command. 

350#: It may also be performed as either part of a larger sequence of 

351#: operations or internally by other image operators. 

352#: 

353#: `ImageMagick Composition Operators`__ 

354#: Demonstrates the results of applying the various composition 

355#: composition operators. 

356#: 

357#: __ http://www.imagemagick.org/Usage/compose/ 

358#: __ http://www.rubblewebs.co.uk/imagemagick/operators/compose.php 

359COMPOSITE_OPERATORS = ( 

360 'undefined', 'no', 'modulus_add', 'atop', 'blend', 'bumpmap', 

361 'change_mask', 'clear', 'color_burn', 'color_dodge', 'colorize', 

362 'copy_black', 'copy_blue', 'copy', 'copy_cyan', 'copy_green', 

363 'copy_magenta', 'copy_opacity', 'copy_red', 'copy_yellow', 'darken', 

364 'dst_atop', 'dst', 'dst_in', 'dst_out', 'dst_over', 'difference', 

365 'displace', 'dissolve', 'exclusion', 'hard_light', 'hue', 'in', 'lighten', 

366 'linear_light', 'luminize', 'minus_dst', 'modulate', 'multiply', 'out', 

367 'over', 'overlay', 'plus', 'replace', 'saturate', 'screen', 'soft_light', 

368 'src_atop', 'src', 'src_in', 'src_out', 'src_over', 'modulus_subtract', 

369 'threshold', 'xor', 'divide_dst', 'distort', 'blur', 'pegtop_light', 

370 'vivid_light', 'pin_light', 'linear_dodge', 'linear_burn', 'mathematics', 

371 'divide_src', 'minus_src', 'darken_intensity', 'lighten_intensity', 

372 'hard_mix', 'stereo' 

373) 

374if MAGICK_VERSION_NUMBER >= 0x700: # pragma: no cover 

375 COMPOSITE_OPERATORS = ( 

376 'undefined', 'alpha', 'atop', 'blend', 'blur', 'bumpmap', 

377 'change_mask', 'clear', 'color_burn', 'color_dodge', 'colorize', 

378 'copy_black', 'copy_blue', 'copy', 'copy_cyan', 'copy_green', 

379 'copy_magenta', 'copy_alpha', 'copy_red', 'copy_yellow', 'darken', 

380 'darken_intensity', 'difference', 'displace', 'dissolve', 'distort', 

381 'divide_dst', 'divide_src', 'dst_atop', 'dst', 'dst_in', 'dst_out', 

382 'dst_over', 'exclusion', 'hard_light', 'hard_mix', 'hue', 'in', 

383 'intensity', 'lighten', 'lighten_intensity', 'linear_burn', 

384 'linear_dodge', 'linear_light', 'luminize', 'mathematics', 'minus_dst', 

385 'minus_src', 'modulate', 'modulus_add', 'modulus_subtract', 'multiply', 

386 'no', 'out', 'over', 'overlay', 'pegtop_light', 'pin_light', 'plus', 

387 'replace', 'saturate', 'screen', 'soft_light', 'src_atop', 'src', 

388 'src_in', 'src_out', 'src_over', 'threshold', 'vivid_light', 'xor', 

389 'stereo' 

390 ) 

391 

392#: (:class:`tuple`) The list of :attr:`Image.compression` types. 

393#: 

394#: .. versionadded:: 0.3.6 

395#: .. versionchanged:: 0.5.0 

396#: Support for ImageMagick-6 & ImageMagick-7 

397COMPRESSION_TYPES = ( 

398 'undefined', 'no', 'bzip', 'dxt1', 'dxt3', 'dxt5', 

399 'fax', 'group4', 'jpeg', 'jpeg2000', 'losslessjpeg', 

400 'lzw', 'rle', 'zip', 'zips', 'piz', 'pxr24', 'b44', 

401 'b44a', 'lzma', 'jbig1', 'jbig2' 

402) 

403if MAGICK_VERSION_NUMBER >= 0x700: # pragma: no cover 

404 COMPRESSION_TYPES = ( 

405 'undefined', 'b44a', 'b44', 'bzip', 'dxt1', 'dxt3', 'dxt5', 'fax', 

406 'group4', 'jbig1', 'jbig2', 'jpeg2000', 'jpeg', 'losslessjpeg', 

407 'lzma', 'lzw', 'no', 'piz', 'pxr24', 'rle', 'zip', 'zips' 

408 ) 

409 

410#: (:class:`tuple`) The list of :attr:`BaseImage.dispose` types. 

411#: 

412#: .. versionadded:: 0.5.0 

413DISPOSE_TYPES = ( 

414 'undefined', 

415 'none', 

416 'background', 

417 'previous' 

418) 

419 

420 

421#: (:class:`tuple`) The list of :meth:`BaseImage.distort` methods. 

422#: 

423#: - ``'undefined'`` 

424#: - ``'affine'`` 

425#: - ``'affine_projection'`` 

426#: - ``'scale_rotate_translate'`` 

427#: - ``'perspective'`` 

428#: - ``'perspective_projection'`` 

429#: - ``'bilinear_forward'`` 

430#: - ``'bilinear_reverse'`` 

431#: - ``'polynomial'`` 

432#: - ``'arc'`` 

433#: - ``'polar'`` 

434#: - ``'depolar'`` 

435#: - ``'cylinder_2_plane'`` 

436#: - ``'plane_2_cylinder'`` 

437#: - ``'barrel'`` 

438#: - ``'barrel_inverse'`` 

439#: - ``'shepards'`` 

440#: - ``'resize'`` 

441#: - ``'sentinel'`` 

442#: 

443#: .. versionadded:: 0.4.1 

444DISTORTION_METHODS = ( 

445 'undefined', 'affine', 'affine_projection', 'scale_rotate_translate', 

446 'perspective', 'perspective_projection', 'bilinear_forward', 

447 'bilinear_reverse', 'polynomial', 'arc', 'polar', 'depolar', 

448 'cylinder_2_plane', 'plane_2_cylinder', 'barrel', 'barrel_inverse', 

449 'shepards', 'resize', 'sentinel' 

450) 

451 

452 

453#: (:class:`tuple`) The list of Dither methods. Used by 

454#: :meth:`Image.posterize() <BaseImage.posterize>` and 

455#: :meth:`Image.remap() <BaseImage.remap>` methods. 

456#: 

457#: - ``'undefined'`` 

458#: - ``'no'`` 

459#: - ``'riemersma'`` 

460#: - ``'floyd_steinberg'`` 

461#: 

462#: .. versionadded:: 0.5.0 

463DITHER_METHODS = ('undefined', 'no', 'riemersma', 'floyd_steinberg') 

464 

465 

466#: (:class:`tuple`) The list of evaluation operators. Used by 

467#: :meth:`Image.evaluate() <BaseImage.evaluate>` method. 

468#: 

469#: - ``'undefined'`` 

470#: - ``'abs'`` 

471#: - ``'add'`` 

472#: - ``'addmodulus'`` 

473#: - ``'and'`` 

474#: - ``'cosine'`` 

475#: - ``'divide'`` 

476#: - ``'exponential'`` 

477#: - ``'gaussiannoise'`` 

478#: - ``'impulsenoise'`` 

479#: - ``'laplaciannoise'`` 

480#: - ``'leftshift'`` 

481#: - ``'log'`` 

482#: - ``'max'`` 

483#: - ``'mean'`` 

484#: - ``'median'`` 

485#: - ``'min'`` 

486#: - ``'multiplicativenoise'`` 

487#: - ``'multiply'`` 

488#: - ``'or'`` 

489#: - ``'poissonnoise'`` 

490#: - ``'pow'`` 

491#: - ``'rightshift'`` 

492#: - ``'set'`` 

493#: - ``'sine'`` 

494#: - ``'subtract'`` 

495#: - ``'sum'`` 

496#: - ``'threshold'`` 

497#: - ``'thresholdblack'`` 

498#: - ``'thresholdwhite'`` 

499#: - ``'uniformnoise'`` 

500#: - ``'xor'`` 

501#: 

502#: .. seealso:: 

503#: 

504#: `ImageMagick Image Evaluation Operators`__ 

505#: Describes the MagickEvaluateImageChannel method and lists the 

506#: various evaluations operators 

507#: 

508#: __ http://www.magickwand.org/MagickEvaluateImage.html 

509EVALUATE_OPS = ('undefined', 'add', 'and', 'divide', 'leftshift', 'max', 

510 'min', 'multiply', 'or', 'rightshift', 'set', 'subtract', 

511 'xor', 'pow', 'log', 'threshold', 'thresholdblack', 

512 'thresholdwhite', 'gaussiannoise', 'impulsenoise', 

513 'laplaciannoise', 'multiplicativenoise', 'poissonnoise', 

514 'uniformnoise', 'cosine', 'sine', 'addmodulus', 'mean', 

515 'abs', 'exponential', 'median', 'sum', 'rootmeansquare') 

516if MAGICK_VERSION_NUMBER >= 0x700: # pragma: no cover 

517 EVALUATE_OPS = ('undefined', 'abs', 'add', 'addmodulus', 'and', 'cosine', 

518 'divide', 'exponential', 'gaussiannoise', 'impulsenoise', 

519 'laplaciannoise', 'leftshift', 'log', 'max', 'mean', 

520 'median', 'min', 'multiplicativenoise', 'multiply', 'or', 

521 'poissonnoise', 'pow', 'rightshift', 'rootmeansquare', 

522 'set', 'sine', 'subtract', 'sum', 'thresholdblack', 

523 'threshold', 'thresholdwhite', 'uniformnoise', 'xor') 

524 

525 

526#: (:class:`tuple`) The list of filter types. Used by 

527#: :meth:`Image.resample() <BaseImage.resample>` and 

528#: :meth:`Image.resize() <BaseImage.resize>` methods. 

529#: 

530#: - ``'undefined'`` 

531#: - ``'point'`` 

532#: - ``'box'`` 

533#: - ``'triangle'`` 

534#: - ``'hermite'`` 

535#: - ``'hanning'`` 

536#: - ``'hamming'`` 

537#: - ``'blackman'`` 

538#: - ``'gaussian'`` 

539#: - ``'quadratic'`` 

540#: - ``'cubic'`` 

541#: - ``'catrom'`` 

542#: - ``'mitchell'`` 

543#: - ``'jinc'`` 

544#: - ``'sinc'`` 

545#: - ``'sincfast'`` 

546#: - ``'kaiser'`` 

547#: - ``'welsh'`` 

548#: - ``'parzen'`` 

549#: - ``'bohman'`` 

550#: - ``'bartlett'`` 

551#: - ``'lagrange'`` 

552#: - ``'lanczos'`` 

553#: - ``'lanczossharp'`` 

554#: - ``'lanczos2'`` 

555#: - ``'lanczos2sharp'`` 

556#: - ``'robidoux'`` 

557#: - ``'robidouxsharp'`` 

558#: - ``'cosine'`` 

559#: - ``'spline'`` 

560#: - ``'sentinel'`` 

561#: 

562#: .. seealso:: 

563#: 

564#: `ImageMagick Resize Filters`__ 

565#: Demonstrates the results of resampling images using the various 

566#: resize filters and blur settings available in ImageMagick. 

567#: 

568#: __ http://www.imagemagick.org/Usage/resize/ 

569FILTER_TYPES = ('undefined', 'point', 'box', 'triangle', 'hermite', 'hanning', 

570 'hamming', 'blackman', 'gaussian', 'quadratic', 'cubic', 

571 'catrom', 'mitchell', 'jinc', 'sinc', 'sincfast', 'kaiser', 

572 'welsh', 'parzen', 'bohman', 'bartlett', 'lagrange', 'lanczos', 

573 'lanczossharp', 'lanczos2', 'lanczos2sharp', 'robidoux', 

574 'robidouxsharp', 'cosine', 'spline', 'sentinel') 

575 

576 

577#: (:class:`tuple`) The list of :attr:`Image.function <BaseImage.function>` 

578#: types. 

579#: 

580#: - ``'undefined'`` 

581#: - ``'arcsin'`` 

582#: - ``'arctan'`` 

583#: - ``'polynomial'`` 

584#: - ``'sinusoid'`` 

585FUNCTION_TYPES = ('undefined', 'polynomial', 'sinusoid', 'arcsin', 'arctan') 

586if MAGICK_VERSION_NUMBER >= 0x700: # pragma: no cover 

587 FUNCTION_TYPES = ('undefined', 'arcsin', 'arctan', 'polynomial', 

588 'sinusoid') 

589 

590 

591#: (:class:`tuple`) The list of :attr:`~BaseImage.gravity` types. 

592#: 

593#: - ``'forget'`` 

594#: - ``'north_west'`` 

595#: - ``'north'`` 

596#: - ``'north_east'`` 

597#: - ``'west'`` 

598#: - ``'center'`` 

599#: - ``'east'`` 

600#: - ``'south_west'`` 

601#: - ``'south'`` 

602#: - ``'south_east'`` 

603#: 

604#: .. versionadded:: 0.3.0 

605GRAVITY_TYPES = ('forget', 'north_west', 'north', 'north_east', 'west', 

606 'center', 'east', 'south_west', 'south', 'south_east', 

607 'static') 

608 

609 

610#: (:class:`tuple`) The list of methods for :meth:`~BaseImage.merge_layers` 

611#: and :meth:`~Image.compare_layers`. 

612#: 

613#: - ``'undefined'`` 

614#: - ``'coalesce'`` 

615#: - ``'compareany'`` - Only used for :meth:`~Image.compare_layers`. 

616#: - ``'compareclear'`` - Only used for :meth:`~Image.compare_layers`. 

617#: - ``'compareoverlay'`` - Only used for :meth:`~Image.compare_layers`. 

618#: - ``'dispose'`` 

619#: - ``'optimize'`` 

620#: - ``'optimizeimage'`` 

621#: - ``'optimizeplus'`` 

622#: - ``'optimizetrans'`` 

623#: - ``'removedups'`` 

624#: - ``'removezero'`` 

625#: - ``'composite'`` 

626#: - ``'merge'`` - Only used for :meth:`~BaseImage.merge_layers`. 

627#: - ``'flatten'`` - Only used for :meth:`~BaseImage.merge_layers`. 

628#: - ``'mosaic'`` - Only used for :meth:`~BaseImage.merge_layers`. 

629#: - ``'trimbounds'`` - Only used for :meth:`~BaseImage.merge_layers`. 

630#: 

631#: .. versionadded:: 0.4.3 

632IMAGE_LAYER_METHOD = ('undefined', 'coalesce', 'compareany', 'compareclear', 

633 'compareoverlay', 'dispose', 'optimize', 'optimizeimage', 

634 'optimizeplus', 'optimizetrans', 'removedups', 

635 'removezero', 'composite', 'merge', 'flatten', 'mosaic', 

636 'trimbounds') 

637 

638 

639#: (:class:`tuple`) The list of image types 

640#: 

641#: - ``'undefined'`` 

642#: - ``'bilevel'`` 

643#: - ``'grayscale'`` 

644#: - ``'grayscalealpha'`` - Only available with ImageMagick-7 

645#: - ``'grayscalematte'`` - Only available with ImageMagick-6 

646#: - ``'palette'`` 

647#: - ``'palettealpha'`` - Only available with ImageMagick-7 

648#: - ``'palettematte'`` - Only available with ImageMagick-6 

649#: - ``'truecolor'`` 

650#: - ``'truecoloralpha'`` - Only available with ImageMagick-7 

651#: - ``'truecolormatte'`` - Only available with ImageMagick-6 

652#: - ``'colorseparation'`` 

653#: - ``'colorseparationalpha'`` - Only available with ImageMagick-7 

654#: - ``'colorseparationmatte'`` - Only available with ImageMagick-6 

655#: - ``'optimize'`` 

656#: - ``'palettebilevelalpha'`` - Only available with ImageMagick-7 

657#: - ``'palettebilevelmatte'`` - Only available with ImageMagick-6 

658#: 

659#: .. seealso:: 

660#: 

661#: `ImageMagick Image Types`__ 

662#: Describes the MagickSetImageType method which can be used 

663#: to set the type of an image 

664#: 

665#: __ http://www.imagemagick.org/api/magick-image.php#MagickSetImageType 

666IMAGE_TYPES = ('undefined', 'bilevel', 'grayscale', 'grayscalematte', 

667 'palette', 'palettematte', 'truecolor', 'truecolormatte', 

668 'colorseparation', 'colorseparationmatte', 'optimize', 

669 'palettebilevelmatte') 

670if MAGICK_VERSION_NUMBER >= 0x700: # pragma: no cover 

671 IMAGE_TYPES = ('undefined', 'bilevel', 'grayscale', 'grayscalealpha', 

672 'palette', 'palettealpha', 'truecolor', 'truecoloralpha', 

673 'colorseparation', 'colorseparationalpha', 'optimize', 

674 'palettebilevelalpha') 

675 

676 

677#: (:class:`tuple`) The list of interlace schemes. 

678#: 

679#: - ``'undefined'`` 

680#: - ``'no'`` 

681#: - ``'line'`` 

682#: - ``'plane'`` 

683#: - ``'partition'`` 

684#: - ``'gif'`` 

685#: - ``'jpeg'`` 

686#: - ``'png'`` 

687#: 

688#: .. versionadded:: 0.5.2 

689INTERLACE_TYPES = ('undefined', 'no', 'line', 'plane', 'partition', 'gif', 

690 'jpeg', 'png') 

691 

692 

693#: (:class:`tuple`) The list of builtin kernels. 

694#: 

695#: - ``'undefined'`` 

696#: - ``'unity'`` 

697#: - ``'gaussian'`` 

698#: - ``'dog'`` 

699#: - ``'log'`` 

700#: - ``'blur'`` 

701#: - ``'comet'`` 

702#: - ``'laplacian'`` 

703#: - ``'sobel'`` 

704#: - ``'frei_chen'`` 

705#: - ``'roberts'`` 

706#: - ``'prewitt'`` 

707#: - ``'compass'`` 

708#: - ``'kirsch'`` 

709#: - ``'diamond'`` 

710#: - ``'square'`` 

711#: - ``'rectangle'`` 

712#: - ``'octagon'`` 

713#: - ``'disk'`` 

714#: - ``'plus'`` 

715#: - ``'cross'`` 

716#: - ``'ring'`` 

717#: - ``'peaks'`` 

718#: - ``'edges'`` 

719#: - ``'corners'`` 

720#: - ``'diagonals'`` 

721#: - ``'line_ends'`` 

722#: - ``'line_junctions'`` 

723#: - ``'ridges'`` 

724#: - ``'convex_hull'`` 

725#: - ``'thin_se'`` 

726#: - ``'skeleton'`` 

727#: - ``'chebyshev'`` 

728#: - ``'manhattan'`` 

729#: - ``'octagonal'`` 

730#: - ``'euclidean'`` 

731#: - ``'user_defined'`` 

732#: - ``'binomial'`` 

733#: 

734KERNEL_INFO_TYPES = ('undefined', 'unity', 'gaussian', 'dog', 'log', 'blur', 

735 'comet', 'laplacian', 'sobel', 'frei_chen', 'roberts', 

736 'prewitt', 'compass', 'kirsch', 'diamond', 'square', 

737 'rectangle', 'octagon', 'disk', 'plus', 'cross', 'ring', 

738 'peaks', 'edges', 'corners', 'diagonals', 'line_ends', 

739 'line_junctions', 'ridges', 'convex_hull', 'thin_se', 

740 'skeleton', 'chebyshev', 'manhattan', 'octagonal', 

741 'euclidean', 'user_defined', 'binomial') 

742if MAGICK_VERSION_NUMBER >= 0x700: # pragma: no cover 

743 KERNEL_INFO_TYPES = ('undefined', 'unity', 'gaussian', 'dog', 'log', 

744 'blur', 'comet', 'binomial', 'laplacian', 'sobel', 

745 'frei_chen', 'roberts', 'prewitt', 'compass', 

746 'kirsch', 'diamond', 'square', 'rectangle', 'octagon', 

747 'disk', 'plus', 'cross', 'ring', 'peaks', 'edges', 

748 'corners', 'diagonals', 'line_ends', 'line_junctions', 

749 'ridges', 'convex_hull', 'thin_se', 'skeleton', 

750 'chebyshev', 'manhattan', 'octagonal', 'euclidean', 

751 'user_defined') 

752 

753#: (:class:`tuple`) The list of morphology methods. 

754#: 

755#: - ``'undefined'`` 

756#: - ``'convolve'`` 

757#: - ``'correlate'`` 

758#: - ``'erode'`` 

759#: - ``'dilate'`` 

760#: - ``'erode_intensity'`` 

761#: - ``'dilate_intensity'`` 

762#: - ``'distance'`` 

763#: - ``'open'`` 

764#: - ``'close'`` 

765#: - ``'open_intensity'`` 

766#: - ``'close_intensity'`` 

767#: - ``'smooth'`` 

768#: - ``'edgein'`` 

769#: - ``'edgeout'`` 

770#: - ``'edge'`` 

771#: - ``'tophat'`` 

772#: - ``'bottom_hat'`` 

773#: - ``'hit_and_miss'`` 

774#: - ``'thinning'`` 

775#: - ``'thicken'`` 

776#: - ``'voronoi'`` 

777#: - ``'iterative_distance'`` 

778#: 

779MORPHOLOGY_METHODS = ('undefined', 'convolve', 'correlate', 'erode', 'dilate', 

780 'erode_intensity', 'dilate_intensity', 'distance', 

781 'open', 'close', 'open_intensity', 'close_intensity', 

782 'smooth', 'edgein', 'edgeout', 'edge', 'tophat', 

783 'bottom_hat', 'hit_and_miss', 'thinning', 'thicken', 

784 'voronoi', 'iterative_distance') 

785if MAGICK_VERSION_NUMBER >= 0x700: # pragma: no cover 

786 MORPHOLOGY_METHODS = ('undefined', 'convolve', 'correlate', 'erode', 

787 'dilate', 'erode_intensity', 'dilate_intensity', 

788 'iterative_distance', 'open', 'close', 

789 'open_intensity', 'close_intensity', 'smooth', 

790 'edgein', 'edgeout', 'edge', 'tophat', 'bottom_hat', 

791 'hit_and_miss', 'thinning', 'thicken', 'distance', 

792 'voronoi') 

793 

794 

795#: (:class:`tuple`) The list of noise types used by 

796#: :meth:`Image.noise() <wand.image.BaseImage.noise>` method. 

797#: 

798#: - ``'undefined'`` 

799#: - ``'uniform'`` 

800#: - ``'gaussian'`` 

801#: - ``'multiplicative_gaussian'`` 

802#: - ``'impulse'`` 

803#: - ``'laplacian'`` 

804#: - ``'poisson'`` 

805#: - ``'random'`` 

806#: 

807#: .. versionadded:: 0.5.3 

808NOISE_TYPES = ('undefined', 'uniform', 'gaussian', 'multiplicative_gaussian', 

809 'impulse', 'laplacian', 'poisson', 'random') 

810 

811 

812#: (:class:`collections.abc.Set`) The set of available 

813#: :attr:`~BaseImage.options`. 

814#: 

815#: .. versionadded:: 0.3.0 

816#: 

817#: .. versionchanged:: 0.3.4 

818#: Added ``'jpeg:sampling-factor'`` option. 

819#: 

820#: .. versionchanged:: 0.3.9 

821#: Added ``'pdf:use-cropbox'`` option. Ensure you set this option *before* 

822#: reading the PDF document. 

823#: 

824#: .. deprecated:: 0.5.0 

825#: Any arbitrary key can be set to the option table. Key-Value pairs set 

826#: on the MagickWand stack allowing for various coders, kernels, morphology 

827#: (&tc) to pick and choose additional user-supplied properties/artifacts. 

828OPTIONS = frozenset([ 

829 'caption', 

830 'comment', 

831 'date:create', 

832 'date:modify', 

833 'exif:ColorSpace', 

834 'exif:InteroperabilityIndex', 

835 'fill', 

836 'film-gamma', 

837 'gamma', 

838 'hdr:exposure', 

839 'jpeg:colorspace', 

840 'jpeg:sampling-factor', 

841 'label', 

842 'pdf:use-cropbox', 

843 'png:bit-depth-written', 

844 'png:IHDR.bit-depth-orig', 

845 'png:IHDR.color-type-orig', 

846 'png:tIME', 

847 'reference-black', 

848 'reference-white', 

849 'signature', 

850 'tiff:Orientation', 

851 'tiff:photometric', 

852 'tiff:ResolutionUnit', 

853 'type:hinting', 

854 'vips:metadata' 

855]) 

856 

857 

858#: (:class:`tuple`) The list of :attr:`~BaseImage.orientation` types. 

859#: 

860#: .. versionadded:: 0.3.0 

861ORIENTATION_TYPES = ('undefined', 'top_left', 'top_right', 'bottom_right', 

862 'bottom_left', 'left_top', 'right_top', 'right_bottom', 

863 'left_bottom') 

864 

865 

866#: (:class:`tuple`) List of interpolate pixel methods (ImageMagick-7 only.) 

867#: 

868#: - ``'undefined'`` 

869#: - ``'average'`` 

870#: - ``'average9'`` 

871#: - ``'average16'`` 

872#: - ``'background'`` 

873#: - ``'bilinear'`` 

874#: - ``'blend'`` 

875#: - ``'catrom'`` 

876#: - ``'integer'`` 

877#: - ``'mesh'`` 

878#: - ``'nearest'`` 

879#: - ``'spline'`` 

880#: 

881#: .. versionadded:: 0.5.0 

882PIXEL_INTERPOLATE_METHODS = ('undefined', 'average', 'average9', 'average16', 

883 'background', 'bilinear', 'blend', 'catrom', 

884 'integer', 'mesh', 'nearest', 'spline') 

885 

886 

887#: (:class:`tuple`) List of rendering intent types used for 

888#: :attr:`Image.rendering_intent <wand.image.BaseImage.rendering_intent>` 

889#: property. 

890#: 

891#: - ``'undefined'`` 

892#: - ``'saturation'`` 

893#: - ``'perceptual'`` 

894#: - ``'absolute'`` 

895#: - ``'relative'`` 

896#: 

897#: .. versionadded:: 0.5.4 

898RENDERING_INTENT_TYPES = ('undefined', 'saturation', 'perceptual', 'absolute', 

899 'relative') 

900 

901 

902#: (:class:`tuple`) List of sparse color methods used by 

903#: :class:`Image.sparse_color() <wand.image.BaseImage.sparse_color>` 

904#: 

905#: - ``'undefined'`` 

906#: - ``'barycentric'`` 

907#: - ``'bilinear'`` 

908#: - ``'shepards'`` 

909#: - ``'voronoi'`` 

910#: - ``'inverse'`` 

911#: - ``'manhattan'`` 

912#: 

913#: .. versionadded:: 0.5.3 

914SPARSE_COLOR_METHODS = dict(undefined=0, barycentric=1, bilinear=7, 

915 shepards=16, voronoi=18, inverse=19, 

916 manhattan=20) 

917 

918 

919#: (:class:`tuple`) The list of statistic types used by 

920#: :meth:`Image.statistic() <wand.image.BaseImage.statistic>`. 

921#: 

922#: - ``'undefined'`` 

923#: - ``'gradient'`` 

924#: - ``'maximum'`` 

925#: - ``'mean'`` 

926#: - ``'median'`` 

927#: - ``'minimum'`` 

928#: - ``'mode'`` 

929#: - ``'nonpeak'`` 

930#: - ``'root_mean_square'`` 

931#: - ``'standard_deviation'`` 

932#: 

933#: .. versionadded:: 0.5.3 

934STATISTIC_TYPES = ('undefined', 'gradient', 'maximum', 'mean', 'median', 

935 'minimum', 'mode', 'nonpeak', 'standard_deviation', 

936 'root_mean_square') 

937if MAGICK_VERSION_NUMBER >= 0x700: # pragma: no cover 

938 STATISTIC_TYPES = ('undefined', 'gradient', 'maximum', 'mean', 'median', 

939 'minimum', 'mode', 'nonpeak', 'root_mean_square', 

940 'standard_deviation') 

941 

942 

943#: (:class:`tuple`) The list of pixel storage types. 

944#: 

945#: - ``'undefined'`` 

946#: - ``'char'`` 

947#: - ``'double'`` 

948#: - ``'float'`` 

949#: - ``'integer'`` 

950#: - ``'long'`` 

951#: - ``'quantum'`` 

952#: - ``'short'`` 

953#: 

954#: .. versionadded:: 0.5.0 

955STORAGE_TYPES = ('undefined', 'char', 'double', 'float', 'integer', 

956 'long', 'quantum', 'short') 

957 

958 

959#: (:class:`tuple`) The list of resolution unit types. 

960#: 

961#: - ``'undefined'`` 

962#: - ``'pixelsperinch'`` 

963#: - ``'pixelspercentimeter'`` 

964#: 

965#: .. seealso:: 

966#: 

967#: `ImageMagick Image Units`__ 

968#: Describes the MagickSetImageUnits method which can be used 

969#: to set image units of resolution 

970#: 

971#: __ http://www.imagemagick.org/api/magick-image.php#MagickSetImageUnits 

972UNIT_TYPES = ('undefined', 'pixelsperinch', 'pixelspercentimeter') 

973 

974 

975#: (:class:`tuple`) The list of :attr:`~BaseImage.virtual_pixel` types. 

976#: 

977#: - ``'undefined'`` 

978#: - ``'background'`` 

979#: - ``'constant'`` - Only available with ImageMagick-6 

980#: - ``'dither'`` 

981#: - ``'edge'`` 

982#: - ``'mirror'`` 

983#: - ``'random'`` 

984#: - ``'tile'`` 

985#: - ``'transparent'`` 

986#: - ``'mask'`` 

987#: - ``'black'`` 

988#: - ``'gray'`` 

989#: - ``'white'`` 

990#: - ``'horizontal_tile'`` 

991#: - ``'vertical_tile'`` 

992#: - ``'horizontal_tile_edge'`` 

993#: - ``'vertical_tile_edge'`` 

994#: - ``'checker_tile'`` 

995#: 

996#: .. versionadded:: 0.4.1 

997VIRTUAL_PIXEL_METHOD = ('undefined', 'background', 'constant', 'dither', 

998 'edge', 'mirror', 'random', 'tile', 'transparent', 

999 'mask', 'black', 'gray', 'white', 'horizontal_tile', 

1000 'vertical_tile', 'horizontal_tile_edge', 

1001 'vertical_tile_edge', 'checker_tile') 

1002if MAGICK_VERSION_NUMBER >= 0x700: # pragma: no cover 

1003 VIRTUAL_PIXEL_METHOD = ('undefined', 'background', 'dither', 

1004 'edge', 'mirror', 'random', 'tile', 'transparent', 

1005 'mask', 'black', 'gray', 'white', 

1006 'horizontal_tile', 'vertical_tile', 

1007 'horizontal_tile_edge', 'vertical_tile_edge', 

1008 'checker_tile') 

1009 

1010 

1011def manipulative(function): 

1012 """Mark the operation manipulating itself instead of returning new one.""" 

1013 @functools.wraps(function) 

1014 def wrapped(self, *args, **kwargs): 

1015 result = function(self, *args, **kwargs) 

1016 self.dirty = True 

1017 return result 

1018 return wrapped 

1019 

1020 

1021def trap_exception(function): 

1022 @functools.wraps(function) 

1023 def wrapped(self, *args, **kwargs): 

1024 result = function(self, *args, **kwargs) 

1025 if not bool(result): 

1026 self.raise_exception() 

1027 return result 

1028 return wrapped 

1029 

1030 

1031class BaseImage(Resource): 

1032 """The abstract base of :class:`Image` (container) and 

1033 :class:`~wand.sequence.SingleImage`. That means the most of 

1034 operations, defined in this abstract class, are possible for 

1035 both :class:`Image` and :class:`~wand.sequence.SingleImage`. 

1036 

1037 .. versionadded:: 0.3.0 

1038 

1039 """ 

1040 

1041 #: (:class:`OptionDict`) The mapping of internal option settings. 

1042 #: 

1043 #: .. versionadded:: 0.3.0 

1044 #: 

1045 #: .. versionchanged:: 0.3.4 

1046 #: Added ``'jpeg:sampling-factor'`` option. 

1047 #: 

1048 #: .. versionchanged:: 0.3.9 

1049 #: Added ``'pdf:use-cropbox'`` option. 

1050 options = None 

1051 

1052 #: (:class:`collections.abc.Sequence`) The list of 

1053 #: :class:`~wand.sequence.SingleImage`\ s that the image contains. 

1054 #: 

1055 #: .. versionadded:: 0.3.0 

1056 sequence = None 

1057 

1058 #: (:class:`bool`) Whether the image is changed or not. 

1059 dirty = None 

1060 

1061 #: (:class:`numbers.Integral`) Internal placeholder for 

1062 #: :attr:`seed` property. 

1063 #: 

1064 #: .. versionadded:: 0.5.5 

1065 _seed = None 

1066 

1067 c_is_resource = library.IsMagickWand 

1068 c_destroy_resource = library.DestroyMagickWand 

1069 c_get_exception = library.MagickGetException 

1070 c_clear_exception = library.MagickClearException 

1071 

1072 __slots__ = '_wand', 

1073 

1074 def __init__(self, wand): 

1075 self.wand = wand 

1076 self.channel_images = ChannelImageDict(self) 

1077 self.channel_depths = ChannelDepthDict(self) 

1078 self.options = OptionDict(self) 

1079 self.dirty = False 

1080 

1081 def __eq__(self, other): 

1082 if isinstance(other, type(self)): 

1083 return self.signature == other.signature 

1084 return False 

1085 

1086 def __getitem__(self, idx): 

1087 if (not isinstance(idx, string_type) and 

1088 isinstance(idx, abc.Iterable)): 

1089 idx = tuple(idx) 

1090 d = len(idx) 

1091 if not (1 <= d <= 2): 

1092 raise ValueError('index cannot be {0}-dimensional'.format(d)) 

1093 elif d == 2: 

1094 x, y = idx 

1095 x_slice = isinstance(x, slice) 

1096 y_slice = isinstance(y, slice) 

1097 if x_slice and not y_slice: 

1098 y = slice(y, y + 1) 

1099 elif not x_slice and y_slice: 

1100 x = slice(x, x + 1) 

1101 elif not (x_slice or y_slice): 

1102 if not (isinstance(x, numbers.Integral) and 

1103 isinstance(y, numbers.Integral)): 

1104 raise TypeError('x and y must be integral, not ' + 

1105 repr((x, y))) 

1106 if x < 0: 

1107 x += self.width 

1108 if y < 0: 

1109 y += self.height 

1110 if x >= self.width: 

1111 raise IndexError('x must be less than width') 

1112 elif y >= self.height: 

1113 raise IndexError('y must be less than height') 

1114 elif x < 0: 

1115 raise IndexError('x cannot be less than 0') 

1116 elif y < 0: 

1117 raise IndexError('y cannot be less than 0') 

1118 with iter(self) as iterator: 

1119 iterator.seek(y) 

1120 return iterator.next(x) 

1121 if not (x.step is None and y.step is None): 

1122 raise ValueError('slicing with step is unsupported') 

1123 elif (x.start is None and x.stop is None and 

1124 y.start is None and y.stop is None): 

1125 return self.clone() 

1126 cloned = self.clone() 

1127 try: 

1128 cloned.crop(x.start, y.start, x.stop, y.stop) 

1129 except ValueError as e: 

1130 raise IndexError(str(e)) 

1131 return cloned 

1132 else: 

1133 return self[idx[0]] 

1134 elif isinstance(idx, numbers.Integral): 

1135 if idx < 0: 

1136 idx += self.height 

1137 elif idx >= self.height: 

1138 raise IndexError('index must be less than height, but got ' + 

1139 repr(idx)) 

1140 elif idx < 0: 

1141 raise IndexError('index cannot be less than zero, but got ' + 

1142 repr(idx)) 

1143 with iter(self) as iterator: 

1144 iterator.seek(idx) 

1145 return iterator.next() 

1146 elif isinstance(idx, slice): 

1147 return self[:, idx] 

1148 raise TypeError('unsupported index type: ' + repr(idx)) 

1149 

1150 def __setitem__(self, idx, color): 

1151 if isinstance(color, string_type): 

1152 color = Color(color) 

1153 assertions.assert_color(color=color) 

1154 if not isinstance(idx, abc.Iterable): 

1155 raise TypeError('Expecting list of x,y coordinates, not ' + 

1156 repr(idx)) 

1157 idx = tuple(idx) 

1158 if len(idx) != 2: 

1159 msg = 'pixel index can not be {0}-dimensional'.format(len(idx)) 

1160 raise ValueError(msg) 

1161 colorspace = self.colorspace 

1162 s_index = STORAGE_TYPES.index("double") 

1163 width, height = self.size 

1164 x1, y1 = idx 

1165 x2, y2 = 1, 1 

1166 if not (isinstance(x1, numbers.Integral) and 

1167 isinstance(y1, numbers.Integral)): 

1168 raise TypeError('Expecting x & y to be integers') 

1169 if x1 < 0: 

1170 x1 += width 

1171 if y1 < 0: 

1172 y1 += height 

1173 if x1 >= width: 

1174 raise ValueError('x must be less then image width') 

1175 elif y1 >= height: 

1176 raise ValueError('y must be less then image height') 

1177 if colorspace == 'gray': 

1178 channel_map = b'I' 

1179 pixel = (ctypes.c_double * 1)() 

1180 pixel[0] = color.red 

1181 elif colorspace == 'cmyk': 

1182 channel_map = b'CMYK' 

1183 pixel = (ctypes.c_double * 5)() 

1184 pixel[0] = color.red 

1185 pixel[1] = color.green 

1186 pixel[2] = color.blue 

1187 pixel[3] = color.black 

1188 if self.alpha_channel: 

1189 channel_map += b'A' 

1190 pixel[4] = color.alpha 

1191 else: 

1192 channel_map = b'RGB' 

1193 pixel = (ctypes.c_double * 4)() 

1194 pixel[0] = color.red 

1195 pixel[1] = color.green 

1196 pixel[2] = color.blue 

1197 if self.alpha_channel: 

1198 channel_map += b'A' 

1199 pixel[3] = color.alpha 

1200 r = library.MagickImportImagePixels(self.wand, 

1201 x1, y1, x2, y2, 

1202 channel_map, 

1203 s_index, 

1204 ctypes.byref(pixel)) 

1205 if not r: 

1206 self.raise_exception() 

1207 

1208 def __hash__(self): 

1209 return hash(self.signature) 

1210 

1211 def __iter__(self): 

1212 return Iterator(image=self) 

1213 

1214 def __len__(self): 

1215 return self.height 

1216 

1217 def __ne__(self, other): 

1218 return not (self == other) 

1219 

1220 def __repr__(self, extra_format=' ({self.width}x{self.height})'): 

1221 cls = type(self) 

1222 typename = '{0}.{1}'.format( 

1223 cls.__module__, 

1224 getattr(cls, '__qualname__', cls.__name__) 

1225 ) 

1226 if getattr(self, 'c_resource', None) is None: 

1227 return '<{0}: (closed)>'.format(typename) 

1228 sig = self.signature 

1229 if not sig: 

1230 return '<{0}: (empty)>'.format(typename) 

1231 return '<{0}: {1}{2}>'.format( 

1232 typename, sig[:7], extra_format.format(self=self) 

1233 ) 

1234 

1235 @property 

1236 def __array_interface__(self): 

1237 """Allows image-data from :class:`Image <wand.image.BaseImage>` 

1238 instances to be loaded into numpy's array. 

1239 

1240 .. code:: 

1241 

1242 import numpy 

1243 from wand.image import Image 

1244 

1245 with Image(filename='rose:') as img: 

1246 img_data = numpy.asarray(img) 

1247 

1248 :raises ValueError: if image has no data. 

1249 

1250 .. versionadded:: 0.5.0 

1251 .. versionchanged:: 0.6.0 

1252 The :attr:`shape` property is now ordered by ``height``, ``width``, 

1253 and ``channel``. 

1254 """ 

1255 if not self.signature: 

1256 raise ValueError("No image data to interface with.") 

1257 width, height = self.size 

1258 storage_type = 1 # CharPixel 

1259 channel_format = binary("RGB") 

1260 channel_number = 3 

1261 if self.alpha_channel: 

1262 channel_format = binary("RGBA") 

1263 channel_number = 4 

1264 self._c_buffer = (width * height * channel_number * ctypes.c_char)() 

1265 # FIXME: Move to pixel-data import/export methods. 

1266 r = library.MagickExportImagePixels(self.wand, 

1267 0, 0, width, height, 

1268 channel_format, storage_type, 

1269 ctypes.byref(self._c_buffer)) 

1270 if not r: 

1271 self.raise_exception() 

1272 return dict(data=(ctypes.addressof(self._c_buffer), True), 

1273 shape=(height, width, channel_number), 

1274 typestr='|u1', 

1275 version=3) 

1276 

1277 @property 

1278 def alpha_channel(self): 

1279 """(:class:`bool`) Get state of image alpha channel. 

1280 It can also be used to enable/disable alpha channel, but with different 

1281 behavior new, copied, or existing. 

1282 

1283 Behavior of setting :attr:`alpha_channel` is defined with the 

1284 following values: 

1285 

1286 - ``'activate'``, ``'on'``, or :const:`True` will enable an images 

1287 alpha channel. Existing alpha data is preserved. 

1288 - ``'deactivate'``, ``'off'``, or :const:`False` will disable an images 

1289 alpha channel. Any data on the alpha will be preserved. 

1290 - ``'associate'`` & ``'disassociate'`` toggle alpha channel flag in 

1291 certain image-file specifications. 

1292 - ``'set'`` enables and resets any data in an images alpha channel. 

1293 - ``'opaque'`` enables alpha/matte channel, and forces full opaque 

1294 image. 

1295 - ``'transparent'`` enables alpha/matte channel, and forces full 

1296 transparent image. 

1297 - ``'extract'`` copies data in alpha channel across all other channels, 

1298 and disables alpha channel. 

1299 - ``'copy'`` calculates the gray-scale of RGB channels, 

1300 and applies it to alpha channel. 

1301 - ``'shape'`` is identical to ``'copy'``, but will color the resulting 

1302 image with the value defined with :attr:`background_color`. 

1303 - ``'remove'`` will composite :attr:`background_color` value. 

1304 - ``'background'`` replaces full-transparent color with background 

1305 color. 

1306 

1307 

1308 .. versionadded:: 0.2.1 

1309 

1310 .. versionchanged:: 0.4.1 

1311 Support for additional setting values. 

1312 However :attr:`Image.alpha_channel` will continue to return 

1313 :class:`bool` if the current alpha/matte state is enabled. 

1314 

1315 .. versionchanged:: 0.6.0 

1316 Setting the alpha channel will apply the change to all frames 

1317 in the image stack. 

1318 """ 

1319 return bool(library.MagickGetImageAlphaChannel(self.wand)) 

1320 

1321 @alpha_channel.setter 

1322 @manipulative 

1323 def alpha_channel(self, alpha_type): 

1324 is_im6 = MAGICK_VERSION_NUMBER < 0x700 

1325 # Map common aliases for ``'deactivate'`` 

1326 if alpha_type is False or (alpha_type == 'off' and is_im6): 

1327 alpha_type = 'deactivate' 

1328 # Map common aliases for ``'activate'`` 

1329 elif alpha_type is True or (alpha_type == 'on' and is_im6): 

1330 alpha_type = 'activate' 

1331 assertions.string_in_list(ALPHA_CHANNEL_TYPES, 

1332 'wand.image.ALPHA_CHANNEL_TYPES', 

1333 alpha_channel=alpha_type) 

1334 alpha_index = ALPHA_CHANNEL_TYPES.index(alpha_type) 

1335 library.MagickSetLastIterator(self.wand) 

1336 n = library.MagickGetIteratorIndex(self.wand) 

1337 library.MagickResetIterator(self.wand) 

1338 for i in xrange(0, n + 1): 

1339 library.MagickSetIteratorIndex(self.wand, i) 

1340 library.MagickSetImageAlphaChannel(self.wand, alpha_index) 

1341 

1342 @property 

1343 def animation(self): 

1344 """(:class:`bool`) Whether the image is animation or not. 

1345 It doesn't only mean that the image has two or more images (frames), 

1346 but all frames are even the same size. It's about image format, 

1347 not content. It's :const:`False` even if :mimetype:`image/ico` 

1348 consits of two or more images of the same size. 

1349 

1350 For example, it's :const:`False` for :mimetype:`image/jpeg`, 

1351 :mimetype:`image/gif`, :mimetype:`image/ico`. 

1352 

1353 If :mimetype:`image/gif` has two or more frames, it's :const:`True`. 

1354 If :mimetype:`image/gif` has only one frame, it's :const:`False`. 

1355 

1356 .. versionadded:: 0.3.0 

1357 

1358 .. versionchanged:: 0.3.8 

1359 Became to accept :mimetype:`image/x-gif` as well. 

1360 

1361 """ 

1362 return False 

1363 

1364 @property 

1365 def antialias(self): 

1366 """(:class:`bool`) If vectors & fonts will use anti-aliasing. 

1367 

1368 .. versionchanged:: 0.5.0 

1369 Previously named :attr:`font_antialias`. 

1370 """ 

1371 return bool(library.MagickGetAntialias(self.wand)) 

1372 

1373 @antialias.setter 

1374 @manipulative 

1375 def antialias(self, antialias): 

1376 assertions.assert_bool(antialias=antialias) 

1377 library.MagickSetAntialias(self.wand, antialias) 

1378 

1379 @property 

1380 def background_color(self): 

1381 """(:class:`wand.color.Color`) The image background color. 

1382 It can also be set to change the background color. 

1383 

1384 .. versionadded:: 0.1.9 

1385 

1386 """ 

1387 pixel = library.NewPixelWand() 

1388 result = library.MagickGetImageBackgroundColor(self.wand, pixel) 

1389 if not result: # pragma: no cover 

1390 self.raise_exception() 

1391 else: 

1392 color = Color.from_pixelwand(pixel) 

1393 pixel = library.DestroyPixelWand(pixel) 

1394 return color 

1395 

1396 @background_color.setter 

1397 @manipulative 

1398 def background_color(self, color): 

1399 if isinstance(color, string_type): 

1400 color = Color(color) 

1401 assertions.assert_color(color=color) 

1402 with color: 

1403 result = library.MagickSetImageBackgroundColor(self.wand, 

1404 color.resource) 

1405 if not result: # pragma: no cover 

1406 self.raise_exception() 

1407 # Also set the image stack. 

1408 result = library.MagickSetBackgroundColor(self.wand, 

1409 color.resource) 

1410 if not result: 

1411 self.raise_exception() 

1412 

1413 @property 

1414 def blue_primary(self): 

1415 """(:class:`tuple`) The chromatic blue primary point for the image. 

1416 With ImageMagick-6 the primary value is ``(x, y)`` coordinates; 

1417 however, ImageMagick-7 has ``(x, y, z)``. 

1418 

1419 .. versionadded:: 0.5.2 

1420 """ 

1421 x = ctypes.c_double(0.0) 

1422 y = ctypes.c_double(0.0) 

1423 r = None 

1424 p = None 

1425 if MAGICK_VERSION_NUMBER < 0x700: 

1426 r = library.MagickGetImageBluePrimary(self.wand, x, y) 

1427 p = (x.value, y.value) 

1428 else: # pragma: no cover 

1429 z = ctypes.c_double(0.0) 

1430 r = library.MagickGetImageBluePrimary(self.wand, x, y, z) 

1431 p = (x.value, y.value, z.value) 

1432 if not r: # pragma: no cover 

1433 self.raise_exception() 

1434 return p 

1435 

1436 @blue_primary.setter 

1437 def blue_primary(self, coordinates): 

1438 r = None 

1439 if not isinstance(coordinates, abc.Sequence): 

1440 raise TypeError('Primary must be a tuple') 

1441 if MAGICK_VERSION_NUMBER < 0x700: 

1442 x, y = coordinates 

1443 r = library.MagickSetImageBluePrimary(self.wand, x, y) 

1444 else: # pragma: no cover 

1445 x, y, z = coordinates 

1446 r = library.MagickSetImageBluePrimary(self.wand, x, y, z) 

1447 if not r: # pragma: no cover 

1448 self.raise_exception() 

1449 

1450 @property 

1451 def border_color(self): 

1452 """(:class:`wand.color.Color`) The image border color. Used for 

1453 special effects like :meth:`polaroid()`. 

1454 

1455 .. versionadded:: 0.5.4 

1456 """ 

1457 pixel = library.NewPixelWand() 

1458 result = library.MagickGetImageBorderColor(self.wand, pixel) 

1459 if not result: # pragma: no cover 

1460 self.raise_exception() 

1461 else: 

1462 color = Color.from_pixelwand(pixel) 

1463 pixel = library.DestroyPixelWand(pixel) 

1464 return color 

1465 

1466 @border_color.setter 

1467 def border_color(self, color): 

1468 if isinstance(color, string_type): 

1469 color = Color(color) 

1470 assertions.assert_color(border_color=color) 

1471 with color: 

1472 r = library.MagickSetImageBorderColor(self.wand, color.resource) 

1473 if not r: # pragma: no cover 

1474 self.raise_exception() 

1475 

1476 @property 

1477 def colors(self): 

1478 """(:class:`numbers.Integral`) Count of unique colors used within the 

1479 image. This is READ ONLY property. 

1480 

1481 .. versionadded:: 0.5.3 

1482 """ 

1483 return library.MagickGetImageColors(self.wand) 

1484 

1485 @property 

1486 def colorspace(self): 

1487 """(:class:`basestring`) The image colorspace. 

1488 

1489 Defines image colorspace as in :const:`COLORSPACE_TYPES` enumeration. 

1490 

1491 It may raise :exc:`ValueError` when the colorspace is unknown. 

1492 

1493 .. versionadded:: 0.3.4 

1494 

1495 """ 

1496 colorspace_type_index = library.MagickGetImageColorspace(self.wand) 

1497 if not colorspace_type_index: # pragma: no cover 

1498 self.raise_exception() 

1499 return COLORSPACE_TYPES[text(colorspace_type_index)] 

1500 

1501 @colorspace.setter 

1502 @manipulative 

1503 def colorspace(self, colorspace_type): 

1504 assertions.string_in_list(COLORSPACE_TYPES, 

1505 'wand.image.COLORSPACE_TYPES', 

1506 colorspace=colorspace_type) 

1507 r = library.MagickSetImageColorspace( 

1508 self.wand, 

1509 COLORSPACE_TYPES.index(colorspace_type) 

1510 ) 

1511 if not r: # pragma: no cover 

1512 self.raise_exception() 

1513 

1514 @property 

1515 def compose(self): 

1516 """(:class:`basestring`) The type of image compose. 

1517 It's a string from :const:`COMPOSITE_OPERATORS` list. 

1518 It also can be set. 

1519 

1520 .. versionadded:: 0.5.1 

1521 """ 

1522 compose_index = library.MagickGetImageCompose(self.wand) 

1523 return COMPOSITE_OPERATORS[compose_index] 

1524 

1525 @compose.setter 

1526 def compose(self, operator): 

1527 assertions.string_in_list(COMPOSITE_OPERATORS, 

1528 'wand.image.COMPOSITE_OPERATORS', 

1529 compose=operator) 

1530 library.MagickSetImageCompose(self.wand, 

1531 COMPOSITE_OPERATORS.index(operator)) 

1532 

1533 @property 

1534 def compression(self): 

1535 """(:class:`basestring`) The type of image compression. 

1536 It's a string from :const:`COMPRESSION_TYPES` list. 

1537 It also can be set. 

1538 

1539 .. versionadded:: 0.3.6 

1540 .. versionchanged:: 0.5.2 

1541 Setting :attr:`compression` now sets both `image_info` 

1542 and `images` in the internal image stack. 

1543 """ 

1544 compression_index = library.MagickGetImageCompression(self.wand) 

1545 return COMPRESSION_TYPES[compression_index] 

1546 

1547 @compression.setter 

1548 def compression(self, value): 

1549 assertions.string_in_list(COMPRESSION_TYPES, 

1550 'wand.image.COMPRESSION_TYPES', 

1551 compression=value) 

1552 library.MagickSetCompression( 

1553 self.wand, 

1554 COMPRESSION_TYPES.index(value) 

1555 ) 

1556 library.MagickSetImageCompression( 

1557 self.wand, 

1558 COMPRESSION_TYPES.index(value) 

1559 ) 

1560 

1561 @property 

1562 def compression_quality(self): 

1563 """(:class:`numbers.Integral`) Compression quality of this image. 

1564 

1565 .. versionadded:: 0.2.0 

1566 .. versionchanged:: 0.5.2 

1567 Setting :attr:`compression_quality` now sets both `image_info` 

1568 and `images` in the internal image stack. 

1569 """ 

1570 return library.MagickGetImageCompressionQuality(self.wand) 

1571 

1572 @compression_quality.setter 

1573 @manipulative 

1574 def compression_quality(self, quality): 

1575 """Set compression quality for the image. 

1576 

1577 :param quality: new compression quality setting 

1578 :type quality: :class:`numbers.Integral` 

1579 

1580 """ 

1581 assertions.assert_integer(compression_quality=quality) 

1582 library.MagickSetCompressionQuality(self.wand, quality) 

1583 r = library.MagickSetImageCompressionQuality(self.wand, quality) 

1584 if not r: # pragma: no cover 

1585 raise ValueError('Unable to set compression quality to ' + 

1586 repr(quality)) 

1587 

1588 @property 

1589 def delay(self): 

1590 """(:class:`numbers.Integral`) The number of ticks between frames. 

1591 

1592 .. versionadded:: 0.5.9 

1593 """ 

1594 return library.MagickGetImageDelay(self.wand) 

1595 

1596 @delay.setter 

1597 def delay(self, value): 

1598 assertions.assert_integer(delay=value) 

1599 library.MagickSetImageDelay(self.wand, value) 

1600 

1601 @property 

1602 def depth(self): 

1603 """(:class:`numbers.Integral`) The depth of this image. 

1604 

1605 .. versionadded:: 0.2.1 

1606 

1607 """ 

1608 return library.MagickGetImageDepth(self.wand) 

1609 

1610 @depth.setter 

1611 @manipulative 

1612 def depth(self, depth): 

1613 r = library.MagickSetImageDepth(self.wand, depth) 

1614 if not r: # pragma: no cover 

1615 raise self.raise_exception() 

1616 

1617 @property 

1618 def dispose(self): 

1619 """(:class:`basestring`) Controls how the image data is 

1620 handled during animations. Values are from :const:`DISPOSE_TYPES` 

1621 list, and can also be set. 

1622 

1623 .. seealso:: 

1624 

1625 `Dispose Images`__ section in ``Animation Basics`` article. 

1626 

1627 __ https://www.imagemagick.org/Usage/anim_basics/#dispose_images 

1628 

1629 .. versionadded:: 0.5.0 

1630 """ 

1631 dispose_idx = library.MagickGetImageDispose(self.wand) 

1632 try: 

1633 return DISPOSE_TYPES[dispose_idx] 

1634 except IndexError: # pragma: no cover 

1635 return DISPOSE_TYPES[0] 

1636 

1637 @dispose.setter 

1638 def dispose(self, value): 

1639 assertions.string_in_list(DISPOSE_TYPES, 

1640 'wand.image.DISPOSE_TYPES', 

1641 dispose=value) 

1642 library.MagickSetImageDispose(self.wand, DISPOSE_TYPES.index(value)) 

1643 

1644 @property 

1645 def font(self): 

1646 """(:class:`wand.font.Font`) The current font options.""" 

1647 if not self.font_path: 

1648 return None 

1649 return Font( 

1650 path=text(self.font_path), 

1651 size=self.font_size, 

1652 color=self.font_color, 

1653 antialias=self.antialias, 

1654 stroke_color=self.stroke_color, 

1655 stroke_width=self.stroke_width 

1656 ) 

1657 

1658 @font.setter 

1659 @manipulative 

1660 def font(self, font): 

1661 if not isinstance(font, Font): 

1662 raise TypeError('font must be a wand.font.Font, not ' + repr(font)) 

1663 self.font_path = font.path 

1664 self.font_size = font.size 

1665 self.font_color = font.color 

1666 self.antialias = font.antialias 

1667 if font.stroke_color: 

1668 self.stroke_color = font.stroke_color 

1669 if font.stroke_width is not None: 

1670 self.stroke_width = font.stroke_width 

1671 

1672 @property 

1673 def font_antialias(self): 

1674 """ 

1675 .. deprecated:: 0.5.0 

1676 Use :attr:`antialias` instead. 

1677 """ 

1678 return self.antialias 

1679 

1680 @font_antialias.setter 

1681 def font_antialias(self, antialias): 

1682 self.antialias = antialias 

1683 

1684 @property 

1685 def font_color(self): 

1686 return Color(self.options['fill']) 

1687 

1688 @font_color.setter 

1689 @manipulative 

1690 def font_color(self, color): 

1691 if isinstance(color, string_type): 

1692 color = Color(color) 

1693 assertions.assert_color(font_color=color) 

1694 self.options['fill'] = color.string 

1695 

1696 @property 

1697 def font_path(self): 

1698 """(:class:`basestring`) The path of the current font. 

1699 It also can be set. 

1700 

1701 """ 

1702 return text(library.MagickGetFont(self.wand)) 

1703 

1704 @font_path.setter 

1705 @manipulative 

1706 def font_path(self, font): 

1707 font = binary(font) 

1708 r = library.MagickSetFont(self.wand, font) 

1709 if not r: # pragma: no cover 

1710 self.raise_exception() 

1711 

1712 @property 

1713 def font_size(self): 

1714 """(:class:`numbers.Real`) The font size. It also can be set.""" 

1715 return library.MagickGetPointsize(self.wand) 

1716 

1717 @font_size.setter 

1718 @manipulative 

1719 def font_size(self, size): 

1720 assertions.assert_real(font_size=size) 

1721 if size < 0.0: 

1722 raise ValueError('cannot be less then 0.0, but got ' + repr(size)) 

1723 r = library.MagickSetPointsize(self.wand, size) 

1724 if not r: # pragma: no cover 

1725 self.raise_exception() 

1726 

1727 @property 

1728 def format(self): 

1729 """(:class:`basestring`) The image format. 

1730 

1731 If you want to convert the image format, just reset this property:: 

1732 

1733 assert isinstance(img, wand.image.Image) 

1734 img.format = 'png' 

1735 

1736 It may raise :exc:`ValueError` when the format is unsupported. 

1737 

1738 .. seealso:: 

1739 

1740 `ImageMagick Image Formats`__ 

1741 ImageMagick uses an ASCII string known as *magick* (e.g. ``GIF``) 

1742 to identify file formats, algorithms acting as formats, 

1743 built-in patterns, and embedded profile types. 

1744 

1745 __ http://www.imagemagick.org/script/formats.php 

1746 

1747 .. versionadded:: 0.1.6 

1748 

1749 """ 

1750 fmt = library.MagickGetImageFormat(self.wand) 

1751 if bool(fmt): 

1752 return text(fmt.value) 

1753 else: # pragma: no cover 

1754 self.raise_exception() 

1755 

1756 @format.setter 

1757 def format(self, fmt): 

1758 assertions.assert_string(format=fmt) 

1759 fmt = fmt.strip() 

1760 r = library.MagickSetImageFormat(self.wand, binary(fmt.upper())) 

1761 if not r: 

1762 raise ValueError(repr(fmt) + ' is unsupported format') 

1763 r = library.MagickSetFilename(self.wand, 

1764 b'buffer.' + binary(fmt.lower())) 

1765 if not r: # pragma: no cover 

1766 self.raise_exception() 

1767 

1768 @property 

1769 def fuzz(self): 

1770 """(:class:`numbers.Real`) The normalized real number between ``0.0`` 

1771 and :attr:`quantum_range`. This property influences the accuracy of 

1772 :meth:`compare()`. 

1773 

1774 .. versionadded:: 0.5.3 

1775 """ 

1776 return library.MagickGetImageFuzz(self.wand) 

1777 

1778 @fuzz.setter 

1779 def fuzz(self, value): 

1780 assertions.assert_real(fuzz=value) 

1781 library.MagickSetImageFuzz(self.wand, value) 

1782 

1783 @property 

1784 def gravity(self): 

1785 """(:class:`basestring`) The text placement gravity used when 

1786 annotating with text. It's a string from :const:`GRAVITY_TYPES` 

1787 list. It also can be set. 

1788 

1789 """ 

1790 gravity_index = library.MagickGetGravity(self.wand) 

1791 if not gravity_index: # pragma: no cover 

1792 self.raise_exception() 

1793 return GRAVITY_TYPES[gravity_index] 

1794 

1795 @gravity.setter 

1796 @manipulative 

1797 def gravity(self, value): 

1798 assertions.string_in_list(GRAVITY_TYPES, 

1799 'wand.image.GRAVITY_TYPES', 

1800 gravity=value) 

1801 library.MagickSetGravity(self.wand, GRAVITY_TYPES.index(value)) 

1802 

1803 @property 

1804 def green_primary(self): 

1805 """(:class:`tuple`) The chromatic green primary point for the image. 

1806 With ImageMagick-6 the primary value is ``(x, y)`` coordinates; 

1807 however, ImageMagick-7 has ``(x, y, z)``. 

1808 

1809 .. versionadded:: 0.5.2 

1810 """ 

1811 x = ctypes.c_double(0.0) 

1812 y = ctypes.c_double(0.0) 

1813 r = None 

1814 p = None 

1815 if MAGICK_VERSION_NUMBER < 0x700: 

1816 r = library.MagickGetImageGreenPrimary(self.wand, x, y) 

1817 p = (x.value, y.value) 

1818 else: # pragma: no cover 

1819 z = ctypes.c_double(0.0) 

1820 r = library.MagickGetImageGreenPrimary(self.wand, x, y, z) 

1821 p = (x.value, y.value, z.value) 

1822 if not r: # pragma: no cover 

1823 self.raise_exception() 

1824 return p 

1825 

1826 @green_primary.setter 

1827 def green_primary(self, coordinates): 

1828 r = None 

1829 if not isinstance(coordinates, abc.Sequence): 

1830 raise TypeError('Primary must be a tuple') 

1831 if MAGICK_VERSION_NUMBER < 0x700: 

1832 x, y = coordinates 

1833 r = library.MagickSetImageGreenPrimary(self.wand, x, y) 

1834 else: # pragma: no cover 

1835 x, y, z = coordinates 

1836 r = library.MagickSetImageGreenPrimary(self.wand, x, y, z) 

1837 if not r: # pragma: no cover 

1838 self.raise_exception() 

1839 

1840 @property 

1841 def height(self): 

1842 """(:class:`numbers.Integral`) The height of this image.""" 

1843 return library.MagickGetImageHeight(self.wand) 

1844 

1845 @height.setter 

1846 @manipulative 

1847 def height(self, height): 

1848 assertions.assert_unsigned_integer(height=height) 

1849 library.MagickSetSize(self.wand, self.width, height) 

1850 

1851 @property 

1852 def histogram(self): 

1853 """(:class:`HistogramDict`) The mapping that represents the histogram. 

1854 Keys are :class:`~wand.color.Color` objects, and values are 

1855 the number of pixels. 

1856 

1857 .. tip:: 

1858 

1859 True-color photos can have millions of color values. If performance 

1860 is more valuable than accuracy, remember to :meth:`quantize` the 

1861 image before generating a :class:`HistogramDict`. 

1862 

1863 with Image(filename='hd_photo.jpg') as img: 

1864 img.quantize(255, 'RGB', 0, False, False) 

1865 hist = img.histogram 

1866 

1867 .. versionadded:: 0.3.0 

1868 

1869 """ 

1870 return HistogramDict(self) 

1871 

1872 @property 

1873 def interlace_scheme(self): 

1874 """(:class:`basestring`) The interlace used by the image. 

1875 See :const:`INTERLACE_TYPES`. 

1876 

1877 .. versionadded:: 0.5.2 

1878 """ 

1879 scheme_idx = library.MagickGetInterlaceScheme(self.wand) 

1880 return INTERLACE_TYPES[scheme_idx] 

1881 

1882 @interlace_scheme.setter 

1883 def interlace_scheme(self, scheme): 

1884 assertions.string_in_list(INTERLACE_TYPES, 

1885 'wand.image.INTERLACE_TYPES', 

1886 interlace_scheme=scheme) 

1887 scheme_idx = INTERLACE_TYPES.index(scheme) 

1888 library.MagickSetInterlaceScheme(self.wand, scheme_idx) 

1889 

1890 @property 

1891 def interpolate_method(self): 

1892 """(:class:`basestring`) The interpolation method of the image. 

1893 See :const:`PIXEL_INTERPOLATE_METHODS`. 

1894 

1895 .. versionadded:: 0.5.2 

1896 """ 

1897 method_idx = library.MagickGetImageInterpolateMethod(self.wand) 

1898 return PIXEL_INTERPOLATE_METHODS[method_idx] 

1899 

1900 @interpolate_method.setter 

1901 def interpolate_method(self, method): 

1902 assertions.string_in_list(PIXEL_INTERPOLATE_METHODS, 

1903 'wand.image.PIXEL_INTERPOLATE_METHODS', 

1904 interpolate_method=method) 

1905 method_idx = PIXEL_INTERPOLATE_METHODS.index(method) 

1906 library.MagickSetImageInterpolateMethod(self.wand, method_idx) 

1907 

1908 @property 

1909 def kurtosis(self): 

1910 """(:class:`numbers.Real`) The kurtosis of the image. 

1911 

1912 .. tip:: 

1913 

1914 If you want both :attr:`kurtosis` & :attr:`skewness`, it 

1915 would be faster to call :meth:`kurtosis_channel()` directly. 

1916 

1917 .. versionadded:: 0.5.3 

1918 """ 

1919 k, _ = self.kurtosis_channel() 

1920 return k 

1921 

1922 @property 

1923 def length_of_bytes(self): 

1924 """(:class:`numbers.Integral`) The original size, in bytes, 

1925 of the image read. This will return `0` if the image was modified in 

1926 a way that would invalidate the original length value. 

1927 

1928 .. versionadded:: 0.5.4 

1929 """ 

1930 size_ptr = ctypes.c_size_t(0) 

1931 library.MagickGetImageLength(self.wand, ctypes.byref(size_ptr)) 

1932 return size_ptr.value 

1933 

1934 @property 

1935 def loop(self): 

1936 """(:class:`numbers.Integral`) Number of frame iterations. 

1937 A value of ``0`` will loop forever.""" 

1938 return library.MagickGetImageIterations(self.wand) 

1939 

1940 @loop.setter 

1941 def loop(self, iterations): 

1942 assertions.assert_unsigned_integer(loop=iterations) 

1943 library.MagickSetImageIterations(self.wand, iterations) 

1944 

1945 @property 

1946 def matte_color(self): 

1947 """(:class:`wand.color.Color`) The color value of the matte channel. 

1948 This can also be set. 

1949 

1950 .. versionadded:: 0.4.1 

1951 """ 

1952 pixel = library.NewPixelWand() 

1953 result = library.MagickGetImageMatteColor(self.wand, pixel) 

1954 if result: 

1955 color = Color.from_pixelwand(pixel) 

1956 pixel = library.DestroyPixelWand(pixel) 

1957 return color 

1958 else: # pragma: no cover 

1959 self.raise_exception() 

1960 

1961 @matte_color.setter 

1962 @manipulative 

1963 def matte_color(self, color): 

1964 if isinstance(color, string_type): 

1965 color = Color(color) 

1966 assertions.assert_color(matte_color=color) 

1967 with color: 

1968 result = library.MagickSetImageMatteColor(self.wand, 

1969 color.resource) 

1970 if not result: # pragma: no cover 

1971 self.raise_exception() 

1972 

1973 @property 

1974 def maxima(self): 

1975 """(:class:`numbers.Real`) The maximum quantum value within the image. 

1976 Value between 0.0 and :attr:`quantum_range` 

1977 

1978 .. tip:: 

1979 

1980 If you want both :attr:`maxima` & :attr:`minima`, 

1981 it would be faster to call :meth:`range_channel()` directly. 

1982 

1983 .. versionadded:: 0.5.3 

1984 """ 

1985 _, max_q = self.range_channel() 

1986 return max_q 

1987 

1988 @property 

1989 def mean(self): 

1990 """(:class:`numbers.Real`) The mean of the image, and have a value 

1991 between 0.0 and :attr:`quantum_range` 

1992 

1993 .. tip:: 

1994 

1995 If you want both :attr:`mean` & :attr:`standard_deviation`, it 

1996 would be faster to call :meth:`mean_channel()` directly. 

1997 

1998 .. versionadded:: 0.5.3 

1999 """ 

2000 m, _ = self.mean_channel() 

2001 return m 

2002 

2003 @property 

2004 def minima(self): 

2005 """(:class:`numbers.Real`) The minimum quantum value within the image. 

2006 Value between 0.0 and :attr:`quantum_range` 

2007 

2008 .. tip:: 

2009 

2010 If you want both :attr:`maxima` & :attr:`minima`, 

2011 it would be faster to call :meth:`range_channel()` directly. 

2012 

2013 .. versionadded:: 0.5.3 

2014 """ 

2015 min_q, _ = self.range_channel() 

2016 return min_q 

2017 

2018 @property 

2019 def orientation(self): 

2020 """(:class:`basestring`) The image orientation. It's a string from 

2021 :const:`ORIENTATION_TYPES` list. It also can be set. 

2022 

2023 .. versionadded:: 0.3.0 

2024 

2025 """ 

2026 orientation_index = library.MagickGetImageOrientation(self.wand) 

2027 try: 

2028 return ORIENTATION_TYPES[orientation_index] 

2029 except IndexError: # pragma: no cover 

2030 return ORIENTATION_TYPES[0] 

2031 

2032 @orientation.setter 

2033 @manipulative 

2034 def orientation(self, value): 

2035 assertions.string_in_list(ORIENTATION_TYPES, 

2036 'wand.image.ORIENTATION_TYPES', 

2037 orientation=value) 

2038 index = ORIENTATION_TYPES.index(value) 

2039 library.MagickSetImageOrientation(self.wand, index) 

2040 

2041 @property 

2042 def page(self): 

2043 """The dimensions and offset of this Wand's page as a 4-tuple: 

2044 ``(width, height, x, y)``. 

2045 

2046 Note that since it is based on the virtual canvas, it may not equal the 

2047 dimensions of an image. See the ImageMagick documentation on the 

2048 virtual canvas for more information. 

2049 

2050 .. versionadded:: 0.4.3 

2051 

2052 """ 

2053 w = ctypes.c_uint() 

2054 h = ctypes.c_uint() 

2055 x = ctypes.c_int() 

2056 y = ctypes.c_int() 

2057 r = library.MagickGetImagePage(self.wand, w, h, x, y) 

2058 if not r: # pragma: no cover 

2059 self.raise_exception() 

2060 return int(w.value), int(h.value), int(x.value), int(y.value) 

2061 

2062 @page.setter 

2063 @manipulative 

2064 def page(self, newpage): 

2065 if isinstance(newpage, abc.Sequence): 

2066 w, h, x, y = newpage 

2067 else: 

2068 raise TypeError("page layout must be 4-tuple") 

2069 r = library.MagickSetImagePage(self.wand, w, h, x, y) 

2070 if not r: # pragma: no cover 

2071 self.raise_exception() 

2072 

2073 @property 

2074 def page_height(self): 

2075 """(:class:`numbers.Integral`) The height of the page for this wand. 

2076 

2077 .. versionadded:: 0.4.3 

2078 

2079 """ 

2080 return self.page[1] 

2081 

2082 @page_height.setter 

2083 @manipulative 

2084 def page_height(self, height): 

2085 newpage = list(self.page) 

2086 newpage[1] = height 

2087 self.page = newpage 

2088 

2089 @property 

2090 def page_width(self): 

2091 """(:class:`numbers.Integral`) The width of the page for this wand. 

2092 

2093 .. versionadded:: 0.4.3 

2094 

2095 """ 

2096 return self.page[0] 

2097 

2098 @page_width.setter 

2099 @manipulative 

2100 def page_width(self, width): 

2101 newpage = list(self.page) 

2102 newpage[0] = width 

2103 self.page = newpage 

2104 

2105 @property 

2106 def page_x(self): 

2107 """(:class:`numbers.Integral`) The X-offset of the page for this wand. 

2108 

2109 .. versionadded:: 0.4.3 

2110 

2111 """ 

2112 return self.page[2] 

2113 

2114 @page_x.setter 

2115 @manipulative 

2116 def page_x(self, x): 

2117 newpage = list(self.page) 

2118 newpage[2] = x 

2119 self.page = newpage 

2120 

2121 @property 

2122 def page_y(self): 

2123 """(:class:`numbers.Integral`) The Y-offset of the page for this wand. 

2124 

2125 .. versionadded:: 0.4.3 

2126 

2127 """ 

2128 return self.page[3] 

2129 

2130 @page_y.setter 

2131 @manipulative 

2132 def page_y(self, y): 

2133 newpage = list(self.page) 

2134 newpage[3] = y 

2135 self.page = newpage 

2136 

2137 @property 

2138 def quantum_range(self): 

2139 """(:class:`int`) The maximum value of a color channel that is 

2140 supported by the imagemagick library. 

2141 

2142 .. versionadded:: 0.2.0 

2143 

2144 """ 

2145 result = ctypes.c_size_t() 

2146 library.MagickGetQuantumRange(ctypes.byref(result)) 

2147 return result.value 

2148 

2149 @property 

2150 def red_primary(self): 

2151 """(:class:`tuple`) The chromatic red primary point for the image. 

2152 With ImageMagick-6 the primary value is ``(x, y)`` coordinates; 

2153 however, ImageMagick-7 has ``(x, y, z)``. 

2154 

2155 .. versionadded:: 0.5.2 

2156 """ 

2157 x = ctypes.c_double(0.0) 

2158 y = ctypes.c_double(0.0) 

2159 r = None 

2160 p = None 

2161 if MAGICK_VERSION_NUMBER < 0x700: 

2162 r = library.MagickGetImageRedPrimary(self.wand, x, y) 

2163 p = (x.value, y.value) 

2164 else: # pragma: no cover 

2165 z = ctypes.c_double(0.0) 

2166 r = library.MagickGetImageRedPrimary(self.wand, x, y, z) 

2167 p = (x.value, y.value, z.value) 

2168 if not r: # pragma: no cover 

2169 self.raise_exception() 

2170 return p 

2171 

2172 @red_primary.setter 

2173 def red_primary(self, coordinates): 

2174 r = None 

2175 if not isinstance(coordinates, abc.Sequence): 

2176 raise TypeError('Primary must be a tuple') 

2177 if MAGICK_VERSION_NUMBER < 0x700: 

2178 x, y = coordinates 

2179 r = library.MagickSetImageRedPrimary(self.wand, x, y) 

2180 else: # pragma: no cover 

2181 x, y, z = coordinates 

2182 r = library.MagickSetImageRedPrimary(self.wand, x, y, z) 

2183 if not r: # pragma: no cover 

2184 self.raise_exception() 

2185 

2186 @property 

2187 def rendering_intent(self): 

2188 """(:class:`basestring`) PNG rendering intent. See 

2189 :const:`RENDERING_INTENT_TYPES` for valid options. 

2190 

2191 .. versionadded:: 0.5.4 

2192 """ 

2193 ri_index = library.MagickGetImageRenderingIntent(self.wand) 

2194 return RENDERING_INTENT_TYPES[ri_index] 

2195 

2196 @rendering_intent.setter 

2197 def rendering_intent(self, value): 

2198 assertions.string_in_list(RENDERING_INTENT_TYPES, 

2199 'wand.image.RENDERING_INTENT_TYPES', 

2200 rendering_intent=value) 

2201 ri_index = RENDERING_INTENT_TYPES.index(value) 

2202 library.MagickSetImageRenderingIntent(self.wand, ri_index) 

2203 

2204 @property 

2205 def resolution(self): 

2206 """(:class:`tuple`) Resolution of this image. 

2207 

2208 .. versionadded:: 0.3.0 

2209 

2210 .. versionchanged:: 0.5.8 

2211 Resolution returns a tuple of float values to 

2212 match ImageMagick's behavior. 

2213 """ 

2214 x = ctypes.c_double(0.0) 

2215 y = ctypes.c_double(0.0) 

2216 r = library.MagickGetImageResolution(self.wand, x, y) 

2217 if not r: # pragma: no cover 

2218 self.raise_exception() 

2219 return x.value, y.value 

2220 

2221 @resolution.setter 

2222 @manipulative 

2223 def resolution(self, geometry): 

2224 if isinstance(geometry, abc.Sequence): 

2225 x, y = geometry 

2226 elif isinstance(geometry, numbers.Real): 

2227 x, y = geometry, geometry 

2228 else: 

2229 raise TypeError('resolution must be a (x, y) pair or a float ' 

2230 'of the same x/y') 

2231 if self.size == (0, 0): 

2232 r = library.MagickSetResolution(self.wand, x, y) 

2233 else: 

2234 r = library.MagickSetImageResolution(self.wand, x, y) 

2235 if not r: # pragma: no cover 

2236 self.raise_exception() 

2237 

2238 @property 

2239 def scene(self): 

2240 """(:class:`numbers.Integral`) The scene number of the current frame 

2241 within an animated image. 

2242 

2243 .. versionadded:: 0.5.4 

2244 """ 

2245 return library.MagickGetImageScene(self.wand) 

2246 

2247 @scene.setter 

2248 def scene(self, value): 

2249 assertions.assert_unsigned_integer(scene=value) 

2250 library.MagickSetImageScene(self.wand, value) 

2251 

2252 @property 

2253 def seed(self): 

2254 """(:class:`numbers.Integral`) The seed for random number generator. 

2255 

2256 .. warning:: 

2257 

2258 This property is only available with ImageMagick 7.0.8-41, or 

2259 greater. 

2260 

2261 .. versionadded:: 0.5.5 

2262 """ 

2263 return self._seed 

2264 

2265 @seed.setter 

2266 def seed(self, value): 

2267 if library.MagickSetSeed is None: 

2268 msg = 'Property requires ImageMagick version 7.0.8-41 or greater.' 

2269 raise WandLibraryVersionError(msg) 

2270 assertions.assert_unsigned_integer(seed=value) 

2271 self._seed = value 

2272 library.MagickSetSeed(self.wand, value) 

2273 

2274 @property 

2275 def signature(self): 

2276 """(:class:`str`) The SHA-256 message digest for the image pixel 

2277 stream. 

2278 

2279 .. versionadded:: 0.1.9 

2280 

2281 """ 

2282 signature = library.MagickGetImageSignature(self.wand) 

2283 return text(signature.value) 

2284 

2285 @property 

2286 def size(self): 

2287 """(:class:`tuple`) The pair of (:attr:`width`, :attr:`height`). 

2288 

2289 .. note:: 

2290 

2291 When working with animations, or other layer-based image formats, 

2292 the :attr:`width` & :attr:`height` properties are referencing the 

2293 last frame read into the image stack. To get the :attr:`size` 

2294 of the entire animated images, call 

2295 :meth:`Image.coalesce() <wand.image.BaseImage.coalesce>` method 

2296 immediately after reading the image. 

2297 """ 

2298 return self.width, self.height 

2299 

2300 @property 

2301 def skewness(self): 

2302 """(:class:`numbers.Real`) The skewness of the image. 

2303 

2304 .. tip:: 

2305 

2306 If you want both :attr:`kurtosis` & :attr:`skewness`, it 

2307 would be faster to call :meth:`kurtosis_channel()` directly. 

2308 

2309 .. versionadded:: 0.5.3 

2310 """ 

2311 _, s = self.kurtosis_channel() 

2312 return s 

2313 

2314 @property 

2315 def standard_deviation(self): 

2316 """(:class:`numbers.Real`) The standard deviation of the image. 

2317 

2318 .. tip:: 

2319 

2320 If you want both :attr:`mean` & :attr:`standard_deviation`, it 

2321 would be faster to call :meth:`mean_channel()` directly. 

2322 

2323 .. versionadded:: 0.5.3 

2324 """ 

2325 _, s = self.mean_channel() 

2326 return s 

2327 

2328 @property 

2329 def stroke_color(self): 

2330 stroke = self.options['stroke'] 

2331 return Color(stroke) if stroke else None 

2332 

2333 @stroke_color.setter 

2334 @manipulative 

2335 def stroke_color(self, color): 

2336 if isinstance(color, string_type): 

2337 color = Color(color) 

2338 if isinstance(color, Color): 

2339 self.options['stroke'] = color.string 

2340 elif color is None: 

2341 del self.options['stroke'] 

2342 else: 

2343 raise TypeError('stroke_color must be a wand.color.Color, not ' + 

2344 repr(color)) 

2345 

2346 @property 

2347 def stroke_width(self): 

2348 strokewidth = self.options['strokewidth'] 

2349 return float(strokewidth) if strokewidth else None 

2350 

2351 @stroke_width.setter 

2352 @manipulative 

2353 def stroke_width(self, width): 

2354 assertions.assert_real(stroke_width=width) 

2355 self.options['strokewidth'] = str(width) 

2356 

2357 @property 

2358 def ticks_per_second(self): 

2359 """(:class:`numbers.Integral`) Internal clock for animated images. 

2360 .. versionadded:: 0.5.4 

2361 """ 

2362 return library.MagickGetImageTicksPerSecond(self.wand) 

2363 

2364 @ticks_per_second.setter 

2365 def ticks_per_second(self, value): 

2366 assertions.assert_unsigned_integer(ticks_per_second=value) 

2367 r = library.MagickSetImageTicksPerSecond(self.wand, value) 

2368 if not r: # pragma: no cover 

2369 self.raise_exception() 

2370 

2371 @property 

2372 def type(self): 

2373 """(:class:`basestring`) The image type. 

2374 

2375 Defines image type as in :const:`IMAGE_TYPES` enumeration. 

2376 

2377 It may raise :exc:`ValueError` when the type is unknown. 

2378 

2379 .. versionadded:: 0.2.2 

2380 

2381 """ 

2382 image_type_index = library.MagickGetImageType(self.wand) 

2383 if not image_type_index: # pragma: no cover 

2384 self.raise_exception() 

2385 return IMAGE_TYPES[text(image_type_index)] 

2386 

2387 @type.setter 

2388 @manipulative 

2389 def type(self, image_type): 

2390 assertions.string_in_list(IMAGE_TYPES, 'wand.image.IMAGE_TYPES', 

2391 type=image_type) 

2392 r = library.MagickSetImageType(self.wand, 

2393 IMAGE_TYPES.index(image_type)) 

2394 if not r: # pragma: no cover 

2395 self.raise_exception() 

2396 

2397 @property 

2398 def units(self): 

2399 """(:class:`basestring`) The resolution units of this image.""" 

2400 r = library.MagickGetImageUnits(self.wand) 

2401 return UNIT_TYPES[text(r)] 

2402 

2403 @units.setter 

2404 @manipulative 

2405 def units(self, units): 

2406 assertions.string_in_list(UNIT_TYPES, 'wand.image.UNIT_TYPES', 

2407 units=units) 

2408 r = library.MagickSetImageUnits(self.wand, UNIT_TYPES.index(units)) 

2409 if not r: # pragma: no cover 

2410 self.raise_exception() 

2411 

2412 @property 

2413 def virtual_pixel(self): 

2414 """(:class:`basestring`) The virtual pixel of image. 

2415 This can also be set with a value from :const:`VIRTUAL_PIXEL_METHOD` 

2416 ... versionadded:: 0.4.1 

2417 """ 

2418 method_index = library.MagickGetImageVirtualPixelMethod(self.wand) 

2419 return VIRTUAL_PIXEL_METHOD[method_index] 

2420 

2421 @virtual_pixel.setter 

2422 def virtual_pixel(self, method): 

2423 assertions.string_in_list(VIRTUAL_PIXEL_METHOD, 

2424 'wand.image.VIRTUAL_PIXEL_METHOD', 

2425 virtual_pixel=method) 

2426 library.MagickSetImageVirtualPixelMethod( 

2427 self.wand, 

2428 VIRTUAL_PIXEL_METHOD.index(method) 

2429 ) 

2430 

2431 @property 

2432 def wand(self): 

2433 """Internal pointer to the MagickWand instance. It may raise 

2434 :exc:`ClosedImageError` when the instance has destroyed already. 

2435 

2436 """ 

2437 try: 

2438 return self.resource 

2439 except DestroyedResourceError: 

2440 raise ClosedImageError(repr(self) + ' is closed already') 

2441 

2442 @wand.setter 

2443 def wand(self, wand): 

2444 try: 

2445 self.resource = wand 

2446 except TypeError: 

2447 raise TypeError(repr(wand) + ' is not a MagickWand instance') 

2448 

2449 @wand.deleter 

2450 def wand(self): 

2451 del self.resource 

2452 

2453 @property 

2454 def width(self): 

2455 """(:class:`numbers.Integral`) The width of this image.""" 

2456 return library.MagickGetImageWidth(self.wand) 

2457 

2458 @width.setter 

2459 @manipulative 

2460 def width(self, width): 

2461 assertions.assert_unsigned_integer(width=width) 

2462 library.MagickSetSize(self.wand, width, self.height) 

2463 

2464 @property 

2465 def white_point(self): 

2466 """(:class:`tuple`) The chromatic white point for the image. 

2467 With ImageMagick-6 the primary value is ``(x, y)`` coordinates; 

2468 however, ImageMagick-7 has ``(x, y, z)``. 

2469 

2470 .. versionadded:: 0.5.2 

2471 """ 

2472 x = ctypes.c_double(0.0) 

2473 y = ctypes.c_double(0.0) 

2474 r = None 

2475 p = None 

2476 if MAGICK_VERSION_NUMBER < 0x700: 

2477 r = library.MagickGetImageWhitePoint(self.wand, x, y) 

2478 p = (x.value, y.value) 

2479 else: # pragma: no cover 

2480 z = ctypes.c_double(0.0) 

2481 r = library.MagickGetImageWhitePoint(self.wand, x, y, z) 

2482 p = (x.value, y.value, z.value) 

2483 if not r: # pragma: no cover 

2484 self.raise_exception() 

2485 return p 

2486 

2487 @white_point.setter 

2488 def white_point(self, coordinates): 

2489 r = None 

2490 if not isinstance(coordinates, abc.Sequence): 

2491 raise TypeError('Primary must be a tuple') 

2492 if MAGICK_VERSION_NUMBER < 0x700: 

2493 x, y = coordinates 

2494 r = library.MagickSetImageWhitePoint(self.wand, x, y) 

2495 else: # pragma: no cover 

2496 x, y, z = coordinates 

2497 r = library.MagickSetImageWhitePoint(self.wand, x, y, z) 

2498 if not r: # pragma: no cover 

2499 self.raise_exception() 

2500 

2501 @manipulative 

2502 def _auto_orient(self): 

2503 """Fallback for :attr:`auto_orient()` method 

2504 (which wraps :c:func:`MagickAutoOrientImage`), 

2505 fixes orientation by checking EXIF data. 

2506 

2507 .. versionadded:: 0.4.1 

2508 

2509 """ 

2510 v_ptr = library.MagickGetImageProperty(self.wand, 

2511 b'exif:orientation') 

2512 if v_ptr: 

2513 exif_orientation = v_ptr.value 

2514 else: 

2515 return 

2516 

2517 if not exif_orientation: 

2518 return 

2519 

2520 orientation_type = ORIENTATION_TYPES[int(exif_orientation)] 

2521 

2522 fn_lookup = { 

2523 'undefined': None, 

2524 'top_left': None, 

2525 'top_right': self.flop, 

2526 'bottom_right': functools.partial(self.rotate, degree=180.0), 

2527 'bottom_left': self.flip, 

2528 'left_top': self.transpose, 

2529 'right_top': functools.partial(self.rotate, degree=90.0), 

2530 'right_bottom': self.transverse, 

2531 'left_bottom': functools.partial(self.rotate, degree=270.0) 

2532 } 

2533 

2534 fn = fn_lookup.get(orientation_type) 

2535 

2536 if not fn: 

2537 return 

2538 

2539 fn() 

2540 self.orientation = 'top_left' 

2541 

2542 def _channel_to_mask(self, value): 

2543 """Attempts to resolve user input into a :c:type:`ChannelType` bit-mask. 

2544 User input can be an integer, a string defined in :const:`CHANNELS`, 

2545 or a string following ImageMagick's `CLI format`__. 

2546 

2547 __ https://imagemagick.org/script/command-line-options.php#channel 

2548 

2549 .. code:: 

2550 

2551 # User generated bit-mask. 

2552 mask = self._channel_to_mask(CHANNELS['red'] | CHANNELS['green']) 

2553 # Defined constant. 

2554 mask = self._channel_to_mask('red') 

2555 # CLI format. 

2556 mask = self._channel_to_mask('RGB,Sync') 

2557 

2558 :param value: Mixed user input. 

2559 :type value: :class:`numbers.Integral` or :class:`basestring` 

2560 :returns: Bit-mask constant. 

2561 :rtype: :class:`numbers.Integral` 

2562 

2563 .. versionadded:: 0.5.5 

2564 """ 

2565 mask = -1 

2566 if isinstance(value, numbers.Integral) and not isinstance(value, bool): 

2567 mask = value 

2568 elif isinstance(value, string_type): 

2569 if value in CHANNELS: 

2570 mask = CHANNELS[value] 

2571 elif libmagick.ParseChannelOption: 

2572 mask = libmagick.ParseChannelOption(binary(value)) 

2573 else: 

2574 raise TypeError(repr(value) + ' is an invalid channel type' 

2575 '; see wand.image.CHANNELS dictionary') 

2576 if mask < 0: 

2577 raise ValueError('expected value from wand.image.CHANNELS, not ' 

2578 + repr(value)) 

2579 return mask 

2580 

2581 def _gravity_to_offset(self, gravity, width, height): 

2582 """Calculate the top/left offset by a given gravity. 

2583 

2584 Some methods in MagickWand's C-API do not respect gravity, but 

2585 instead, expect a x/y offset. This is confusing to folks coming from 

2586 the CLI documentation that does respect gravity 

2587 

2588 :param gravity: Value from :const:`GRAVITY_TYPES`. 

2589 :type gravity: :class:`basestring` 

2590 :raises: :class:`ValueError` if gravity is no known. 

2591 :returns: :class:`numbers.Intergal` top, :class:`numbers.Intergal` left 

2592 

2593 .. versionadded:: 0.5.3 

2594 """ 

2595 top, left = 0, 0 

2596 assertions.string_in_list(GRAVITY_TYPES, 'wand.image.GRAVITY_TYPES', 

2597 gravity=gravity) 

2598 # Set `top` based on given gravity 

2599 if gravity in ('north_west', 'north', 'north_east'): 

2600 top = 0 

2601 elif gravity in ('west', 'center', 'east'): 

2602 top = int(self.height / 2) - int(height / 2) 

2603 elif gravity in ('south_west', 'south', 'south_east'): 

2604 top = self.height - height 

2605 # Set `left` based on given gravity 

2606 if gravity in ('north_west', 'west', 'south_west'): 

2607 left = 0 

2608 elif gravity in ('north', 'center', 'south'): 

2609 left = int(self.width / 2) - int(width / 2) 

2610 elif gravity in ('north_east', 'east', 'south_east'): 

2611 left = self.width - width 

2612 return top, left 

2613 

2614 @manipulative 

2615 @trap_exception 

2616 def adaptive_blur(self, radius=0.0, sigma=0.0, channel=None): 

2617 """Adaptively blurs the image by decreasing Gaussian as the operator 

2618 approaches detected edges. 

2619 

2620 :param radius: size of gaussian aperture. 

2621 :type radius: :class:`numbers.Real` 

2622 :param sigma: Standard deviation of the gaussian filter. 

2623 :type sigma: :class:`numbers.Real` 

2624 :param channel: Apply the blur effect on a specific channel. 

2625 See :const:`CHANNELS`. 

2626 :type channel: :class:`basestring` 

2627 

2628 .. versionadded:: 0.5.3 

2629 

2630 .. versionchanged:: 0.5.5 

2631 Added optional ``channel`` argument 

2632 """ 

2633 assertions.assert_real(radius=radius, sigma=sigma) 

2634 if channel is None: 

2635 r = library.MagickAdaptiveBlurImage(self.wand, radius, sigma) 

2636 else: 

2637 channel_ch = self._channel_to_mask(channel) 

2638 if MAGICK_VERSION_NUMBER < 0x700: 

2639 r = library.MagickAdaptiveBlurImageChannel(self.wand, 

2640 channel_ch, 

2641 radius, 

2642 sigma) 

2643 else: # pragma: no cover 

2644 mask = library.MagickSetImageChannelMask(self.wand, 

2645 channel_ch) 

2646 r = library.MagickAdaptiveBlurImage(self.wand, radius, sigma) 

2647 library.MagickSetImageChannelMask(self.wand, mask) 

2648 return r 

2649 

2650 @manipulative 

2651 @trap_exception 

2652 def adaptive_resize(self, columns=None, rows=None): 

2653 """Adaptively resize image by applying Mesh interpolation. 

2654 

2655 :param columns: width of resized image. 

2656 :type columns: :class:`numbers.Integral` 

2657 :param rows: hight of resized image. 

2658 :type rows: :class:`numbers.Integral` 

2659 

2660 .. versionadded:: 0.5.3 

2661 """ 

2662 if columns is None: 

2663 columns = self.width 

2664 if rows is None: 

2665 rows = self.height 

2666 assertions.assert_integer(columns=columns, rows=rows) 

2667 return library.MagickAdaptiveResizeImage(self.wand, columns, rows) 

2668 

2669 @manipulative 

2670 @trap_exception 

2671 def adaptive_sharpen(self, radius=0.0, sigma=0.0, channel=None): 

2672 """Adaptively sharpens the image by sharpening more intensely near 

2673 image edges and less intensely far from edges. 

2674 

2675 :param radius: size of gaussian aperture. 

2676 :type radius: :class:`numbers.Real` 

2677 :param sigma: Standard deviation of the gaussian filter. 

2678 :type sigma: :class:`numbers.Real` 

2679 :param channel: Apply the sharpen effect on a specific channel. 

2680 See :const:`CHANNELS`. 

2681 :type channel: :class:`basestring` 

2682 

2683 .. versionadded:: 0.5.3 

2684 

2685 .. versionchanged:: 0.5.5 

2686 Added optional ``channel`` argument 

2687 """ 

2688 assertions.assert_real(radius=radius, sigma=sigma) 

2689 if channel is None: 

2690 r = library.MagickAdaptiveSharpenImage(self.wand, radius, sigma) 

2691 else: 

2692 channel_ch = self._channel_to_mask(channel) 

2693 if MAGICK_VERSION_NUMBER < 0x700: 

2694 r = library.MagickAdaptiveSharpenImageChannel(self.wand, 

2695 channel_ch, 

2696 radius, 

2697 sigma) 

2698 else: # pragma: no cover 

2699 mask = library.MagickSetImageChannelMask(self.wand, 

2700 channel_ch) 

2701 r = library.MagickAdaptiveSharpenImage(self.wand, 

2702 radius, 

2703 sigma) 

2704 library.MagickSetImageChannelMask(self.wand, mask) 

2705 return r 

2706 

2707 @manipulative 

2708 def adaptive_threshold(self, width, height, offset=0.0): 

2709 """Applies threshold for each pixel based on neighboring pixel values. 

2710 

2711 :param width: size of neighboring pixels on the X-axis. 

2712 :type width: :class:`numbers.Integral` 

2713 :param height: size of neighboring pixels on the Y-axis. 

2714 :type height: :class:`numbers.Integral` 

2715 :param offset: normalized number between `0.0` and 

2716 :attr:`quantum_range`. Forces the pixels to black if 

2717 values are below ``offset``. 

2718 :type offset: :class:`numbers.Real` 

2719 

2720 .. versionadded:: 0.5.3 

2721 """ 

2722 assertions.assert_integer(width=width, height=height) 

2723 assertions.assert_real(offset=offset) 

2724 if MAGICK_VERSION_NUMBER < 0x700: 

2725 offset = int(offset) 

2726 return library.MagickAdaptiveThresholdImage(self.wand, width, 

2727 height, offset) 

2728 

2729 @manipulative 

2730 @trap_exception 

2731 def annotate(self, text, drawing_wand, left=0, baseline=0, angle=0): 

2732 """Draws text on an image. This method differs from :meth:`caption()` 

2733 as it uses :class:`~wand.drawing.Drawing` class to manage the 

2734 font configuration & style context. 

2735 

2736 .. code:: 

2737 

2738 from wand.drawing import Drawing 

2739 from wand.image import Image 

2740 

2741 with Image(filename='input.jpg') as img: 

2742 with Drawing() as ctx: 

2743 ctx.font_family = 'Times New Roman, Nimbus Roman No9' 

2744 ctx.font_size = 18 

2745 ctx.text_decoration = 'underline' 

2746 ctx.text_kerning = -1 

2747 img.annotate('Hello World', ctx, left=20, baseline=50) 

2748 img.save(filename='output.jpg') 

2749 

2750 :param text: String to annotate on image. 

2751 :type text: :class:`basestring` 

2752 :param drawing_wand: Font configuration & style context. 

2753 :type text: :class:`wand.drawing.Drawing` 

2754 :param left: X-axis offset of the text baseline. 

2755 :type left: :class:`numbers.Real` 

2756 :param baseline: Y-axis offset of the bottom of the text. 

2757 :type baseline: :class:`numbers.Real` 

2758 :param angle: Degree rotation to draw text at. 

2759 :type angle: :class:`numbers.Real` 

2760 

2761 .. versionadded:: 0.5.6 

2762 """ 

2763 from .drawing import Drawing 

2764 if not isinstance(drawing_wand, Drawing): 

2765 raise TypeError('drawing_wand must be in instances of ' + 

2766 'wand.drawing.Drawing, not ' + repr(drawing_wand)) 

2767 assertions.assert_real(left=left, baseline=baseline, angle=angle) 

2768 btext = binary(text) 

2769 return library.MagickAnnotateImage(self.wand, drawing_wand.resource, 

2770 left, baseline, angle, btext) 

2771 

2772 @manipulative 

2773 @trap_exception 

2774 def auto_gamma(self): 

2775 """Adjust the gamma level of an image. 

2776 

2777 .. versionadded:: 0.5.4 

2778 """ 

2779 return library.MagickAutoGammaImage(self.wand) 

2780 

2781 @manipulative 

2782 @trap_exception 

2783 def auto_level(self): 

2784 """Scale the minimum and maximum values to a full quantum range. 

2785 

2786 .. versionadded:: 0.5.4 

2787 """ 

2788 return library.MagickAutoLevelImage(self.wand) 

2789 

2790 @manipulative 

2791 @trap_exception 

2792 def auto_orient(self): 

2793 """Adjusts an image so that its orientation is suitable 

2794 for viewing (i.e. top-left orientation). If available it uses 

2795 :c:func:`MagickAutoOrientImage` (was added in ImageMagick 6.8.9+) 

2796 if you have an older magick library, 

2797 it will use :attr:`_auto_orient()` method for fallback. 

2798 

2799 .. versionadded:: 0.4.1 

2800 

2801 """ 

2802 try: 

2803 return library.MagickAutoOrientImage(self.wand) 

2804 except AttributeError: # pragma: no cover 

2805 self._auto_orient() 

2806 return True 

2807 

2808 @manipulative 

2809 @trap_exception 

2810 def auto_threshold(self, method='kapur'): 

2811 """Automatically performs threshold method to reduce grayscale data 

2812 down to a binary black & white image. Included algorithms are 

2813 Kapur, Otsu, and Triangle methods. 

2814 

2815 .. warning:: 

2816 

2817 This class method is only available with ImageMagick 7.0.8-41, or 

2818 greater. 

2819 

2820 :param method: Which threshold method to apply. 

2821 See :const:`AUTO_THRESHOLD_METHODS`. 

2822 Defaults to ``'kapur'``. 

2823 :type method: :class:`basestring` 

2824 :raises WandLibraryVersionError: if function is not available on 

2825 system's library. 

2826 

2827 .. versionadded:: 0.5.5 

2828 """ 

2829 if library.MagickAutoThresholdImage is None: 

2830 msg = 'Method requires ImageMagick version 7.0.8-41 or greater.' 

2831 raise WandLibraryVersionError(msg) 

2832 assertions.string_in_list(AUTO_THRESHOLD_METHODS, 

2833 'wand.image.AUTO_THRESHOLD_METHODS', 

2834 method=method) 

2835 method_idx = AUTO_THRESHOLD_METHODS.index(method) 

2836 return library.MagickAutoThresholdImage(self.wand, method_idx) 

2837 

2838 @manipulative 

2839 @trap_exception 

2840 def black_threshold(self, threshold): 

2841 """Forces all pixels above a given color as black. Leaves pixels 

2842 above threshold unaltered. 

2843 

2844 :param threshold: Color to be referenced as a threshold. 

2845 :type threshold: :class:`Color` 

2846 

2847 .. versionadded:: 0.5.3 

2848 """ 

2849 if isinstance(threshold, string_type): 

2850 threshold = Color(threshold) 

2851 assertions.assert_color(threshold=threshold) 

2852 with threshold: 

2853 r = library.MagickBlackThresholdImage(self.wand, 

2854 threshold.resource) 

2855 return r 

2856 

2857 @manipulative 

2858 @trap_exception 

2859 def blue_shift(self, factor=1.5): 

2860 """Mutes colors of the image by shifting blue values. 

2861 

2862 :param factor: Amount to adjust values. 

2863 :type factor: :class:`numbers.Real` 

2864 

2865 .. versionadded:: 0.5.3 

2866 """ 

2867 assertions.assert_real(factor=factor) 

2868 return library.MagickBlueShiftImage(self.wand, factor) 

2869 

2870 @manipulative 

2871 @trap_exception 

2872 def blur(self, radius=0.0, sigma=0.0, channel=None): 

2873 """Blurs the image. Convolve the image with a gaussian operator 

2874 of the given ``radius`` and standard deviation (``sigma``). 

2875 For reasonable results, the ``radius`` should be larger 

2876 than ``sigma``. Use a ``radius`` of 0 and :meth:`blur()` selects 

2877 a suitable ``radius`` for you. 

2878 

2879 :param radius: the radius of the, in pixels, 

2880 not counting the center pixel. Default is ``0.0``. 

2881 :type radius: :class:`numbers.Real` 

2882 :param sigma: the standard deviation of the, in pixels. Default value 

2883 is ``0.0``. 

2884 :type sigma: :class:`numbers.Real` 

2885 :param channel: Optional color channel to apply blur. See 

2886 :const:`CHANNELS`. 

2887 :type channel: :class:`basestring` 

2888 

2889 .. versionadded:: 0.4.5 

2890 

2891 .. versionchanged:: 0.5.5 

2892 Added optional ``channel`` argument. 

2893 

2894 .. versionchanged:: 0.5.7 

2895 Positional arguments ``radius`` & ``sigman`` have been converted to 

2896 key-word arguments. 

2897 """ 

2898 assertions.assert_real(radius=radius, sigma=sigma) 

2899 if channel is None: 

2900 r = library.MagickBlurImage(self.wand, radius, sigma) 

2901 else: 

2902 channel_ch = self._channel_to_mask(channel) 

2903 if MAGICK_VERSION_NUMBER < 0x700: 

2904 r = library.MagickBlurImageChannel(self.wand, 

2905 channel_ch, 

2906 radius, 

2907 sigma) 

2908 else: # pragma: no cover 

2909 mask = library.MagickSetImageChannelMask(self.wand, channel_ch) 

2910 r = library.MagickBlurImage(self.wand, radius, sigma) 

2911 library.MagickSetImageChannelMask(self.wand, mask) 

2912 return r 

2913 

2914 @trap_exception 

2915 def border(self, color, width, height, compose="copy"): 

2916 """Surrounds the image with a border. 

2917 

2918 :param bordercolor: the border color pixel wand 

2919 :type image: :class:`~wand.color.Color` 

2920 :param width: the border width 

2921 :type width: :class:`numbers.Integral` 

2922 :param height: the border height 

2923 :type height: :class:`numbers.Integral` 

2924 :param compose: Use composite operator when applying frame. Only used 

2925 if called with ImageMagick 7+. 

2926 :type compose: :class:`basestring` 

2927 

2928 .. versionadded:: 0.3.0 

2929 .. versionchanged:: 0.5.0 

2930 Added ``compose`` paramater, and ImageMagick 7 support. 

2931 """ 

2932 if isinstance(color, string_type): 

2933 color = Color(color) 

2934 assertions.assert_color(color=color) 

2935 with color: 

2936 if MAGICK_VERSION_NUMBER < 0x700: 

2937 result = library.MagickBorderImage(self.wand, color.resource, 

2938 width, height) 

2939 else: # pragma: no cover 

2940 assertions.string_in_list(COMPOSITE_OPERATORS, 

2941 'wand.image.COMPOSITE_OPERATORS', 

2942 compose=compose) 

2943 compose_idx = COMPOSITE_OPERATORS.index(compose) 

2944 result = library.MagickBorderImage(self.wand, color.resource, 

2945 width, height, compose_idx) 

2946 return result 

2947 

2948 @manipulative 

2949 @trap_exception 

2950 def brightness_contrast(self, brightness=0.0, contrast=0.0, channel=None): 

2951 """Converts ``brightness`` & ``contrast`` paramaters into a slope & 

2952 intercept, and applies a polynomial function. 

2953 

2954 :param brightness: between ``-100.0`` and ``100.0``. Default is ``0.0`` 

2955 for unchanged. 

2956 :type brightness: :class:`numbers.Real` 

2957 :param contrast: between ``-100.0`` and ``100.0``. Default is ``0.0`` 

2958 for unchanged. 

2959 :type contrast: :class:`numbers.Real` 

2960 :param channel: Isolate a single color channel to apply contrast. 

2961 See :const:`CHANNELS`. 

2962 

2963 .. versionadded:: 0.5.4 

2964 

2965 .. versionchanged:: 0.5.5 

2966 Optional ``channel`` argument added. 

2967 """ 

2968 assertions.assert_real(brightness=brightness, contrast=contrast) 

2969 if channel is None: 

2970 r = library.MagickBrightnessContrastImage(self.wand, 

2971 brightness, 

2972 contrast) 

2973 else: 

2974 channel_ch = self._channel_to_mask(channel) 

2975 if MAGICK_VERSION_NUMBER < 0x700: 

2976 r = library.MagickBrightnessContrastImageChannel(self.wand, 

2977 channel_ch, 

2978 brightness, 

2979 contrast) 

2980 else: # pragma: no cover 

2981 mask = library.MagickSetImageChannelMask(self.wand, channel_ch) 

2982 r = library.MagickBrightnessContrastImage(self.wand, 

2983 brightness, 

2984 contrast) 

2985 library.MagickSetImageChannelMask(self.wand, mask) 

2986 return r 

2987 

2988 @manipulative 

2989 @trap_exception 

2990 def canny(self, radius=0.0, sigma=1.0, lower_percent=0.1, 

2991 upper_percent=0.3): 

2992 """Detect edges by leveraging a multi-stage Canny algorithm. 

2993 

2994 .. warning:: 

2995 

2996 This class method is only available with ImageMagick 7.0.8-41, or 

2997 greater. 

2998 

2999 :param radius: Size of gaussian filter. 

3000 :type radius: :class:`numbers.Real` 

3001 :param sigma: Standard deviation of gaussian filter. 

3002 :type sigma: :class:`numbers.Real` 

3003 :param lower_percent: Normalized lower threshold. Values between 

3004 ``0.0`` (0%) and ``1.0`` (100%). The default 

3005 value is ``0.1`` or 10%. 

3006 :type lower_percent: :class:`numbers.Real` 

3007 :param upper_percent: Normalized upper threshold. Values between 

3008 ``0.0`` (0%) and ``1.0`` (100%). The default 

3009 value is ``0.3`` or 30%. 

3010 :type upper_percent: :class:`numbers.Real` 

3011 :raises WandLibraryVersionError: if function is not available on 

3012 system's library. 

3013 

3014 .. versionadded:: 0.5.5 

3015 """ 

3016 if library.MagickCannyEdgeImage is None: 

3017 msg = 'Method requires ImageMagick version 7.0.8-41 or greater.' 

3018 raise WandLibraryVersionError(msg) 

3019 assertions.assert_real(radius=radius, sigma=sigma, 

3020 lower_percent=lower_percent, 

3021 upper_percent=upper_percent) 

3022 return library.MagickCannyEdgeImage(self.wand, radius, sigma, 

3023 lower_percent, upper_percent) 

3024 

3025 @manipulative 

3026 def caption(self, text, left=0, top=0, width=None, height=None, font=None, 

3027 gravity=None): 

3028 """Writes a caption ``text`` into the position. 

3029 

3030 :param text: text to write 

3031 :type text: :class:`basestring` 

3032 :param left: x offset in pixels 

3033 :type left: :class:`numbers.Integral` 

3034 :param top: y offset in pixels 

3035 :type top: :class:`numbers.Integral` 

3036 :param width: width of caption in pixels. 

3037 default is :attr:`width` of the image 

3038 :type width: :class:`numbers.Integral` 

3039 :param height: height of caption in pixels. 

3040 default is :attr:`height` of the image 

3041 :type height: :class:`numbers.Integral` 

3042 :param font: font to use. default is :attr:`font` of the image 

3043 :type font: :class:`wand.font.Font` 

3044 :param gravity: text placement gravity. 

3045 uses the current :attr:`gravity` setting of the image 

3046 by default 

3047 :type gravity: :class:`basestring` 

3048 

3049 .. versionadded:: 0.3.0 

3050 

3051 """ 

3052 assertions.assert_integer(left=left, top=top) 

3053 if font is not None and not isinstance(font, Font): 

3054 raise TypeError('font must be a wand.font.Font, not ' + repr(font)) 

3055 if gravity is not None: 

3056 assertions.string_in_list(GRAVITY_TYPES, 

3057 'wand.image.GRAVITY_TYPES', 

3058 gravity=gravity) 

3059 if width is None: 

3060 width = self.width - left 

3061 else: 

3062 assertions.assert_integer(width=width) 

3063 if height is None: 

3064 height = self.height - top 

3065 else: 

3066 assertions.assert_integer(height=height) 

3067 if not font: 

3068 try: 

3069 font = self.font 

3070 except TypeError: 

3071 raise TypeError('font must be specified or existing in image') 

3072 with Image() as textboard: 

3073 library.MagickSetSize(textboard.wand, width, height) 

3074 textboard.font = font 

3075 textboard.gravity = gravity or self.gravity 

3076 with Color('transparent') as background_color: 

3077 library.MagickSetBackgroundColor(textboard.wand, 

3078 background_color.resource) 

3079 textboard.read(filename=b'caption:' + text.encode('utf-8')) 

3080 self.composite(textboard, left, top) 

3081 

3082 def cdl(self, ccc): 

3083 """Alias for :meth:`color_decision_list`. 

3084 

3085 .. versionadded:: 0.5.7 

3086 """ 

3087 return self.color_decision_list(ccc) 

3088 

3089 @trap_exception 

3090 def charcoal(self, radius, sigma): 

3091 """Transform an image into a simulated charcoal drawing. 

3092 

3093 :param radius: The size of the Gaussian operator. 

3094 :type radius: :class:`numbers.Real` 

3095 :param sigma: The standard deviation of the Gaussian. 

3096 :type sigma: :class:`numbers.Real` 

3097 

3098 .. versionadded:: 0.5.3 

3099 """ 

3100 assertions.assert_real(radius=radius, sigma=sigma) 

3101 return library.MagickCharcoalImage(self.wand, radius, sigma) 

3102 

3103 @manipulative 

3104 @trap_exception 

3105 def chop(self, width, height, x=0, y=0): 

3106 """Removes a region of an image, and reduces the image size 

3107 accordingly. 

3108 

3109 :param width: Size of region. 

3110 :type width: :class:`numbers.Integral` 

3111 :param height: Size of region. 

3112 :type height: :class:`numbers.Integral` 

3113 :param x: Offset on the X-axis. 

3114 :type x: :class:`numbers.Integral` 

3115 :param y: Offset on the Y-axis. 

3116 :type y: :class:`numbers.Integral` 

3117 

3118 .. versionadded:: 0.5.5 

3119 """ 

3120 assertions.assert_unsigned_integer(width=width, height=height) 

3121 assertions.assert_integer(x=x, y=y) 

3122 return library.MagickChopImage(self.wand, width, height, x, y) 

3123 

3124 @manipulative 

3125 @trap_exception 

3126 def clahe(self, width, height, number_bins, clip_limit): 

3127 """Contrast limited adaptive histogram equalization. 

3128 

3129 .. warning:: 

3130 

3131 The CLAHE method is only available with ImageMagick-7. 

3132 

3133 :param width: Tile division width. 

3134 :type width: :class:`numbers.Integral` 

3135 :param height: Tile division height. 

3136 :type height: :class:`numbers.Integral` 

3137 :param number_bins: Histogram bins. 

3138 :type number_bins: :class:`numbers.Real` 

3139 :param clip_limit: contrast limit. 

3140 :type clip_limit: :class:`numbers.Real` 

3141 :raises WandLibraryVersionError: If system's version of ImageMagick 

3142 does not support this method. 

3143 

3144 .. versionadded:: 0.5.5 

3145 """ 

3146 if library.MagickCLAHEImage is None: 

3147 msg = 'CLAHE method not defined in ImageMagick library.' 

3148 raise WandLibraryVersionError(msg) 

3149 assertions.assert_unsigned_integer(width=width, height=height) 

3150 assertions.assert_real(number_bins=number_bins, clip_limit=clip_limit) 

3151 return library.MagickCLAHEImage(self.wand, width, height, 

3152 number_bins, clip_limit) 

3153 

3154 @trap_exception 

3155 def clamp(self, channel=None): 

3156 """Restrict color values between 0 and quantum range. This is useful 

3157 when applying arithmetic operations that could result in color values 

3158 over/under-flowing. 

3159 

3160 :param channel: Optional color channel. 

3161 :type channel: :class:`basestring` 

3162 

3163 .. versionadded:: 0.5.0 

3164 

3165 .. versionchanged:: 0.5.5 

3166 Added ``channel`` argument. 

3167 """ 

3168 if channel is None: 

3169 r = library.MagickClampImage(self.wand) 

3170 else: 

3171 channel_ch = self._channel_to_mask(channel) 

3172 if MAGICK_VERSION_NUMBER < 0x700: 

3173 r = library.MagickClampImageChannel(self.wand, channel_ch) 

3174 else: # pragma: no cover 

3175 mask = library.MagickSetImageChannelMask(self.wand, channel_ch) 

3176 r = library.MagickClampImage(self.wand) 

3177 library.MagickSetImageChannelMask(self.wand, mask) 

3178 return r 

3179 

3180 def clone(self): 

3181 """Clones the image. It is equivalent to call :class:`Image` with 

3182 ``image`` parameter. :: 

3183 

3184 with img.clone() as cloned: 

3185 # manipulate the cloned image 

3186 pass 

3187 

3188 :returns: the cloned new image 

3189 :rtype: :class:`Image` 

3190 

3191 .. versionadded:: 0.1.1 

3192 

3193 """ 

3194 return Image(image=self) 

3195 

3196 @manipulative 

3197 @trap_exception 

3198 def clut(self, image, method='undefined', channel=None): 

3199 """Replace color values by referencing another image as a Color 

3200 Look Up Table. 

3201 

3202 :param image: Color Look Up Table image. 

3203 :type image: :class:`wand.image.BaseImage` 

3204 :param method: Pixel Interpolate method. Only available with 

3205 ImageMagick-7. See :const:`PIXEL_INTERPOLATE_METHODS` 

3206 :type method: :class:`basestring` 

3207 :param channel: Optional color channel to target. See 

3208 :const:`CHANNELS` 

3209 :type channel: :class:`basestring` 

3210 

3211 .. versionadded:: 0.5.0 

3212 

3213 .. versionchanged:: 0.5.5 

3214 Added optional ``channel`` argument. 

3215 """ 

3216 if not isinstance(image, BaseImage): 

3217 raise TypeError('image must be a base image, not ' + repr(image)) 

3218 if MAGICK_VERSION_NUMBER < 0x700: 

3219 if channel is None: 

3220 r = library.MagickClutImage(self.wand, image.wand) 

3221 else: 

3222 channel_ch = self._channel_to_mask(channel) 

3223 r = library.MagickClutImageChannel(self.wand, 

3224 channel_ch, 

3225 image.wand) 

3226 else: # pragma: no cover 

3227 assertions.string_in_list(PIXEL_INTERPOLATE_METHODS, 

3228 'wand.image.PIXEL_INTERPOLATE_METHODS', 

3229 pixel_interpolate_method=method) 

3230 method_idx = PIXEL_INTERPOLATE_METHODS.index(method) 

3231 if channel is None: 

3232 r = library.MagickClutImage(self.wand, image.wand, method_idx) 

3233 else: 

3234 channel_ch = self._channel_to_mask(channel) 

3235 mask = library.MagickSetImageChannelMask(self.wand, channel_ch) 

3236 r = library.MagickClutImage(self.wand, image.wand, method_idx) 

3237 library.MagickSetImageChannelMask(self.wand, mask) 

3238 return r 

3239 

3240 @manipulative 

3241 @trap_exception 

3242 def coalesce(self): 

3243 """Rebuilds image sequence with each frame size the same as first frame, 

3244 and composites each frame atop of previous. 

3245 

3246 .. note:: 

3247 

3248 Only affects GIF, and other formats with multiple pages/layers. 

3249 

3250 .. versionadded:: 0.5.0 

3251 """ 

3252 r = library.MagickCoalesceImages(self.wand) 

3253 if r: 

3254 self.wand = r 

3255 self.reset_sequence() 

3256 return bool(r) 

3257 

3258 @manipulative 

3259 @trap_exception 

3260 def color_decision_list(self, ccc): 

3261 """Applies color correction from a Color Correction Collection (CCC) 

3262 xml string. An example of xml: 

3263 

3264 .. code-block:: xml 

3265 

3266 <ColorCorrectionCollection xmlns="urn:ASC:CDL:v1.2"> 

3267 <ColorCorrection id="cc03345"> 

3268 <SOPNode> 

3269 <Slope> 0.9 1.2 0.5 </Slope> 

3270 <Offset> 0.4 -0.5 0.6 </Offset> 

3271 <Power> 1.0 0.8 1.5 </Power> 

3272 </SOPNode> 

3273 <SATNode> 

3274 <Saturation> 0.85 </Saturation> 

3275 </SATNode> 

3276 </ColorCorrection> 

3277 </ColorCorrectionCollection> 

3278 

3279 :param ccc: A XML string of the CCC contents. 

3280 :type ccc: :class:`basestring` 

3281 

3282 .. versionadded:: 0.5.7 

3283 """ 

3284 return library.MagickColorDecisionListImage(self.wand, binary(ccc)) 

3285 

3286 def color_map(self, index, color=None): 

3287 """Get & Set a color at a palette index. If ``color`` is given, 

3288 the color at the index location will be set & returned. Omitting the 

3289 ``color`` argument will only return the color value at index. 

3290 

3291 Valid indexes are between ``0`` and total :attr:`colors` of the image. 

3292 

3293 .. note:: 

3294 

3295 Ensure the image type is set to ``'palette'`` before calling the 

3296 :meth:`color_map` method. For example:: 

3297 

3298 with Image(filename='graph.png') as img: 

3299 img.type = 'palette' 

3300 palette = [img.color_map(idx) for idx in range(img.colors)] 

3301 # ... 

3302 

3303 :param index: The color postion of the image palette. 

3304 :type index: :class:`numbers.Integral` 

3305 :param color: Optional color to _set_ at the given index. 

3306 :type color: :class:`wand.color.Color` 

3307 :returns: Color at index. 

3308 :rtype: :class:`wand.color.Color` 

3309 

3310 .. versionadded:: 0.5.3 

3311 """ 

3312 if not isinstance(index, numbers.Integral): 

3313 raise TypeError('index most be an integer, not ' + repr(index)) 

3314 if index < 0 or index >= self.colors: 

3315 raise ValueError('index is out of palette range') 

3316 if color: 

3317 if isinstance(color, string_type): 

3318 color = Color(color) 

3319 if not isinstance(color, Color): 

3320 raise TypeError('expecting in instance of Color, not ' + 

3321 repr(color)) 

3322 with color: 

3323 r = library.MagickSetImageColormapColor(self.wand, 

3324 index, 

3325 color.resource) 

3326 if not r: # pragma: no cover 

3327 self.raise_exception() 

3328 else: 

3329 color_ptr = library.NewPixelWand() 

3330 r = library.MagickGetImageColormapColor(self.wand, 

3331 index, 

3332 color_ptr) 

3333 if not r: # pragma: no cover 

3334 color_ptr = library.DestroyPixelWand(color_ptr) 

3335 self.raise_exception() 

3336 color = Color.from_pixelwand(color_ptr) 

3337 color_ptr = library.DestroyPixelWand(color_ptr) 

3338 return color 

3339 

3340 @manipulative 

3341 @trap_exception 

3342 def color_matrix(self, matrix): 

3343 """Adjust color values by applying a matrix transform per pixel. 

3344 

3345 Matrix should be given as 2D list, with a max size of 6x6. 

3346 

3347 An example of 3x3 matrix:: 

3348 

3349 matrix = [ 

3350 [1.0, 0.0, 0.0], 

3351 [0.0, 1.0, 0.0], 

3352 [0.0, 0.0, 1.0], 

3353 ] 

3354 

3355 Which would translate RGB color channels by calculating the 

3356 following: 

3357 

3358 .. math:: 

3359 

3360 \\begin{aligned} 

3361 red' &= 1.0 * red + 0.0 * green + 0.0 * blue\\\\ 

3362 green' &= 0.0 * red + 1.0 * green + 0.0 * blue\\\\ 

3363 blue' &= 0.0 * red + 0.0 * green + 1.0 * blue\\\\ 

3364 \\end{aligned} 

3365 

3366 For RGB colorspace images, the rows & columns are laid out as: 

3367 

3368 +---------+-----+-------+------+------+-------+--------+ 

3369 | | Red | Green | Blue | n/a | Alpha | Offset | 

3370 +=========+=====+=======+======+======+=======+========+ 

3371 | Red' | 1 | 0 | 0 | 0 | 0 | 0 | 

3372 +---------+-----+-------+------+------+-------+--------+ 

3373 | Green' | 0 | 1 | 0 | 0 | 0 | 0 | 

3374 +---------+-----+-------+------+------+-------+--------+ 

3375 | Blue' | 0 | 0 | 1 | 0 | 0 | 0 | 

3376 +---------+-----+-------+------+------+-------+--------+ 

3377 | n/a | 0 | 0 | 0 | 0 | 0 | 0 | 

3378 +---------+-----+-------+------+------+-------+--------+ 

3379 | Alpha' | 0 | 0 | 0 | 0 | 0 | 0 | 

3380 +---------+-----+-------+------+------+-------+--------+ 

3381 | Offset' | 0 | 0 | 0 | 0 | 0 | 0 | 

3382 +---------+-----+-------+------+------+-------+--------+ 

3383 

3384 Or for a CMYK colorspace image: 

3385 

3386 +----------+------+--------+---------+-------+-------+--------+ 

3387 | | Cyan | Yellow | Magenta | Black | Alpha | Offset | 

3388 +==========+======+========+=========+=======+=======+========+ 

3389 | Cyan' | 1 | 0 | 0 | 0 | 0 | 0 | 

3390 +----------+------+--------+---------+-------+-------+--------+ 

3391 | Yellow' | 0 | 1 | 0 | 0 | 0 | 0 | 

3392 +----------+------+--------+---------+-------+-------+--------+ 

3393 | Magenta' | 0 | 0 | 1 | 0 | 0 | 0 | 

3394 +----------+------+--------+---------+-------+-------+--------+ 

3395 | Black' | 0 | 0 | 0 | 0 | 0 | 0 | 

3396 +----------+------+--------+---------+-------+-------+--------+ 

3397 | Alpha' | 0 | 0 | 0 | 0 | 0 | 0 | 

3398 +----------+------+--------+---------+-------+-------+--------+ 

3399 | Offset' | 0 | 0 | 0 | 0 | 0 | 0 | 

3400 +----------+------+--------+---------+-------+-------+--------+ 

3401 

3402 See `color-matrix`__ for examples. 

3403 

3404 __ https://www.imagemagick.org/Usage/color_mods/#color-matrix 

3405 

3406 :param matrix: 2D List of doubles. 

3407 :type matrix: :class:`collections.abc.Sequence` 

3408 

3409 .. versionadded:: 0.5.3 

3410 """ 

3411 if not isinstance(matrix, abc.Sequence): 

3412 raise TypeError('matrix must be a sequence, not ' + repr(matrix)) 

3413 rows = len(matrix) 

3414 columns = None 

3415 values = [] 

3416 for row in matrix: 

3417 if not isinstance(row, abc.Sequence): 

3418 raise TypeError('nested row must be a sequence, not ' + 

3419 repr(row)) 

3420 if columns is None: 

3421 columns = len(row) 

3422 elif columns != len(row): 

3423 raise ValueError('rows have different column length') 

3424 for column in row: 

3425 values.append(str(column)) 

3426 kernel = binary('{0}x{1}:{2}'.format(columns, 

3427 rows, 

3428 ','.join(values))) 

3429 exception_info = libmagick.AcquireExceptionInfo() 

3430 if MAGICK_VERSION_NUMBER < 0x700: 

3431 kernel_info = libmagick.AcquireKernelInfo(kernel) 

3432 else: # pragma: no cover 

3433 kernel_info = libmagick.AcquireKernelInfo(kernel, exception_info) 

3434 exception_info = libmagick.DestroyExceptionInfo(exception_info) 

3435 r = library.MagickColorMatrixImage(self.wand, kernel_info) 

3436 kernel_info = libmagick.DestroyKernelInfo(kernel_info) 

3437 return r 

3438 

3439 @manipulative 

3440 @trap_exception 

3441 def colorize(self, color=None, alpha=None): 

3442 """Blends a given fill color over the image. The amount of blend is 

3443 determined by the color channels given by the ``alpha`` argument. 

3444 

3445 :param color: Color to paint image with. 

3446 :type color: :class:`wand.color.Color` 

3447 :param alpha: Defines how to blend color. 

3448 :type alpha: :class:`wand.color.Color` 

3449 

3450 .. versionadded:: 0.5.3 

3451 """ 

3452 if isinstance(color, string_type): 

3453 color = Color(color) 

3454 if isinstance(alpha, string_type): 

3455 alpha = Color(alpha) 

3456 assertions.assert_color(color=color, alpha=alpha) 

3457 with color: 

3458 with alpha: 

3459 r = library.MagickColorizeImage(self.wand, 

3460 color.resource, 

3461 alpha.resource) 

3462 return r 

3463 

3464 @manipulative 

3465 @trap_exception 

3466 def combine(self, channel='rgb_channels', colorspace='rgb'): 

3467 """Creates an image where each color channel is assigned by a grayscale 

3468 image in a sequence. 

3469 

3470 .. warning:: 

3471 

3472 If your using ImageMagick-6, use ``channel`` argument to control 

3473 the color-channel order. With ImageMagick-7, the ``channel`` 

3474 argument has been replaced with ``colorspace``. 

3475 

3476 For example:: 

3477 

3478 for wand.image import Image 

3479 

3480 with Image() as img: 

3481 img.read(filename='red_channel.png') 

3482 img.read(filename='green_channel.png') 

3483 img.read(filename='blue_channel.png') 

3484 img.combine(colorspace='rgb') 

3485 img.save(filename='output.png') 

3486 

3487 :param channel: Determines the colorchannel ordering of the 

3488 sequence. Only used for ImageMagick-6. 

3489 See :const:`CHANNELS`. 

3490 :type channel: :class:`basestring` 

3491 :param colorspace: Determines the colorchannel ordering of the 

3492 sequence. Only used for ImageMagick-7. 

3493 See :const:`COLORSPACE_TYPES`. 

3494 :type colorspace: :class:`basestring` 

3495 

3496 .. versionadded:: 0.5.9 

3497 """ 

3498 assertions.string_in_list(COLORSPACE_TYPES, 

3499 'wand.image.COLORSPACE_TYPES', 

3500 colorspace=colorspace) 

3501 library.MagickResetIterator(self.wand) 

3502 colorspace_c = COLORSPACE_TYPES.index(colorspace) 

3503 channel_c = self._channel_to_mask(channel) 

3504 if MAGICK_VERSION_NUMBER < 0x700: 

3505 new_wand = library.MagickCombineImages(self.wand, channel_c) 

3506 else: # pragma: no-cover 

3507 new_wand = library.MagickCombineImages(self.wand, colorspace_c) 

3508 if new_wand: 

3509 self.wand = new_wand 

3510 self.reset_sequence() 

3511 return bool(new_wand) 

3512 

3513 @manipulative 

3514 def compare(self, image, metric='undefined', highlight=None, 

3515 lowlight=None): 

3516 """Compares an image to a reconstructed image. 

3517 

3518 Set :attr:`fuzz` property to adjust pixel-compare thresholds. 

3519 

3520 For example:: 

3521 

3522 from wand.image import Image 

3523 

3524 with Image(filename='input.jpg') as base: 

3525 with Image(filename='subject.jpg') as img: 

3526 base.fuzz = base.quantum_range * 0.20 # Threshold of 20% 

3527 result_image, result_metric = base.compare(img) 

3528 with result_image: 

3529 result_image.save(filename='diff.jpg') 

3530 

3531 :param image: The reference image 

3532 :type image: :class:`wand.image.Image` 

3533 :param metric: The metric type to use for comparing. See 

3534 :const:`COMPARE_METRICS` 

3535 :type metric: :class:`basestring` 

3536 :param highlight: Set the color of the delta pixels in the resulting 

3537 difference image. 

3538 :type highlight: :class:`~wand.color.Color` or :class:`basestring` 

3539 :param lowlight: Set the color of the similar pixels in the resulting 

3540 difference image. 

3541 :type lowlight: :class:`~wand.color.Color` or :class:`basestring` 

3542 :returns: The difference image(:class:`wand.image.Image`), 

3543 the computed distortion between the images 

3544 (:class:`numbers.Integral`) 

3545 :rtype: :class:`tuple` 

3546 

3547 .. versionadded:: 0.4.3 

3548 

3549 .. versionchanged:: 0.5.3 

3550 Added support for ``highlight`` & ``lowlight``. 

3551 """ 

3552 assertions.string_in_list(COMPARE_METRICS, 

3553 'wand.image.COMPARE_METRICS', 

3554 metric=metric) 

3555 if highlight: 

3556 if isinstance(highlight, Color): 

3557 highlight = highlight.string 

3558 library.MagickSetImageArtifact(self.wand, 

3559 b'compare:highlight-color', 

3560 binary(highlight)) 

3561 if lowlight: 

3562 if isinstance(lowlight, Color): 

3563 lowlight = lowlight.string 

3564 library.MagickSetImageArtifact(self.wand, 

3565 b'compare:lowlight-color', 

3566 binary(lowlight)) 

3567 metric = COMPARE_METRICS.index(metric) 

3568 distortion = ctypes.c_double() 

3569 compared_image = library.MagickCompareImages(self.wand, image.wand, 

3570 metric, 

3571 ctypes.byref(distortion)) 

3572 return Image(BaseImage(compared_image)), distortion.value 

3573 

3574 @manipulative 

3575 def complex(self, operator='undefined', snr=None): 

3576 """Performs `complex`_ mathematics against two images in a sequence, 

3577 and generates a new image with two results. 

3578 

3579 .. seealso:: 

3580 

3581 :meth:`forward_fourier_transform` & 

3582 :meth:`inverse_fourier_transform` 

3583 

3584 .. code:: 

3585 

3586 from wand.image import Image 

3587 

3588 with Image(filename='real_part.png') as imgA: 

3589 with Image(filename='imaginary_part.png') as imgB: 

3590 imgA.sequence.append(imgB) 

3591 with imgA.complex('conjugate') as results: 

3592 results.save(filename='output-%02d.png') 

3593 

3594 .. _complex: https://en.wikipedia.org/wiki/Complex_number 

3595 

3596 .. warning:: 

3597 

3598 This class method is only available with ImageMagick 7.0.8-41, or 

3599 greater. 

3600 

3601 :param operator: Define which mathematic operator to perform. See 

3602 :const:`COMPLEX_OPERATORS`. 

3603 :type operator: :class:`basestring` 

3604 :param snr: Optional ``SNR`` parameter for ``'divide'`` operator. 

3605 :type snr: :class:`basestring` 

3606 :raises WandLibraryVersionError: If ImageMagick library does not 

3607 support this function. 

3608 

3609 .. versionadded:: 0.5.5 

3610 """ 

3611 if library.MagickComplexImages is None: 

3612 msg = 'Method requires ImageMagick version 7.0.8-41 or greater.' 

3613 raise WandLibraryVersionError(msg) 

3614 assertions.string_in_list(COMPLEX_OPERATORS, 

3615 'wand.image.COMPLEX_OPERATORS', 

3616 operator=operator) 

3617 if snr is not None: 

3618 library.MagickSetImageArtifact(self.wand, 

3619 b'complex:snr=float', 

3620 b'{0}'.format(snr)) 

3621 operator_idx = COMPLEX_OPERATORS.index(operator) 

3622 wand = library.MagickComplexImages(self.wand, operator_idx) 

3623 if not bool(wand): 

3624 self.raise_exception() 

3625 return Image(BaseImage(wand)) 

3626 

3627 @trap_exception 

3628 def composite(self, image, left=None, top=None, operator='over', 

3629 arguments=None, gravity=None): 

3630 """Places the supplied ``image`` over the current image, with the top 

3631 left corner of ``image`` at coordinates ``left``, ``top`` of the 

3632 current image. The dimensions of the current image are not changed. 

3633 

3634 :param image: the image placed over the current image 

3635 :type image: :class:`wand.image.Image` 

3636 :param left: the x-coordinate where `image` will be placed 

3637 :type left: :class:`numbers.Integral` 

3638 :param top: the y-coordinate where `image` will be placed 

3639 :type top: :class:`numbers.Integral` 

3640 :param operator: the operator that affects how the composite 

3641 is applied to the image. available values 

3642 can be found in the :const:`COMPOSITE_OPERATORS` 

3643 list. Default is ``'over'``. 

3644 :type operator: :class:`basestring` 

3645 :param arguments: Additional numbers given as a geometry string, or 

3646 comma delimited values. This is needed for 

3647 ``'blend'``, ``'displace'``, ``'dissolve'``, and 

3648 ``'modulate'`` operators. 

3649 :type arguments: :class:`basestring` 

3650 :param gravity: Calculate the ``top`` & ``left`` values based on 

3651 gravity value from :const:`GRAVITY_TYPES`. 

3652 :type: gravity: :class:`basestring` 

3653 

3654 .. versionadded:: 0.2.0 

3655 

3656 .. versionchanged:: 0.5.3 

3657 The operator can be set, as well as additional composite arguments. 

3658 

3659 .. versionchanged:: 0.5.3 

3660 Optional ``gravity`` argument was added. 

3661 """ 

3662 if top is None and left is None: 

3663 if gravity is None: 

3664 gravity = self.gravity 

3665 top, left = self._gravity_to_offset(gravity, 

3666 image.width, 

3667 image.height) 

3668 elif gravity is not None: 

3669 raise TypeError('Can not use gravity if top & left are given') 

3670 elif top is None: 

3671 top = 0 

3672 elif left is None: 

3673 left = 0 

3674 assertions.assert_integer(left=left, top=top) 

3675 try: 

3676 op = COMPOSITE_OPERATORS.index(operator) 

3677 except IndexError: 

3678 raise ValueError(repr(operator) + ' is an invalid composite ' 

3679 'operator type; see wand.image.COMPOSITE_' 

3680 'OPERATORS dictionary') 

3681 if arguments: 

3682 assertions.assert_string(arguments=arguments) 

3683 r = library.MagickSetImageArtifact(image.wand, 

3684 binary('compose:args'), 

3685 binary(arguments)) 

3686 if not r: 

3687 self.raise_exception() 

3688 r = library.MagickSetImageArtifact(self.wand, 

3689 binary('compose:args'), 

3690 binary(arguments)) 

3691 if not r: # pragma: no cover 

3692 self.raise_exception() 

3693 if MAGICK_VERSION_NUMBER < 0x700: 

3694 r = library.MagickCompositeImage(self.wand, image.wand, op, 

3695 int(left), int(top)) 

3696 else: # pragma: no cover 

3697 r = library.MagickCompositeImage(self.wand, image.wand, op, True, 

3698 int(left), int(top)) 

3699 return r 

3700 

3701 @manipulative 

3702 @trap_exception 

3703 def composite_channel(self, channel, image, operator, left=None, top=None, 

3704 arguments=None, gravity=None): 

3705 """Composite two images using the particular ``channel``. 

3706 

3707 :param channel: the channel type. available values can be found 

3708 in the :const:`CHANNELS` mapping 

3709 :param image: the composited source image. 

3710 (the receiver image becomes the destination) 

3711 :type image: :class:`Image` 

3712 :param operator: the operator that affects how the composite 

3713 is applied to the image. available values 

3714 can be found in the :const:`COMPOSITE_OPERATORS` 

3715 list 

3716 :type operator: :class:`basestring` 

3717 :param left: the column offset of the composited source image 

3718 :type left: :class:`numbers.Integral` 

3719 :param top: the row offset of the composited source image 

3720 :type top: :class:`numbers.Integral` 

3721 :param arguments: Additional numbers given as a geometry string, or 

3722 comma delimited values. This is needed for 

3723 ``'blend'``, ``'displace'``, ``'dissolve'``, and 

3724 ``'modulate'`` operators. 

3725 :type arguments: :class:`basestring` 

3726 :param gravity: Calculate the ``top`` & ``left`` values based on 

3727 gravity value from :const:`GRAVITY_TYPES`. 

3728 :type: gravity: :class:`basestring` 

3729 :raises ValueError: when the given ``channel`` or 

3730 ``operator`` is invalid 

3731 

3732 .. versionadded:: 0.3.0 

3733 

3734 .. versionchanged:: 0.5.3 

3735 Support for optional composite arguments has been added. 

3736 

3737 .. versionchanged:: 0.5.3 

3738 Optional ``gravity`` argument was added. 

3739 """ 

3740 assertions.assert_string(operator=operator) 

3741 ch_const = self._channel_to_mask(channel) 

3742 if gravity: 

3743 if left is None and top is None: 

3744 top, left = self._gravity_to_offset(gravity, 

3745 image.width, 

3746 image.height) 

3747 else: 

3748 raise TypeError('Can not use gravity if top & left are given') 

3749 if top is None: 

3750 top = 0 

3751 if left is None: 

3752 left = 0 

3753 assertions.assert_integer(left=left, top=top) 

3754 try: 

3755 op = COMPOSITE_OPERATORS.index(operator) 

3756 except IndexError: 

3757 raise IndexError(repr(operator) + ' is an invalid composite ' 

3758 'operator type; see wand.image.COMPOSITE_' 

3759 'OPERATORS dictionary') 

3760 if arguments: 

3761 assertions.assert_string(arguments=arguments) 

3762 library.MagickSetImageArtifact(image.wand, 

3763 binary('compose:args'), 

3764 binary(arguments)) 

3765 library.MagickSetImageArtifact(self.wand, 

3766 binary('compose:args'), 

3767 binary(arguments)) 

3768 if library.MagickCompositeImageChannel: 

3769 r = library.MagickCompositeImageChannel(self.wand, ch_const, 

3770 image.wand, op, int(left), 

3771 int(top)) 

3772 else: # pragma: no cover 

3773 ch_mask = library.MagickSetImageChannelMask(self.wand, ch_const) 

3774 r = library.MagickCompositeImage(self.wand, image.wand, op, True, 

3775 int(left), int(top)) 

3776 library.MagickSetImageChannelMask(self.wand, ch_mask) 

3777 return r 

3778 

3779 @manipulative 

3780 @trap_exception 

3781 def concat(self, stacked=False): 

3782 """Concatenates images in stack into a single image. Left-to-right 

3783 by default, top-to-bottom if ``stacked`` is True. 

3784 

3785 :param stacked: stack images in a column, or in a row (default) 

3786 :type stacked: :class:`bool` 

3787 

3788 .. versionadded:: 0.5.0 

3789 """ 

3790 assertions.assert_bool(stacked=stacked) 

3791 r = library.MagickAppendImages(self.wand, stacked) 

3792 if r: 

3793 self.wand = r 

3794 self.reset_sequence() 

3795 return bool(r) 

3796 

3797 def connected_components(self, connectivity=4, area_threshold=None, 

3798 mean_color=False, keep=None, remove=None): 

3799 """Evaluates binary image, and groups connected pixels into objects. 

3800 This method will also return a list of 

3801 :class:`ConnectedComponentObject` instances that will describe an 

3802 object's features. 

3803 

3804 .. code:: 

3805 

3806 from wand.image import Image 

3807 

3808 with Image(filename='objects.gif') as img: 

3809 objects = img.connected_components() 

3810 for cc_obj in objects: 

3811 print("{0._id}: {0.size} {0.offset}".format(cc_obj)) 

3812 

3813 #=> 0: (256, 171) (0, 0) 

3814 #=> 2: (120, 135) (104, 18) 

3815 #=> 3: (50, 36) (129, 44) 

3816 #=> 4: (21, 23) (0, 45) 

3817 #=> 1: (4, 10) (252, 0) 

3818 

3819 .. warning:: 

3820 

3821 This class method is only available with ImageMagick 7.0.8-41, or 

3822 greater. 

3823 

3824 .. tip:: 

3825 

3826 Set :attr:`fuzz` property to increase pixel matching by reducing 

3827 tolerance of color-value comparisons:: 

3828 

3829 from wand.image import Image 

3830 from wand.version import QUANTUM_RANGE 

3831 

3832 with Image(filename='objects.gif') as img: 

3833 img.fuzz = 0.1 * QUANTUM_RANGE # 10% 

3834 objects = img.connected_components() 

3835 

3836 :param connectivity: Either ``4``, or ``8``. A value of ``4`` will 

3837 evaluate each pixels top-bottom, & left-right 

3838 neighbors. A value of ``8`` will use the same 

3839 pixels as with ``4``, but will also include the 

3840 four corners of each pixel. 

3841 :type connectivity: :class:`numbers.Integral` 

3842 :param area_threshold: Optional argument to exclude objects under an 

3843 area size. 

3844 :type area_threshold: :class:`basestring` 

3845 :param mean_color: Optional argument. Replace object color with mean 

3846 color of the source image. 

3847 :type mean_color: :class:`bool` 

3848 :param keep: Comma separated list of object IDs to isolate, the reset 

3849 are converted to transparent. 

3850 :type keep: :class:`basestring` 

3851 :param remove: Comma separated list of object IDs to ignore, and 

3852 convert to transparent. 

3853 :type remove: :class:`basestring` 

3854 :returns: A list of :class:`ConnectedComponentObject`. 

3855 :rtype: :class:`list` [:class:`ConnectedComponentObject`] 

3856 :raises WandLibraryVersionError: If ImageMagick library 

3857 does not support this method. 

3858 

3859 .. versionadded:: 0.5.5 

3860 

3861 .. versionchanged:: 0.5.6 

3862 Added ``mean_color``, ``keep``, & ``remove`` optional arguments. 

3863 """ 

3864 if library.MagickConnectedComponentsImage is None: 

3865 msg = 'Method requires ImageMagick version 7.0.8-41 or greater.' 

3866 raise WandLibraryVersionError(msg) 

3867 if connectivity not in (4, 8): 

3868 raise ValueError('connectivity must be 4, or 8.') 

3869 if area_threshold is not None: 

3870 key = b'connected-components:area-threshold' 

3871 library.MagickSetImageArtifact(self.wand, 

3872 key, 

3873 b'{0}'.format(area_threshold)) 

3874 if mean_color: 

3875 key = b'connected-components:mean-color' 

3876 library.MagickSetImageArtifact(self.wand, 

3877 key, 

3878 b'true') 

3879 if keep is not None: 

3880 key = b'connected-components:keep' 

3881 library.MagickSetImageArtifact(self.wand, 

3882 key, 

3883 b'{0}'.format(keep)) 

3884 if remove is not None: 

3885 key = b'connected-components:remove' 

3886 library.MagickSetImageArtifact(self.wand, 

3887 key, 

3888 b'{0}'.format(remove)) 

3889 objects_ptr = ctypes.c_void_p(0) 

3890 ccoi_mem_size = ctypes.sizeof(CCObjectInfo) 

3891 r = library.MagickConnectedComponentsImage(self.wand, connectivity, 

3892 ctypes.byref(objects_ptr)) 

3893 objects = [] 

3894 if r and objects_ptr.value: 

3895 for i in range(self.colors): 

3896 temp = CCObjectInfo() 

3897 src_addr = objects_ptr.value + (i * ccoi_mem_size) 

3898 ctypes.memmove(ctypes.addressof(temp), src_addr, ccoi_mem_size) 

3899 objects.append(ConnectedComponentObject(temp)) 

3900 objects_ptr = libmagick.RelinquishMagickMemory(objects_ptr) 

3901 else: 

3902 self.raise_exception() 

3903 return objects 

3904 

3905 @manipulative 

3906 @trap_exception 

3907 def contrast(self, sharpen=True): 

3908 """Enhances the difference between lighter & darker values of the 

3909 image. Set ``sharpen`` to ``False`` to reduce contrast. 

3910 

3911 :param sharpen: Increase, or decrease, contrast. Default is ``True`` 

3912 for increased contrast. 

3913 :type sharpen: :class:`bool` 

3914 

3915 .. versionadded:: 0.5.7 

3916 """ 

3917 assertions.assert_bool(sharpen=sharpen) 

3918 return library.MagickContrastImage(self.wand, sharpen) 

3919 

3920 @manipulative 

3921 @trap_exception 

3922 def contrast_stretch(self, black_point=0.0, white_point=None, 

3923 channel=None): 

3924 """Enhance contrast of image by adjusting the span of the available 

3925 colors. 

3926 

3927 :param black_point: black point between 0.0 and 1.0. default is 0.0 

3928 :type black_point: :class:`numbers.Real` 

3929 :param white_point: white point between 0.0 and 1.0. 

3930 Defaults to the same value given to the 

3931 ``black_point`` argument. 

3932 :type white_point: :class:`numbers.Real` 

3933 :param channel: optional color channel to apply contrast stretch 

3934 :type channel: :const:`CHANNELS` 

3935 :raises ValueError: if ``channel`` is not in :const:`CHANNELS` 

3936 

3937 .. versionadded:: 0.4.1 

3938 

3939 .. versionchanged:: 0.5.5 

3940 The ``white_point`` argument will now default to the value given 

3941 by the ``black_point`` argument. 

3942 """ 

3943 assertions.assert_real(black_point=black_point) 

3944 # If only black-point is given, match CLI behavior by 

3945 # calculating white point 

3946 if white_point is None: 

3947 white_point = black_point 

3948 assertions.assert_real(white_point=white_point) 

3949 contrast_range = float(self.width * self.height) 

3950 if 0.0 < black_point <= 1.0: 

3951 black_point *= contrast_range 

3952 if 0.0 < white_point <= 1.0: 

3953 white_point *= contrast_range 

3954 white_point = contrast_range - white_point 

3955 if channel is None: 

3956 r = library.MagickContrastStretchImage(self.wand, 

3957 black_point, 

3958 white_point) 

3959 else: 

3960 ch_const = self._channel_to_mask(channel) 

3961 if library.MagickContrastStretchImageChannel: 

3962 r = library.MagickContrastStretchImageChannel(self.wand, 

3963 ch_const, 

3964 black_point, 

3965 white_point) 

3966 else: # pragma: no cover 

3967 # Set active channel, and capture mask to restore. 

3968 channel_mask = library.MagickSetImageChannelMask(self.wand, 

3969 ch_const) 

3970 r = library.MagickContrastStretchImage(self.wand, 

3971 black_point, 

3972 white_point) 

3973 # Restore original state of channels 

3974 library.MagickSetImageChannelMask(self.wand, channel_mask) 

3975 return r 

3976 

3977 @manipulative 

3978 @trap_exception 

3979 def crop(self, left=0, top=0, right=None, bottom=None, 

3980 width=None, height=None, reset_coords=True, 

3981 gravity=None): 

3982 """Crops the image in-place. 

3983 

3984 .. sourcecode:: text 

3985 

3986 +--------------------------------------------------+ 

3987 | ^ ^ | 

3988 | | | | 

3989 | top | | 

3990 | | | | 

3991 | v | | 

3992 | <-- left --> +-------------------+ bottom | 

3993 | | ^ | | | 

3994 | | <-- width --|---> | | | 

3995 | | height | | | 

3996 | | | | | | 

3997 | | v | | | 

3998 | +-------------------+ v | 

3999 | <--------------- right ----------> | 

4000 +--------------------------------------------------+ 

4001 

4002 :param left: x-offset of the cropped image. default is 0 

4003 :type left: :class:`numbers.Integral` 

4004 :param top: y-offset of the cropped image. default is 0 

4005 :type top: :class:`numbers.Integral` 

4006 :param right: second x-offset of the cropped image. 

4007 default is the :attr:`width` of the image. 

4008 this parameter and ``width`` parameter are exclusive 

4009 each other 

4010 :type right: :class:`numbers.Integral` 

4011 :param bottom: second y-offset of the cropped image. 

4012 default is the :attr:`height` of the image. 

4013 this parameter and ``height`` parameter are exclusive 

4014 each other 

4015 :type bottom: :class:`numbers.Integral` 

4016 :param width: the :attr:`width` of the cropped image. 

4017 default is the :attr:`width` of the image. 

4018 this parameter and ``right`` parameter are exclusive 

4019 each other 

4020 :type width: :class:`numbers.Integral` 

4021 :param height: the :attr:`height` of the cropped image. 

4022 default is the :attr:`height` of the image. 

4023 this parameter and ``bottom`` parameter are exclusive 

4024 each other 

4025 :type height: :class:`numbers.Integral` 

4026 :param reset_coords: 

4027 optional flag. If set, after the rotation, the coordinate frame 

4028 will be relocated to the upper-left corner of the new image. 

4029 By default is `True`. 

4030 :type reset_coords: :class:`bool` 

4031 :param gravity: optional flag. If set, will calculate the :attr:`top` 

4032 and :attr:`left` attributes. This requires both 

4033 :attr:`width` and :attr:`height` parameters to be 

4034 included. 

4035 :type gravity: :const:`GRAVITY_TYPES` 

4036 :raises ValueError: when one or more arguments are invalid 

4037 

4038 .. note:: 

4039 

4040 If you want to crop the image but not in-place, use slicing 

4041 operator. 

4042 

4043 .. versionchanged:: 0.4.1 

4044 Added ``gravity`` option. Using ``gravity`` along with 

4045 ``width`` & ``height`` to auto-adjust ``left`` & ``top`` 

4046 attributes. 

4047 

4048 .. versionchanged:: 0.1.8 

4049 Made to raise :exc:`~exceptions.ValueError` instead of 

4050 :exc:`~exceptions.IndexError` for invalid ``width``/``height`` 

4051 arguments. 

4052 

4053 .. versionadded:: 0.1.7 

4054 

4055 """ 

4056 if not (right is None or width is None): 

4057 raise TypeError('parameters right and width are exclusive each ' 

4058 'other; use one at a time') 

4059 elif not (bottom is None or height is None): 

4060 raise TypeError('parameters bottom and height are exclusive each ' 

4061 'other; use one at a time') 

4062 

4063 def abs_(n, m, null=None): 

4064 if n is None: 

4065 return m if null is None else null 

4066 elif not isinstance(n, numbers.Integral): 

4067 raise TypeError('expected integer, not ' + repr(n)) 

4068 elif n > m: 

4069 raise ValueError(repr(n) + ' > ' + repr(m)) 

4070 return m + n if n < 0 else n 

4071 

4072 # Define left & top if gravity is given. 

4073 if gravity: 

4074 if width is None or height is None: 

4075 raise TypeError( 

4076 'both width and height must be defined with gravity' 

4077 ) 

4078 top, left = self._gravity_to_offset(gravity, width, height) 

4079 else: 

4080 left = abs_(left, self.width, 0) 

4081 top = abs_(top, self.height, 0) 

4082 

4083 if width is None: 

4084 right = abs_(right, self.width) 

4085 width = right - left 

4086 if height is None: 

4087 bottom = abs_(bottom, self.height) 

4088 height = bottom - top 

4089 assertions.assert_counting_number(width=width, height=height) 

4090 if ( 

4091 left == top == 0 and 

4092 width == self.width and 

4093 height == self.height 

4094 ): 

4095 return True 

4096 if self.animation: 

4097 self.wand = library.MagickCoalesceImages(self.wand) 

4098 self.reset_sequence() 

4099 library.MagickSetLastIterator(self.wand) 

4100 n = library.MagickGetIteratorIndex(self.wand) 

4101 library.MagickResetIterator(self.wand) 

4102 for i in xrange(0, n + 1): 

4103 library.MagickSetIteratorIndex(self.wand, i) 

4104 r = library.MagickCropImage(self.wand, 

4105 width, height, 

4106 left, top) 

4107 if reset_coords: 

4108 self.reset_coords() 

4109 else: 

4110 r = library.MagickCropImage(self.wand, width, height, left, top) 

4111 if reset_coords: 

4112 self.reset_coords() 

4113 return r 

4114 

4115 @trap_exception 

4116 def cycle_color_map(self, offset=1): 

4117 """Shift the image color-map by a given offset. 

4118 

4119 :param offset: number of steps to rotate index by. 

4120 :type offset: :class:`numbers.Integral` 

4121 

4122 .. versionadded:: 0.5.3 

4123 """ 

4124 assertions.assert_integer(offset=offset) 

4125 return library.MagickCycleColormapImage(self.wand, offset) 

4126 

4127 @manipulative 

4128 @trap_exception 

4129 def deconstruct(self): 

4130 """Iterates over internal image stack, and adjust each frame size to 

4131 minimum bounding region of any changes from the previous frame. 

4132 

4133 .. versionadded:: 0.5.0 

4134 """ 

4135 r = library.MagickDeconstructImages(self.wand) 

4136 if r: 

4137 self.wand = r 

4138 self.reset_sequence() 

4139 return bool(r) 

4140 

4141 @manipulative 

4142 @trap_exception 

4143 def deskew(self, threshold): 

4144 """Attempts to remove skew artifacts common with most 

4145 scanning & optical import devices. 

4146 

4147 :params threshold: limit between foreground & background. Use a real 

4148 number between `0.0` & `1.0` to match CLI's percent 

4149 argument. 

4150 :type threshold: :class:`numbers.Real` 

4151 

4152 .. versionadded:: 0.5.0 

4153 """ 

4154 assertions.assert_real(threshold=threshold) 

4155 if 0 < threshold <= 1.0: 

4156 threshold *= self.quantum_range 

4157 return library.MagickDeskewImage(self.wand, threshold) 

4158 

4159 @manipulative 

4160 @trap_exception 

4161 def despeckle(self): 

4162 """Applies filter to reduce noise in image. 

4163 

4164 .. versionadded:: 0.5.0 

4165 """ 

4166 return library.MagickDespeckleImage(self.wand) 

4167 

4168 @manipulative 

4169 @trap_exception 

4170 def distort(self, method, arguments, best_fit=False): 

4171 """Distorts an image using various distorting methods. 

4172 

4173 .. code:: python 

4174 

4175 from wand.image import Image 

4176 from wand.color import Color 

4177 

4178 with Image(filename='checks.png') as img: 

4179 img.virtual_pixel = 'background' 

4180 img.background_color = Color('green') 

4181 img.matte_color = Color('skyblue') 

4182 arguments = (0, 0, 20, 60, 

4183 90, 0, 70, 63, 

4184 0, 90, 5, 83, 

4185 90, 90, 85, 88) 

4186 img.distort('perspective', arguments) 

4187 img.save(filename='checks_perspective.png') 

4188 

4189 .. image:: ../_images/wand/image/checks.png 

4190 .. image:: ../_images/wand/image/checks_perspective.png 

4191 

4192 Use :attr:`virtual_pixel`, :attr:`background_color`, and 

4193 :attr:`matte_color` properties to control the behavior of pixels 

4194 rendered outside of the image boundaries. 

4195 

4196 Use :attr:`interpolate_method` to control how images scale-up. 

4197 

4198 Distortion viewport, and scale, can be defined by using 

4199 :attr:`Image.artifacts` dictionary. For example:: 

4200 

4201 img.artifacts['distort:viewport'] = '44x44+15+0' 

4202 img.artifacts['distort:scale'] = '10' 

4203 

4204 :param method: Distortion method name from :const:`DISTORTION_METHODS` 

4205 :type method: :class:`basestring` 

4206 :param arguments: List of distorting float arguments 

4207 unique to distortion method 

4208 :type arguments: :class:`collections.abc.Sequence` 

4209 :param best_fit: Attempt to resize resulting image fit distortion. 

4210 Defaults False 

4211 :type best_fit: :class:`bool` 

4212 

4213 .. versionadded:: 0.4.1 

4214 """ 

4215 assertions.string_in_list(DISTORTION_METHODS, 

4216 'wand.image.DISTORTION_METHODS', 

4217 method=method) 

4218 if not isinstance(arguments, abc.Sequence): 

4219 raise TypeError('expected sequence of doubles, not ' + 

4220 repr(arguments)) 

4221 argc = len(arguments) 

4222 argv = (ctypes.c_double * argc)(*arguments) 

4223 method_idx = DISTORTION_METHODS.index(method) 

4224 return library.MagickDistortImage(self.wand, method_idx, 

4225 argc, argv, bool(best_fit)) 

4226 

4227 @manipulative 

4228 @trap_exception 

4229 def edge(self, radius=0.0): 

4230 """Applies convolution filter to detect edges. 

4231 

4232 :param radius: aperture of detection filter. 

4233 :type radius: :class:`numbers.Real` 

4234 

4235 .. versionadded:: 0.5.0 

4236 """ 

4237 assertions.assert_real(radius=radius) 

4238 return library.MagickEdgeImage(self.wand, radius) 

4239 

4240 @manipulative 

4241 @trap_exception 

4242 def emboss(self, radius=0.0, sigma=0.0): 

4243 """Applies convolution filter against Gaussians filter. 

4244 

4245 .. note:: 

4246 

4247 The `radius` value should be larger than `sigma` for best results. 

4248 

4249 :param radius: filter aperture size. 

4250 :type radius: :class:`numbers.Real` 

4251 :param sigma: standard deviation. 

4252 :type sigma: :class:`numbers.Real` 

4253 

4254 .. versionadded:: 0.5.0 

4255 """ 

4256 assertions.assert_real(radius=radius, sigma=sigma) 

4257 return library.MagickEmbossImage(self.wand, radius, sigma) 

4258 

4259 @manipulative 

4260 @trap_exception 

4261 def enhance(self): 

4262 """Applies digital filter to reduce noise. 

4263 

4264 .. versionadded:: 0.5.0 

4265 """ 

4266 return library.MagickEnhanceImage(self.wand) 

4267 

4268 @manipulative 

4269 @trap_exception 

4270 def equalize(self, channel=None): 

4271 """Equalizes the image histogram 

4272 

4273 :param channel: Optional channel. See :const:`CHANNELS`. 

4274 :type channel: :class:`basestring` 

4275 

4276 .. versionadded:: 0.3.10 

4277 

4278 .. versionchanged:: 0.5.5 

4279 Added optional ``channel`` argument. 

4280 """ 

4281 if channel is None: 

4282 r = library.MagickEqualizeImage(self.wand) 

4283 else: 

4284 channel_ch = self._channel_to_mask(channel) 

4285 if MAGICK_VERSION_NUMBER < 0x700: 

4286 r = library.MagickEqualizeImageChannel(self.wand, channel_ch) 

4287 else: # pragma: no cover 

4288 mask = library.MagickSetImageChannelMask(self.wand, channel_ch) 

4289 r = library.MagickEqualizeImage(self.wand) 

4290 library.MagickSetImageChannelMask(self.wand, mask) 

4291 return r 

4292 

4293 @manipulative 

4294 @trap_exception 

4295 def evaluate(self, operator=None, value=0.0, channel=None): 

4296 """Apply arithmetic, relational, or logical expression to an image. 

4297 

4298 Percent values must be calculated against the quantum range of the 

4299 image:: 

4300 

4301 fifty_percent = img.quantum_range * 0.5 

4302 img.evaluate(operator='set', value=fifty_percent) 

4303 

4304 :param operator: Type of operation to calculate 

4305 :type operator: :const:`EVALUATE_OPS` 

4306 :param value: Number to calculate with ``operator`` 

4307 :type value: :class:`numbers.Real` 

4308 :param channel: Optional channel to apply operation on. 

4309 :type channel: :const:`CHANNELS` 

4310 :raises TypeError: When ``value`` is not numeric. 

4311 :raises ValueError: When ``operator``, or ``channel`` are not defined 

4312 in constants. 

4313 

4314 .. versionadded:: 0.4.1 

4315 """ 

4316 assertions.string_in_list(EVALUATE_OPS, 'wand.image.EVALUATE_OPS', 

4317 operator=operator) 

4318 assertions.assert_real(value=value) 

4319 idx_op = EVALUATE_OPS.index(operator) 

4320 if channel is None: 

4321 r = library.MagickEvaluateImage(self.wand, idx_op, value) 

4322 else: 

4323 ch_const = self._channel_to_mask(channel) 

4324 # Use channel method if IM6, else create channel mask for IM7. 

4325 if library.MagickEvaluateImageChannel: 

4326 r = library.MagickEvaluateImageChannel(self.wand, 

4327 ch_const, 

4328 idx_op, 

4329 value) 

4330 else: # pragma: no cover 

4331 # Set active channel, and capture mask to restore. 

4332 channel_mask = library.MagickSetImageChannelMask(self.wand, 

4333 ch_const) 

4334 r = library.MagickEvaluateImage(self.wand, idx_op, value) 

4335 # Restore original state of channels 

4336 library.MagickSetImageChannelMask(self.wand, channel_mask) 

4337 return r 

4338 

4339 def export_pixels(self, x=0, y=0, width=None, height=None, 

4340 channel_map="RGBA", storage='char'): 

4341 """Export pixel data from a raster image to 

4342 a list of values. 

4343 

4344 The ``channel_map`` tells ImageMagick which color 

4345 channels to export, and what order they should be 

4346 written as -- per pixel. Valid entries for 

4347 ``channel_map`` are: 

4348 

4349 - ``'R'`` - Red channel 

4350 - ``'G'`` - Green channel 

4351 - ``'B'`` - Blue channel 

4352 - ``'A'`` - Alpha channel (``0`` is transparent) 

4353 - ``'O'`` - Alpha channel (``0`` is opaque) 

4354 - ``'C'`` - Cyan channel 

4355 - ``'Y'`` - Yellow channel 

4356 - ``'M'`` - Magenta channel 

4357 - ``'K'`` - Black channel 

4358 - ``'I'`` - Intensity channel (only for grayscale) 

4359 - ``'P'`` - Padding 

4360 

4361 See :const:`STORAGE_TYPES` for a list of valid 

4362 ``storage`` options. This tells ImageMagick 

4363 what type of data it should calculate & write to. 

4364 For example; a storage type of ``'char'`` will write 

4365 a 8-bit value between 0 ~ 255, a storage type 

4366 of ``'short'`` will write a 16-bit value between 

4367 0 ~ 65535, and a ``'integer'`` will write a 

4368 32-bit value between 0 ~ 4294967295. 

4369 

4370 .. note:: 

4371 

4372 By default, the entire image will be exported 

4373 as ``'char'`` storage with each pixel mapping 

4374 Red, Green, Blue, & Alpha channels. 

4375 

4376 

4377 :param x: horizontal starting coordinate of raster. 

4378 :type x: :class:`numbers.Integral` 

4379 :param y: vertical starting coordinate of raster. 

4380 :type y: :class:`numbers.Integral` 

4381 :param width: horizontal length of raster. 

4382 :type width: :class:`numbers.Integral` 

4383 :param height: vertical length of raster. 

4384 :type height: :class:`numbers.Integral` 

4385 :param channel_map: a string listing the channel data 

4386 format for each pixel. 

4387 :type channel_map: :class:`basestring` 

4388 :param storage: what data type each value should 

4389 be calculated as. 

4390 :type storage: :class:`basestring` 

4391 :returns: list of values. 

4392 :rtype: :class:`collections.abc.Sequence` 

4393 

4394 .. versionadded:: 0.5.0 

4395 """ 

4396 _w, _h = self.size 

4397 if width is None: 

4398 width = _w 

4399 if height is None: 

4400 height = _h 

4401 assertions.assert_integer(x=x, y=y, width=width, height=height) 

4402 assertions.assert_string(channel_map=channel_map) 

4403 assertions.string_in_list(STORAGE_TYPES, 'wand.image.STORAGE_TYPES', 

4404 storage=storage) 

4405 channel_map = channel_map.upper() 

4406 valid_channels = 'RGBAOCYMKIP' 

4407 for channel in channel_map: 

4408 if channel not in valid_channels: 

4409 raise ValueError('Unknown channel label: ' + 

4410 repr(channel)) 

4411 c_storage_types = [ 

4412 None, 

4413 ctypes.c_ubyte, 

4414 ctypes.c_double, 

4415 ctypes.c_float, 

4416 ctypes.c_uint, 

4417 ctypes.c_ulong, 

4418 ctypes.c_double, # FIXME: Might be c_longdouble? 

4419 ctypes.c_ushort 

4420 ] 

4421 s_index = STORAGE_TYPES.index(storage) 

4422 c_storage = c_storage_types[s_index] 

4423 total_pixels = width * height 

4424 c_buffer_size = total_pixels * len(channel_map) 

4425 c_buffer = (c_buffer_size * c_storage)() 

4426 r = library.MagickExportImagePixels(self.wand, 

4427 x, y, width, height, 

4428 binary(channel_map), 

4429 s_index, 

4430 ctypes.byref(c_buffer)) 

4431 if not r: # pragma: no cover 

4432 self.raise_exception() 

4433 return c_buffer[:c_buffer_size] 

4434 

4435 @manipulative 

4436 @trap_exception 

4437 def extent(self, width=None, height=None, x=0, y=0): 

4438 """extends the image as defined by the geometry, gravity, and wand 

4439 background color. Set the (x,y) offset of the geometry to move the 

4440 original wand relative to the extended wand. 

4441 

4442 :param width: the :attr:`width` of the extended image. 

4443 default is the :attr:`width` of the image. 

4444 :type width: :class:`numbers.Integral` 

4445 :param height: the :attr:`height` of the extended image. 

4446 default is the :attr:`height` of the image. 

4447 :type height: :class:`numbers.Integral` 

4448 :param x: the :attr:`x` offset of the extended image. 

4449 default is 0 

4450 :type x: :class:`numbers.Integral` 

4451 :param y: the :attr:`y` offset of the extended image. 

4452 default is 0 

4453 :type y: :class:`numbers.Integral` 

4454 

4455 .. versionadded:: 0.4.5 

4456 """ 

4457 if width is None or width == 0: 

4458 width = self.width 

4459 if height is None or height == 0: 

4460 height = self.height 

4461 if width < 0: 

4462 raise ValueError('image width cannot be negative integer') 

4463 elif height < 0: 

4464 raise ValueError('image height cannot be negative integer') 

4465 

4466 return library.MagickExtentImage(self.wand, width, height, x, y) 

4467 

4468 def features(self, distance): 

4469 """Calculate directional image features for each color channel. 

4470 Feature metrics including: 

4471 

4472 - angular second moment 

4473 - contrast 

4474 - correlation 

4475 - variance sum of squares 

4476 - inverse difference moment 

4477 - sum average 

4478 - sum variance 

4479 - sum entropy 

4480 - entropy 

4481 - difference variance 

4482 - difference entropy 

4483 - information measures of correlation 1 

4484 - information measures of correlation 2 

4485 - maximum correlation coefficient 

4486 

4487 With each metric containing horizontal, vertical, left & right 

4488 diagonal values. 

4489 

4490 .. code:: 

4491 

4492 from wand.image import Image 

4493 

4494 with Image(filename='rose:') as img: 

4495 channel_features = img.features(distance=32) 

4496 for channels, features in channel_features.items(): 

4497 print(channels) 

4498 for feature, directions in features.items(): 

4499 print(' ', feature) 

4500 for name, value in directions.items(): 

4501 print(' ', name, value) 

4502 

4503 :param distance: Define the distance if pixels to calculate. 

4504 :type distance: :class:`numbers.Integral` 

4505 :returns: a dict mapping each color channel with a dict of each 

4506 feature. 

4507 :rtype: :class:`dict` 

4508 

4509 .. versionadded:: 0.5.5 

4510 """ 

4511 def build_channel(address, channel): 

4512 feature = ChannelFeature() 

4513 size = ctypes.sizeof(feature) 

4514 ctypes.memmove(ctypes.addressof(feature), 

4515 feature_ptr + (CHANNELS[channel] * size), 

4516 size) 

4517 keys = ('horizontal', 'vertical', 

4518 'left_diagonal', 'right_diagonal') 

4519 feature_dict = {} 

4520 for k in feature._fields_: 

4521 a = k[0] 

4522 feature_dict[a] = dict(zip(keys, getattr(feature, a))) 

4523 return feature_dict 

4524 if MAGICK_VERSION_NUMBER < 0x700: 

4525 method = library.MagickGetImageChannelFeatures 

4526 else: # pragma: no cover 

4527 method = library.MagickGetImageFeatures 

4528 assertions.assert_unsigned_integer(distance=distance) 

4529 feature_ptr = method(self.wand, distance) 

4530 response = {} 

4531 if feature_ptr: 

4532 colorspace = self.colorspace 

4533 if self.alpha_channel: 

4534 response['alpha'] = build_channel(feature_ptr, 'alpha') 

4535 if colorspace == 'gray': 

4536 response['gray'] = build_channel(feature_ptr, 'gray') 

4537 elif colorspace == 'cmyk': 

4538 response['cyan'] = build_channel(feature_ptr, 'cyan') 

4539 response['magenta'] = build_channel(feature_ptr, 'magenta') 

4540 response['yellow'] = build_channel(feature_ptr, 'yellow') 

4541 response['black'] = build_channel(feature_ptr, 'black') 

4542 else: 

4543 response['red'] = build_channel(feature_ptr, 'red') 

4544 response['green'] = build_channel(feature_ptr, 'green') 

4545 response['blue'] = build_channel(feature_ptr, 'blue') 

4546 feature_ptr = library.MagickRelinquishMemory(feature_ptr) 

4547 return response 

4548 

4549 def fft(self, magnitude=True): 

4550 """Alias for :meth:`forward_fourier_transform`. 

4551 

4552 .. versionadded:: 0.5.7 

4553 """ 

4554 return self.forward_fourier_transform(magnitude) 

4555 

4556 @manipulative 

4557 @trap_exception 

4558 def flip(self): 

4559 """Creates a vertical mirror image by reflecting the pixels around 

4560 the central x-axis. It manipulates the image in place. 

4561 

4562 .. versionadded:: 0.3.0 

4563 

4564 """ 

4565 return library.MagickFlipImage(self.wand) 

4566 

4567 @manipulative 

4568 @trap_exception 

4569 def flop(self): 

4570 """Creates a horizontal mirror image by reflecting the pixels around 

4571 the central y-axis. It manipulates the image in place. 

4572 

4573 .. versionadded:: 0.3.0 

4574 

4575 """ 

4576 return library.MagickFlopImage(self.wand) 

4577 

4578 @trap_exception 

4579 def forward_fourier_transform(self, magnitude=True): 

4580 """Performs a discrete Fourier transform. The image stack is replaced 

4581 with the results. Either a pair of magnitude & phase images, or 

4582 real & imaginary (HDRI). 

4583 

4584 .. code:: 

4585 

4586 from wand.image import Image 

4587 from wand.version import QUANTUM_RANGE 

4588 

4589 with Image(filename='source.png') as img: 

4590 img.forward_fourier_transform() 

4591 img.depth = QUANTUM_RANGE 

4592 img.save(filename='fft_%02d.png') 

4593 

4594 .. seealso:: :meth:`inverse_fourier_transform` & :meth:`complex` 

4595 

4596 .. note:: 

4597 

4598 ImageMagick must have HDRI support to compute real & imaginary 

4599 components (i.e. ``magnitude=False``). 

4600 

4601 :param magnitude: If ``True``, generate magnitude & phase, else 

4602 real & imaginary. Default ``True`` 

4603 :type magnitude: :class:`bool` 

4604 

4605 .. versionadded:: 0.5.5 

4606 """ 

4607 assertions.assert_bool(magnitude=magnitude) 

4608 return library.MagickForwardFourierTransformImage(self.wand, magnitude) 

4609 

4610 @manipulative 

4611 @trap_exception 

4612 def frame(self, matte=None, width=1, height=1, inner_bevel=0, 

4613 outer_bevel=0, compose='over'): 

4614 """Creates a bordered frame around image. 

4615 Inner & outer bevel can simulate a 3D effect. 

4616 

4617 :param matte: color of the frame 

4618 :type matte: :class:`wand.color.Color` 

4619 :param width: total size of frame on x-axis 

4620 :type width: :class:`numbers.Integral` 

4621 :param height: total size of frame on y-axis 

4622 :type height: :class:`numbers.Integral` 

4623 :param inner_bevel: inset shadow length 

4624 :type inner_bevel: :class:`numbers.Real` 

4625 :param outer_bevel: outset highlight length 

4626 :type outer_bevel: :class:`numbers.Real` 

4627 :param compose: Optional composite operator. Default ``'over'``, and 

4628 only available with ImageMagick-7. 

4629 :type compose: :class:`basestring` 

4630 

4631 .. versionadded:: 0.4.1 

4632 

4633 .. versionchanged:: 0.5.6 

4634 Added optional ``compose`` parameter. 

4635 """ 

4636 if matte is None: 

4637 matte = Color('gray') 

4638 if isinstance(matte, string_type): 

4639 matte = Color(matte) 

4640 assertions.assert_color(matte=matte) 

4641 assertions.assert_integer(width=width, height=height) 

4642 assertions.assert_real(inner_bevel=inner_bevel, 

4643 outer_bevel=outer_bevel) 

4644 with matte: 

4645 if MAGICK_VERSION_NUMBER < 0x700: 

4646 r = library.MagickFrameImage(self.wand, 

4647 matte.resource, 

4648 width, height, 

4649 inner_bevel, outer_bevel) 

4650 else: # pragma: no cover 

4651 assertions.string_in_list(COMPOSITE_OPERATORS, 

4652 'wand.image.COMPOSITE_OPERATORS', 

4653 compose=compose) 

4654 op = COMPOSITE_OPERATORS.index(compose) 

4655 r = library.MagickFrameImage(self.wand, 

4656 matte.resource, 

4657 width, height, 

4658 inner_bevel, outer_bevel, 

4659 op) 

4660 return r 

4661 

4662 @manipulative 

4663 @trap_exception 

4664 def function(self, function, arguments, channel=None): 

4665 """Apply an arithmetic, relational, or logical expression to an image. 

4666 

4667 Defaults entire image, but can isolate affects to single color channel 

4668 by passing :const:`CHANNELS` value to ``channel`` parameter. 

4669 

4670 .. note:: 

4671 

4672 Support for function methods added in the following versions 

4673 of ImageMagick. 

4674 

4675 - ``'polynomial'`` >= 6.4.8-8 

4676 - ``'sinusoid'`` >= 6.4.8-8 

4677 - ``'arcsin'`` >= 6.5.3-1 

4678 - ``'arctan'`` >= 6.5.3-1 

4679 

4680 :param function: a string listed in :const:`FUNCTION_TYPES` 

4681 :type function: :class:`basestring` 

4682 :param arguments: a sequence of doubles to apply against ``function`` 

4683 :type arguments: :class:`collections.abc.Sequence` 

4684 :param channel: optional :const:`CHANNELS`, defaults all 

4685 :type channel: :class:`basestring` 

4686 :raises ValueError: when a ``function``, or ``channel`` is not 

4687 defined in there respected constant 

4688 :raises TypeError: if ``arguments`` is not a sequence 

4689 

4690 .. versionadded:: 0.4.1 

4691 """ 

4692 assertions.string_in_list(FUNCTION_TYPES, 'wand.image.FUNCTION_TYPES', 

4693 function=function) 

4694 if not isinstance(arguments, abc.Sequence): 

4695 raise TypeError('expecting sequence of arguments, not ' + 

4696 repr(arguments)) 

4697 argc = len(arguments) 

4698 argv = (ctypes.c_double * argc)(*arguments) 

4699 index = FUNCTION_TYPES.index(function) 

4700 if channel is None: 

4701 r = library.MagickFunctionImage(self.wand, index, argc, argv) 

4702 else: 

4703 ch_channel = self._channel_to_mask(channel) 

4704 # Use channel method if IM6, else create channel mask for IM7. 

4705 if library.MagickFunctionImageChannel: 

4706 r = library.MagickFunctionImageChannel(self.wand, 

4707 ch_channel, 

4708 index, 

4709 argc, 

4710 argv) 

4711 else: # pragma: no cover 

4712 # Set active channel, and capture mask to restore. 

4713 channel_mask = library.MagickSetImageChannelMask(self.wand, 

4714 ch_channel) 

4715 r = library.MagickFunctionImage(self.wand, index, argc, argv) 

4716 # Restore original state of channels 

4717 library.MagickSetImageChannelMask(self.wand, channel_mask) 

4718 return r 

4719 

4720 @manipulative 

4721 def fx(self, expression, channel=None): 

4722 """Manipulate each pixel of an image by given expression. 

4723 

4724 FX will preserver current wand instance, and return a new instance of 

4725 :class:`Image` containing affected pixels. 

4726 

4727 Defaults entire image, but can isolate affects to single color channel 

4728 by passing :const:`CHANNELS` value to ``channel`` parameter. 

4729 

4730 .. seealso:: The anatomy of FX expressions can be found at 

4731 http://www.imagemagick.org/script/fx.php 

4732 

4733 

4734 :param expression: The entire FX expression to apply 

4735 :type expression: :class:`basestring` 

4736 :param channel: Optional channel to target. 

4737 :type channel: :const:`CHANNELS` 

4738 :returns: A new instance of an image with expression applied 

4739 :rtype: :class:`Image` 

4740 

4741 .. versionadded:: 0.4.1 

4742 """ 

4743 assertions.assert_string(expression=expression) 

4744 c_expression = binary(expression) 

4745 if channel is None: 

4746 new_wand = library.MagickFxImage(self.wand, c_expression) 

4747 else: 

4748 ch_channel = self._channel_to_mask(channel) 

4749 if library.MagickFxImageChannel: 

4750 new_wand = library.MagickFxImageChannel(self.wand, 

4751 ch_channel, 

4752 c_expression) 

4753 else: # pragma: no cover 

4754 # Set active channel, and capture mask to restore. 

4755 channel_mask = library.MagickSetImageChannelMask(self.wand, 

4756 ch_channel) 

4757 new_wand = library.MagickFxImage(self.wand, c_expression) 

4758 # Restore original state of channels 

4759 library.MagickSetImageChannelMask(self.wand, channel_mask) 

4760 if new_wand: 

4761 return Image(image=BaseImage(new_wand)) 

4762 else: # pragma: no cover 

4763 self.raise_exception() 

4764 

4765 @manipulative 

4766 @trap_exception 

4767 def gamma(self, adjustment_value=1.0, channel=None): 

4768 """Gamma correct image. 

4769 

4770 Specific color channels can be correct individual. Typical values 

4771 range between 0.8 and 2.3. 

4772 

4773 :param adjustment_value: value to adjust gamma level. Default `1.0` 

4774 :type adjustment_value: :class:`numbers.Real` 

4775 :param channel: optional channel to apply gamma correction 

4776 :type channel: :class:`basestring` 

4777 :raises TypeError: if ``gamma_point`` is not a :class:`numbers.Real` 

4778 :raises ValueError: if ``channel`` is not in :const:`CHANNELS` 

4779 

4780 .. versionadded:: 0.4.1 

4781 

4782 """ 

4783 assertions.assert_real(adjustment_value=adjustment_value) 

4784 if channel is None: 

4785 r = library.MagickGammaImage(self.wand, adjustment_value) 

4786 else: 

4787 ch_const = self._channel_to_mask(channel) 

4788 if library.MagickGammaImageChannel: 

4789 r = library.MagickGammaImageChannel(self.wand, 

4790 ch_const, 

4791 adjustment_value) 

4792 else: # pragma: no cover 

4793 # Set active channel, and capture mask to restore. 

4794 channel_mask = library.MagickSetImageChannelMask(self.wand, 

4795 ch_const) 

4796 r = library.MagickGammaImage(self.wand, adjustment_value) 

4797 # Restore original state of channels 

4798 library.MagickSetImageChannelMask(self.wand, channel_mask) 

4799 return r 

4800 

4801 @manipulative 

4802 @trap_exception 

4803 def gaussian_blur(self, radius=0.0, sigma=0.0, channel=None): 

4804 """Blurs the image. We convolve the image with a gaussian operator 

4805 of the given ``radius`` and standard deviation (``sigma``). 

4806 For reasonable results, the ``radius`` should be larger 

4807 than ``sigma``. Use a ``radius`` of 0 and :meth:`blur()` selects 

4808 a suitable ``radius`` for you. 

4809 

4810 :param radius: the radius of the, in pixels, 

4811 not counting the center pixel 

4812 :type radius: :class:`numbers.Real` 

4813 :param sigma: the standard deviation of the, in pixels 

4814 :type sigma: :class:`numbers.Real` 

4815 :param channel: Optional color channel to target. See 

4816 :const:`CHANNELS` 

4817 :type channel: :class:`basestring` 

4818 

4819 .. versionadded:: 0.3.3 

4820 

4821 .. versionchanged:: 0.5.5 

4822 Added ``channel`` argument. 

4823 .. versionchanged:: 0.5.7 

4824 Positional arguments ``radius`` & ``sigma`` have been converted 

4825 to keyword arguments. 

4826 """ 

4827 assertions.assert_real(radius=radius, sigma=sigma) 

4828 if channel is None: 

4829 r = library.MagickGaussianBlurImage(self.wand, radius, sigma) 

4830 else: 

4831 channel_ch = self._channel_to_mask(channel) 

4832 if MAGICK_VERSION_NUMBER < 0x700: 

4833 r = library.MagickGaussianBlurImageChannel(self.wand, 

4834 channel_ch, 

4835 radius, 

4836 sigma) 

4837 else: # pragma: no cover 

4838 mask = library.MagickSetImageChannelMask(self.wand, channel_ch) 

4839 r = library.MagickGaussianBlurImage(self.wand, radius, sigma) 

4840 library.MagickSetImageChannelMask(self.wand, mask) 

4841 return r 

4842 

4843 @manipulative 

4844 @trap_exception 

4845 def hald_clut(self, image, channel=None): 

4846 """Replace color values by referencing a Higher And Lower Dimension 

4847 (HALD) Color Look Up Table (CLUT). You can generate a HALD image 

4848 by using ImageMagick's `hald:` protocol. :: 

4849 

4850 with Image(filename='rose:') as img: 

4851 with Image(filename='hald:3') as hald: 

4852 hald.gamma(1.367) 

4853 img.hald_clut(hald) 

4854 

4855 :param image: The HALD color matrix. 

4856 :type image: :class:`wand.image.BaseImage` 

4857 :param channel: Optional color channel to target. See 

4858 :const:`CHANNELS` 

4859 :type channel: :class:`basestring` 

4860 

4861 .. versionadded:: 0.5.0 

4862 

4863 .. versionchanged:: 0.5.5 

4864 Added ``channel`` argument. 

4865 """ 

4866 if not isinstance(image, BaseImage): 

4867 raise TypeError('expecting a base image, not ' + repr(image)) 

4868 if channel is None: 

4869 r = library.MagickHaldClutImage(self.wand, image.wand) 

4870 else: 

4871 channel_ch = self._channel_to_mask(channel) 

4872 if MAGICK_VERSION_NUMBER < 0x700: 

4873 r = library.MagickHaldClutImageChannel(self.wand, channel_ch, 

4874 image.wand) 

4875 else: # pragma: no cover 

4876 mask = library.MagickSetImageChannelMask(self.wand, channel_ch) 

4877 r = library.MagickHaldClutImage(self.wand, image.wand) 

4878 library.MagickSetImageChannelMask(self.wand, mask) 

4879 return r 

4880 

4881 @manipulative 

4882 @trap_exception 

4883 def hough_lines(self, width, height=None, threshold=40): 

4884 """Identify lines within an image. Use :meth:`canny` to reduce image 

4885 to a binary edge before calling this method. 

4886 

4887 .. warning:: 

4888 

4889 This class method is only available with ImageMagick 7.0.8-41, or 

4890 greater. 

4891 

4892 :param width: Local maxima of neighboring pixels. 

4893 :type width: :class:`numbers.Integral` 

4894 :param height: Local maxima of neighboring pixels. 

4895 :type height: :class:`numbers.Integral` 

4896 :param threshold: Line count to limit. Default to 40. 

4897 :type threshold: :class:`numbers.Integral` 

4898 :raises WandLibraryVersionError: If system's version of ImageMagick 

4899 does not support this method. 

4900 

4901 .. versionadded:: 0.5.5 

4902 """ 

4903 if library.MagickHoughLineImage is None: 

4904 msg = 'Method requires ImageMagick version 7.0.8-41 or greater.' 

4905 raise WandLibraryVersionError(msg) 

4906 if height is None: 

4907 height = width 

4908 assertions.assert_unsigned_integer(width=width, height=height, 

4909 threshold=threshold) 

4910 return library.MagickHoughLineImage(self.wand, width, height, 

4911 threshold) 

4912 

4913 def ift(self, phase, magnitude=True): 

4914 """Alias for :meth:`inverse_fourier_transform`. 

4915 

4916 .. versionadded:: 0.5.7 

4917 """ 

4918 return self.inverse_fourier_transform(phase, magnitude) 

4919 

4920 @trap_exception 

4921 def implode(self, amount=0.0, method="undefined"): 

4922 """Creates a "imploding" effect by pulling pixels towards the center 

4923 of the image. 

4924 

4925 :param amount: Normalized degree of effect between `0.0` & `1.0`. 

4926 :type amount: :class:`numbers.Real` 

4927 :param method: Which interpolate method to apply to effected pixels. 

4928 See :const:`PIXEL_INTERPOLATE_METHODS` for a list of 

4929 options. Only available with ImageMagick-7. 

4930 :type method: :class:`basestring` 

4931 

4932 .. versionadded:: 0.5.2 

4933 """ 

4934 assertions.assert_real(amount=amount) 

4935 assertions.string_in_list(PIXEL_INTERPOLATE_METHODS, 

4936 'wand.image.PIXEL_INTERPOLATE_METHODS', 

4937 method=method) 

4938 if MAGICK_VERSION_NUMBER < 0x700: 

4939 r = library.MagickImplodeImage(self.wand, amount) 

4940 else: # pragma: no cover 

4941 method_idx = PIXEL_INTERPOLATE_METHODS.index(method) 

4942 r = library.MagickImplodeImage(self.wand, amount, method_idx) 

4943 return r 

4944 

4945 @trap_exception 

4946 def import_pixels(self, x=0, y=0, width=None, height=None, 

4947 channel_map='RGB', storage='char', data=None): 

4948 """Import pixel data from a byte-string to 

4949 the image. The instance of :class:`Image` must already 

4950 be allocated with the correct size. 

4951 

4952 The ``channel_map`` tells ImageMagick which color 

4953 channels to export, and what order they should be 

4954 written as -- per pixel. Valid entries for 

4955 ``channel_map`` are: 

4956 

4957 - ``'R'`` - Red channel 

4958 - ``'G'`` - Green channel 

4959 - ``'B'`` - Blue channel 

4960 - ``'A'`` - Alpha channel (``0`` is transparent) 

4961 - ``'O'`` - Alpha channel (``0`` is opaque) 

4962 - ``'C'`` - Cyan channel 

4963 - ``'Y'`` - Yellow channel 

4964 - ``'M'`` - Magenta channel 

4965 - ``'K'`` - Black channel 

4966 - ``'I'`` - Intensity channel (only for grayscale) 

4967 - ``'P'`` - Padding 

4968 

4969 See :const:`STORAGE_TYPES` for a list of valid 

4970 ``storage`` options. This tells ImageMagick 

4971 what type of data it should calculate & write to. 

4972 For example; a storage type of ``'char'`` will write 

4973 a 8-bit value between 0 ~ 255, a storage type 

4974 of ``'short'`` will write a 16-bit value between 

4975 0 ~ 65535, and a ``'integer'`` will write a 

4976 32-bit value between 0 ~ 4294967295. 

4977 

4978 .. note:: 

4979 

4980 By default, the entire image will be exported 

4981 as ``'char'`` storage with each pixel mapping 

4982 Red, Green, Blue, & Alpha channels. 

4983 

4984 

4985 :param x: horizontal starting coordinate of raster. 

4986 :type x: :class:`numbers.Integral` 

4987 :param y: vertical starting coordinate of raster. 

4988 :type y: :class:`numbers.Integral` 

4989 :param width: horizontal length of raster. 

4990 :type width: :class:`numbers.Integral` 

4991 :param height: vertical length of raster. 

4992 :type height: :class:`numbers.Integral` 

4993 :param channel_map: a string listing the channel data 

4994 format for each pixel. 

4995 :type channel_map: :class:`basestring` 

4996 :param storage: what data type each value should 

4997 be calculated as. 

4998 :type storage: :class:`basestring` 

4999 

5000 .. versionadded:: 0.5.0 

5001 """ 

5002 _w, _h = self.size 

5003 if width is None: 

5004 width = _w 

5005 if height is None: 

5006 height = _h 

5007 assertions.assert_integer(x=x, y=y, width=width, height=height) 

5008 assertions.string_in_list(STORAGE_TYPES, 'wand.image.STORAGE_TYPES', 

5009 storage=storage) 

5010 assertions.assert_string(channel_map=channel_map) 

5011 channel_map = channel_map.upper() 

5012 valid_channels = 'RGBAOCYMKIP' 

5013 for channel in channel_map: 

5014 if channel not in valid_channels: 

5015 raise ValueError('Unknown channel label: ' + 

5016 repr(channel)) 

5017 if not isinstance(data, abc.Sequence): 

5018 raise TypeError('data must list of values, not' + 

5019 repr(data)) 

5020 # Ensure enough data was given. 

5021 expected_len = width * height * len(channel_map) 

5022 given_len = len(data) 

5023 if expected_len != given_len: 

5024 msg = 'data length should be {0}, not {1}.'.format( 

5025 expected_len, 

5026 given_len 

5027 ) 

5028 raise ValueError(msg) 

5029 c_storage_types = [ 

5030 None, 

5031 ctypes.c_ubyte, 

5032 ctypes.c_double, 

5033 ctypes.c_float, 

5034 ctypes.c_uint, 

5035 ctypes.c_ulong, 

5036 ctypes.c_double, # FIXME: Might be c_longdouble ? 

5037 ctypes.c_ushort 

5038 ] 

5039 s_index = STORAGE_TYPES.index(storage) 

5040 c_type = c_storage_types[s_index] 

5041 c_buffer = (len(data) * c_type)(*data) 

5042 r = library.MagickImportImagePixels(self.wand, 

5043 x, y, width, height, 

5044 binary(channel_map), 

5045 s_index, 

5046 ctypes.byref(c_buffer)) 

5047 return r 

5048 

5049 @trap_exception 

5050 def inverse_fourier_transform(self, phase, magnitude=True): 

5051 """Applies the inverse of a discrete Fourier transform. The image stack 

5052 is replaced with the results. Either a pair of magnitude & phase 

5053 images, or real & imaginary (HDRI). 

5054 

5055 .. code:: 

5056 

5057 from wand.image import Image 

5058 

5059 with Image(filename='magnitude.png') as img: 

5060 with Image(filename='phase.png') as phase: 

5061 img.inverse_fourier_transform(phase) 

5062 img.save(filename='output.png') 

5063 

5064 .. seealso:: :meth:`forward_fourier_transform` & :meth:`complex` 

5065 

5066 .. note:: 

5067 

5068 ImageMagick must have HDRI support to compute real & imaginary 

5069 components (i.e. ``magnitude=False``). 

5070 

5071 :param phase: Second part (image) of the transform. Either the phase, 

5072 or the imaginary part. 

5073 :type phase: :class:`BaseImage` 

5074 :param magnitude: If ``True``, accept magnitude & phase input, else 

5075 real & imaginary. Default ``True`` 

5076 :type magnitude: :class:`bool` 

5077 

5078 .. versionadded:: 0.5.5 

5079 """ 

5080 if not isinstance(phase, BaseImage): 

5081 raise TypeError('phase must be an image, not ' + repr(phase)) 

5082 assertions.assert_bool(magnitude=magnitude) 

5083 return library.MagickInverseFourierTransformImage(self.wand, 

5084 phase.wand, 

5085 magnitude) 

5086 

5087 def kurtosis_channel(self, channel='default_channels'): 

5088 """Calculates the kurtosis and skewness of the image. 

5089 

5090 .. code:: python 

5091 

5092 from wand.image import Image 

5093 

5094 with Image(filename='input.jpg') as img: 

5095 kurtosis, skewness = img.kurtosis_channel() 

5096 

5097 :param channel: Select which color channel to evaluate. See 

5098 :const:`CHANNELS`. Default ``'default_channels'``. 

5099 :type channel: :class:`basestring` 

5100 :returns: Tuple of :attr:`kurtosis` & :attr:`skewness` 

5101 values. 

5102 :rtype: :class:`tuple` 

5103 

5104 .. versionadded:: 0.5.3 

5105 """ 

5106 ch_channel = self._channel_to_mask(channel) 

5107 k = ctypes.c_double(0.0) 

5108 s = ctypes.c_double(0.0) 

5109 if MAGICK_VERSION_NUMBER < 0x700: 

5110 library.MagickGetImageChannelKurtosis(self.wand, ch_channel, 

5111 ctypes.byref(k), 

5112 ctypes.byref(s)) 

5113 else: # pragma: no cover 

5114 # Set active channel, and capture mask to restore. 

5115 channel_mask = library.MagickSetImageChannelMask(self.wand, 

5116 ch_channel) 

5117 library.MagickGetImageKurtosis(self.wand, 

5118 ctypes.byref(k), 

5119 ctypes.byref(s)) 

5120 # Restore original state of channels 

5121 library.MagickSetImageChannelMask(self.wand, channel_mask) 

5122 return k.value, s.value 

5123 

5124 @manipulative 

5125 @trap_exception 

5126 def kuwahara(self, radius=1.0, sigma=None): 

5127 """Edge preserving noise reduction filter. 

5128 

5129 https://en.wikipedia.org/wiki/Kuwahara_filter 

5130 

5131 If ``sigma`` is not given, the value will be calculated as: 

5132 

5133 sigma = radius - 0.5 

5134 

5135 To match original algorithm's behavior, increase ``radius`` value by 

5136 one: 

5137 

5138 myImage.kuwahara(myRadius + 1, mySigma) 

5139 

5140 .. warning:: 

5141 

5142 This class method is only available with ImageMagick 7.0.8-41, or 

5143 greater. 

5144 

5145 :param radius: Size of the filter aperture. 

5146 :type radius: :class:`numbers.Real` 

5147 :param sigma: Standard deviation of Gaussian filter. 

5148 :type sigma: :class:`numbers.Real` 

5149 :raises WandLibraryVersionError: If system's version of ImageMagick 

5150 does not support this method. 

5151 

5152 .. versionadded:: 0.5.5 

5153 """ 

5154 if library.MagickKuwaharaImage is None: 

5155 msg = 'Method requires ImageMagick version 7.0.8-41 or greater.' 

5156 raise WandLibraryVersionError(msg) 

5157 if sigma is None: 

5158 sigma = radius - 0.5 

5159 assertions.assert_real(radius=radius, sigma=sigma) 

5160 return library.MagickKuwaharaImage(self.wand, radius, sigma) 

5161 

5162 @trap_exception 

5163 def level(self, black=0.0, white=None, gamma=1.0, channel=None): 

5164 """Adjusts the levels of an image by scaling the colors falling 

5165 between specified black and white points to the full available 

5166 quantum range. 

5167 

5168 If only ``black`` is given, ``white`` will be adjusted inward. 

5169 

5170 :param black: Black point, as a percentage of the system's quantum 

5171 range. Defaults to 0. 

5172 :type black: :class:`numbers.Real` 

5173 :param white: White point, as a percentage of the system's quantum 

5174 range. Defaults to 1.0. 

5175 :type white: :class:`numbers.Real` 

5176 :param gamma: Optional gamma adjustment. Values > 1.0 lighten the 

5177 image's midtones while values < 1.0 darken them. 

5178 :type gamma: :class:`numbers.Real` 

5179 :param channel: The channel type. Available values can be found 

5180 in the :const:`CHANNELS` mapping. If ``None``, 

5181 normalize all channels. 

5182 :type channel: :const:`CHANNELS` 

5183 

5184 .. note:: 

5185 Images may not be affected if the ``white`` value is equal, or 

5186 less then, the ``black`` value. 

5187 

5188 .. versionadded:: 0.4.1 

5189 

5190 """ 

5191 assertions.assert_real(black=black) 

5192 # If white is not given, mimic CLI behavior by reducing top point 

5193 if white is None: 

5194 white = 1.0 - black 

5195 assertions.assert_real(white=white, gamma=gamma) 

5196 

5197 bp = float(self.quantum_range * black) 

5198 wp = float(self.quantum_range * white) 

5199 if MAGICK_HDRI: # pragma: no cover 

5200 bp -= 0.5 # TODO: Document why HDRI requires 0.5 adjustments. 

5201 wp -= 0.5 

5202 if channel is None: 

5203 r = library.MagickLevelImage(self.wand, bp, gamma, wp) 

5204 else: 

5205 ch_const = self._channel_to_mask(channel) 

5206 if library.MagickLevelImageChannel: 

5207 r = library.MagickLevelImageChannel(self.wand, 

5208 ch_const, 

5209 bp, 

5210 gamma, 

5211 wp) 

5212 else: # pragma: no cover 

5213 # Set active channel, and capture mask to restore. 

5214 channel_mask = library.MagickSetImageChannelMask(self.wand, 

5215 ch_const) 

5216 r = library.MagickLevelImage(self.wand, bp, gamma, wp) 

5217 # Restore original state of channels 

5218 library.MagickSetImageChannelMask(self.wand, channel_mask) 

5219 return r 

5220 

5221 @manipulative 

5222 @trap_exception 

5223 def level_colors(self, black_color, white_color, channel=None): 

5224 """Maps given colors to "black" & "white" values. 

5225 

5226 .. warning:: 

5227 

5228 This class method is only available with ImageMagick 7.0.8-54, or 

5229 greater. 

5230 

5231 :param black_color: linearly map given color as "black" point. 

5232 :type black_color: :class:`Color` 

5233 :param white_color: linearly map given color as "white" point. 

5234 :type white_color: :class:`Color` 

5235 :param channel: target a specific color-channel to levelize. 

5236 :type channel: :class:`basestring` 

5237 :raises WandLibraryVersionError: If system's version of ImageMagick 

5238 does not support this method. 

5239 

5240 .. versionadded:: 0.5.6 

5241 """ 

5242 if library.MagickLevelImageColors is None: 

5243 msg = 'Method requires ImageMagick version 7.0.8-54 or greater.' 

5244 raise WandLibraryVersionError(msg) 

5245 if isinstance(black_color, string_type): 

5246 black_color = Color(black_color) 

5247 if isinstance(white_color, string_type): 

5248 white_color = Color(white_color) 

5249 assertions.assert_color(black_color=black_color, 

5250 white_color=white_color) 

5251 channel_mask = None 

5252 if channel is not None: 

5253 ch_const = self._channel_to_mask(channel) 

5254 channel_mask = library.MagickSetImageChannelMask(self.wand, 

5255 ch_const) 

5256 with black_color: 

5257 with white_color: 

5258 r = library.MagickLevelImageColors(self.wand, 

5259 black_color.resource, 

5260 white_color.resource, 

5261 False) 

5262 if channel is not None: 

5263 library.MagickSetImageChannelMask(self.wand, channel_mask) 

5264 return r 

5265 

5266 @manipulative 

5267 @trap_exception 

5268 def levelize(self, black=0.0, white=None, gamma=1.0, channel=None): 

5269 """Reverse of :meth:`level()`, this method compresses the range of 

5270 colors between ``black`` & ``white`` values. 

5271 

5272 If only ``black`` is given, ``white`` will be adjusted inward. 

5273 

5274 .. warning:: 

5275 

5276 This class method is only available with ImageMagick 7.0.8-41, or 

5277 greater. 

5278 

5279 :param black: Black point, as a percentage of the system's quantum 

5280 range. Defaults to 0. 

5281 :type black: :class:`numbers.Real` 

5282 :param white: White point, as a percentage of the system's quantum 

5283 range. Defaults to 1.0. 

5284 :type white: :class:`numbers.Real` 

5285 :param gamma: Optional gamma adjustment. Values > 1.0 lighten the 

5286 image's midtones while values < 1.0 darken them. 

5287 :type gamma: :class:`numbers.Real` 

5288 :param channel: The channel type. Available values can be found 

5289 in the :const:`CHANNELS` mapping. If ``None``, 

5290 normalize all channels. 

5291 :type channel: :const:`CHANNELS` 

5292 :raises WandLibraryVersionError: If system's version of ImageMagick 

5293 does not support this method. 

5294 

5295 .. versionadded:: 0.5.5 

5296 """ 

5297 if library.MagickLevelizeImage is None: 

5298 msg = 'Method requires ImageMagick version 7.0.8-41 or greater.' 

5299 raise WandLibraryVersionError(msg) 

5300 if white is None: 

5301 white = float(self.quantum_range) 

5302 assertions.assert_real(black=black, white=white, gamma=gamma) 

5303 if 0 < black <= 1.0: 

5304 black *= self.quantum_range 

5305 if 0 < white <= 1.0: 

5306 white *= self.quantum_range 

5307 if channel is None: 

5308 r = library.MagickLevelizeImage(self.wand, black, gamma, white) 

5309 else: 

5310 ch_const = self._channel_to_mask(channel) 

5311 channel_mask = library.MagickSetImageChannelMask(self.wand, 

5312 ch_const) 

5313 r = library.MagickLevelizeImage(self.wand, black, gamma, white) 

5314 library.MagickSetImageChannelMask(self.wand, channel_mask) 

5315 return r 

5316 

5317 @manipulative 

5318 @trap_exception 

5319 def levelize_colors(self, black_color, white_color, channel=None): 

5320 """Reverse of :meth:`level_colors()`, and creates a de-contrasting 

5321 gradient of given colors. This works best with grayscale images. 

5322 

5323 .. warning:: 

5324 

5325 This class method is only available with ImageMagick 7.0.8-54, or 

5326 greater. 

5327 

5328 :param black_color: tint map given color as "black" point. 

5329 :type black_color: :class:`Color` 

5330 :param white_color: tint map given color as "white" point. 

5331 :type white_color: :class:`Color` 

5332 :param channel: target a specific color-channel to levelize. 

5333 :type channel: :class:`basestring` 

5334 :raises WandLibraryVersionError: If system's version of ImageMagick 

5335 does not support this method. 

5336 

5337 .. versionadded:: 0.5.6 

5338 """ 

5339 if library.MagickLevelImageColors is None: 

5340 msg = 'Method requires ImageMagick version 7.0.8-54 or greater.' 

5341 raise WandLibraryVersionError(msg) 

5342 if isinstance(black_color, string_type): 

5343 black_color = Color(black_color) 

5344 if isinstance(white_color, string_type): 

5345 white_color = Color(white_color) 

5346 assertions.assert_color(black_color=black_color, 

5347 white_color=white_color) 

5348 channel_mask = None 

5349 ch_const = None 

5350 if channel is not None: 

5351 ch_const = self._channel_to_mask(channel) 

5352 channel_mask = library.MagickSetImageChannelMask(self.wand, 

5353 ch_const) 

5354 with black_color: 

5355 with white_color: 

5356 r = library.MagickLevelImageColors(self.wand, 

5357 black_color.resource, 

5358 white_color.resource, 

5359 True) 

5360 if channel is not None: 

5361 library.MagickSetImageChannelMask(self.wand, channel_mask) 

5362 return r 

5363 

5364 @manipulative 

5365 @trap_exception 

5366 def linear_stretch(self, black_point=0.0, white_point=1.0): 

5367 """Enhance saturation intensity of an image. 

5368 

5369 :param black_point: Black point between 0.0 and 1.0. Default 0.0 

5370 :type black_point: :class:`numbers.Real` 

5371 :param white_point: White point between 0.0 and 1.0. Default 1.0 

5372 :type white_point: :class:`numbers.Real` 

5373 

5374 .. versionadded:: 0.4.1 

5375 """ 

5376 assertions.assert_real(black_point=black_point, 

5377 white_point=white_point) 

5378 linear_range = float(self.width * self.height) 

5379 return library.MagickLinearStretchImage(self.wand, 

5380 linear_range * black_point, 

5381 linear_range * white_point) 

5382 

5383 @manipulative 

5384 def liquid_rescale(self, width, height, delta_x=0, rigidity=0): 

5385 """Rescales the image with `seam carving`_, also known as 

5386 image retargeting, content-aware resizing, or liquid rescaling. 

5387 

5388 :param width: the width in the scaled image 

5389 :type width: :class:`numbers.Integral` 

5390 :param height: the height in the scaled image 

5391 :type height: :class:`numbers.Integral` 

5392 :param delta_x: maximum seam transversal step. 

5393 0 means straight seams. default is 0 

5394 :type delta_x: :class:`numbers.Real` 

5395 :param rigidity: introduce a bias for non-straight seams. 

5396 default is 0 

5397 :type rigidity: :class:`numbers.Real` 

5398 :raises wand.exceptions.MissingDelegateError: 

5399 when ImageMagick isn't configured ``--with-lqr`` option. 

5400 

5401 .. note:: 

5402 

5403 This feature requires ImageMagick to be configured 

5404 ``--with-lqr`` option. Or it will raise 

5405 :exc:`~wand.exceptions.MissingDelegateError`: 

5406 

5407 .. seealso:: 

5408 

5409 `Seam carving`_ --- Wikipedia 

5410 The article which explains what seam carving is 

5411 on Wikipedia. 

5412 

5413 .. _Seam carving: http://en.wikipedia.org/wiki/Seam_carving 

5414 

5415 """ 

5416 assertions.assert_integer(width=width, height=height) 

5417 assertions.assert_real(delta_x=delta_x, rigidity=rigidity) 

5418 library.MagickLiquidRescaleImage(self.wand, width, height, 

5419 delta_x, rigidity) 

5420 try: 

5421 self.raise_exception() 

5422 except MissingDelegateError as e: # pragma: no cover 

5423 raise MissingDelegateError( 

5424 str(e) + '\n\nImageMagick in the system is likely to be ' 

5425 'impossible to load liblqr. You might not install liblqr, ' 

5426 'or ImageMagick may not compiled with liblqr.' 

5427 ) 

5428 

5429 @manipulative 

5430 @trap_exception 

5431 def local_contrast(self, radius=10, strength=12.5): 

5432 """Increase light-dark transitions within image. 

5433 

5434 .. warning:: 

5435 

5436 This class method is only available with ImageMagick 6.9.3, or 

5437 greater. 

5438 

5439 :param radius: The size of the Gaussian operator. Default value is 

5440 ``10.0``. 

5441 :type radius: :class:`numbers.Real` 

5442 :param strength: Percentage of blur mask to apply. Values can be 

5443 between ``0.0`` and ``100`` with a default of 

5444 ``12.5``. 

5445 :type strength: :class:`numbers.Real` 

5446 

5447 .. versionadded:: 0.5.7 

5448 """ 

5449 if library.MagickLocalContrastImage is None: # pragma: no cover 

5450 msg = 'Method requires ImageMagick version 6.9.3 or greater.' 

5451 raise WandLibraryVersionError(msg) 

5452 assertions.assert_real(radius=radius, strength=strength) 

5453 return library.MagickLocalContrastImage(self.wand, radius, strength) 

5454 

5455 @manipulative 

5456 @trap_exception 

5457 def magnify(self): 

5458 """Quickly double an image in size. This is a convenience method. 

5459 Use :meth:`resize()`, :meth:`resample()`, or :meth:`sample()` for 

5460 more control. 

5461 

5462 .. versionadded:: 0.5.5 

5463 """ 

5464 return library.MagickMagnifyImage(self.wand) 

5465 

5466 def mean_channel(self, channel='default_channels'): 

5467 """Calculates the mean and standard deviation of the image. 

5468 

5469 .. code:: python 

5470 

5471 from wand.image import Image 

5472 

5473 with Image(filename='input.jpg') as img: 

5474 mean, stddev = img.mean_channel() 

5475 

5476 :param channel: Select which color channel to evaluate. See 

5477 :const:`CHANNELS`. Default ``'default_channels'``. 

5478 :type channel: :class:`basestring` 

5479 :returns: Tuple of :attr:`mean` & :attr:`standard_deviation` 

5480 values. The ``mean`` value will be between 0.0 & 

5481 :attr:`quantum_range` 

5482 :rtype: :class:`tuple` 

5483 

5484 .. versionadded:: 0.5.3 

5485 """ 

5486 ch_channel = self._channel_to_mask(channel) 

5487 m = ctypes.c_double(0.0) 

5488 s = ctypes.c_double(0.0) 

5489 if MAGICK_VERSION_NUMBER < 0x700: 

5490 library.MagickGetImageChannelMean(self.wand, ch_channel, 

5491 ctypes.byref(m), 

5492 ctypes.byref(s)) 

5493 else: # pragma: no cover 

5494 # Set active channel, and capture mask to restore. 

5495 channel_mask = library.MagickSetImageChannelMask(self.wand, 

5496 ch_channel) 

5497 library.MagickGetImageMean(self.wand, 

5498 ctypes.byref(m), 

5499 ctypes.byref(s)) 

5500 # Restore original state of channels 

5501 library.MagickSetImageChannelMask(self.wand, channel_mask) 

5502 return m.value, s.value 

5503 

5504 @manipulative 

5505 @trap_exception 

5506 def mean_shift(self, width, height, color_distance=0.1): 

5507 """Recalculates pixel value by comparing neighboring pixels within a 

5508 color distance, and replacing with a mean value. Works best with 

5509 Gray, YCbCr, YIQ, or YUV colorspaces. 

5510 

5511 .. warning:: 

5512 

5513 This class method is only available with ImageMagick 7.0.8-41, or 

5514 greater. 

5515 

5516 :param width: Size of the neighborhood window in pixels. 

5517 :type width: :class:`numbers.Integral` 

5518 :param height: Size of the neighborhood window in pixels. 

5519 :type height: :class:`numbers.Integral` 

5520 :param color_distance: Include pixel values within this color distance. 

5521 :type color_distance: :class:`numbers.Real` 

5522 :raises WandLibraryVersionError: If system's version of ImageMagick 

5523 does not support this method. 

5524 

5525 .. versionadded:: 0.5.5 

5526 """ 

5527 if library.MagickMeanShiftImage is None: 

5528 msg = 'Method requires ImageMagick version 7.0.8-41 or greater.' 

5529 raise WandLibraryVersionError(msg) 

5530 assertions.assert_counting_number(width=width, height=height) 

5531 assertions.assert_real(color_distance=color_distance) 

5532 if 0 < color_distance <= 1.0: 

5533 color_distance *= self.quantum_range 

5534 return library.MagickMeanShiftImage(self.wand, width, height, 

5535 color_distance) 

5536 

5537 @manipulative 

5538 @trap_exception 

5539 def merge_layers(self, method): 

5540 """Composes all the image layers from the current given image onward 

5541 to produce a single image of the merged layers. 

5542 

5543 The initial canvas's size depends on the given ImageLayerMethod, and is 

5544 initialized using the first images background color. The images 

5545 are then composited onto that image in sequence using the given 

5546 composition that has been assigned to each individual image. 

5547 The method must be set with a value from :const:`IMAGE_LAYER_METHOD` 

5548 that is acceptable to this operation. (See ImageMagick documentation 

5549 for more details.) 

5550 

5551 :param method: the method of selecting the size of the initial canvas. 

5552 :type method: :class:`basestring` 

5553 

5554 .. versionadded:: 0.4.3 

5555 

5556 """ 

5557 assertions.assert_string(method=method) 

5558 if method not in ('merge', 'flatten', 'mosaic', 'trimbounds'): 

5559 raise ValueError('method can only be \'merge\', \'flatten\', ' 

5560 '\'mosaic\', or \'trimbounds\'') 

5561 m = IMAGE_LAYER_METHOD.index(method) 

5562 r = library.MagickMergeImageLayers(self.wand, m) 

5563 if r: 

5564 self.wand = r 

5565 self.reset_sequence() 

5566 return bool(r) 

5567 

5568 @manipulative 

5569 def mode(self, width, height=None): 

5570 """Replace each pixel with the mathematical mode of the neighboring 

5571 colors. This is an alias of the :meth:`statistic` method. 

5572 

5573 :param width: Number of neighboring pixels to include in mode. 

5574 :type width: :class:`numbers.Integral` 

5575 :param height: Optional height of neighboring pixels, defaults to the 

5576 same value as ``width``. 

5577 :type height: :class:`numbers.Integral` 

5578 

5579 .. versionadded:: 0.5.4 

5580 """ 

5581 if height is None: 

5582 height = width 

5583 self.statistic('mode', width, height) 

5584 

5585 @manipulative 

5586 @trap_exception 

5587 def modulate(self, brightness=100.0, saturation=100.0, hue=100.0): 

5588 """Changes the brightness, saturation and hue of an image. 

5589 We modulate the image with the given ``brightness``, ``saturation`` 

5590 and ``hue``. 

5591 

5592 :param brightness: percentage of brightness 

5593 :type brightness: :class:`numbers.Real` 

5594 :param saturation: percentage of saturation 

5595 :type saturation: :class:`numbers.Real` 

5596 :param hue: percentage of hue rotation 

5597 :type hue: :class:`numbers.Real` 

5598 :raises ValueError: when one or more arguments are invalid 

5599 

5600 .. versionadded:: 0.3.4 

5601 

5602 """ 

5603 assertions.assert_real(brightness=brightness, saturation=saturation, 

5604 hue=hue) 

5605 return library.MagickModulateImage( 

5606 self.wand, 

5607 brightness, 

5608 saturation, 

5609 hue 

5610 ) 

5611 

5612 @manipulative 

5613 @trap_exception 

5614 def morphology(self, method=None, kernel=None, iterations=1, channel=None): 

5615 """Manipulate pixels based on the shape of neighboring pixels. 

5616 

5617 The ``method`` determines what type of effect to apply to matching 

5618 ``kernel`` shapes. Common methods can be add/remove, 

5619 or lighten/darken pixel values. 

5620 

5621 The ``kernel`` describes the shape of the matching neighbors. Common 

5622 shapes are provided as "built-in" kernels. See 

5623 :const`KERNEL_INFO_TYPES` for examples. The format for built-in kernels 

5624 is: 

5625 

5626 .. sourcecode:: text 

5627 

5628 label:geometry 

5629 

5630 Where `label` is the kernel name defined in :const:`KERNEL_INFO_TYPES`, 

5631 and `:geometry` is an optional geometry size. For example:: 

5632 

5633 with Image(filename='rose:') as img: 

5634 img.morphology(method='dilate', kernel='octagon:3x3') 

5635 # or simply 

5636 img.morphology(method='edgein', kernel='octagon') 

5637 

5638 Custom kernels can be applied by following a similar format: 

5639 

5640 .. sourcecode:: text 

5641 

5642 geometry:args 

5643 

5644 Where `geometry` is the size of the custom kernel, and `args` 

5645 list a comma separated list of values. For example:: 

5646 

5647 custom_kernel='5x3:nan,1,1,1,nan 1,1,1,1,1 nan,1,1,1,nan' 

5648 with Image(filename='rose:') as img: 

5649 img.morphology(method='dilate', kernel=custom_kernel) 

5650 

5651 :param method: effect function to apply. See 

5652 :const:`MORPHOLOGY_METHODS` for a list of 

5653 methods. 

5654 :type method: :class:`basestring` 

5655 :param kernel: shape to evaluate surrounding pixels. See 

5656 :const:`KERNEL_INFO_TYPES` for a list of 

5657 built-in shapes. 

5658 :type kernel: :class:`basestring` 

5659 :param iterations: Number of times a morphology method should be 

5660 applied to the image. Default ``1``. Use ``-1`` for 

5661 unlimited iterations until the image is unchanged 

5662 by the method operator. 

5663 :type iterations: :class:`numbers.Integral` 

5664 :param channel: Optional color channel to target. See 

5665 :const:`CHANNELS` 

5666 :type channel: `basestring` 

5667 

5668 .. versionadded:: 0.5.0 

5669 

5670 .. versionchanged:: 0.5.5 

5671 Added ``channel`` argument. 

5672 """ 

5673 assertions.assert_string(method=method, kernel=kernel) 

5674 assertions.assert_integer(iterations=iterations) 

5675 buitin = None 

5676 geometry = '' 

5677 parts = kernel.split(':') 

5678 if parts[0] in KERNEL_INFO_TYPES: 

5679 buitin = parts[0] 

5680 if len(parts) == 2: 

5681 geometry = parts[1] 

5682 exception_info = libmagick.AcquireExceptionInfo() 

5683 if buitin: 

5684 kernel_idx = KERNEL_INFO_TYPES.index(buitin) 

5685 geometry_info = GeometryInfo() 

5686 flags = libmagick.ParseGeometry(binary(geometry), 

5687 ctypes.byref(geometry_info)) 

5688 if buitin in ('unity',): 

5689 if (flags & 0x0004) == 0: 

5690 geometry_info.rho = 1.0 

5691 elif buitin in ('square', 'diamond', 'octagon', 'disk', 

5692 'plus', 'cross'): 

5693 if (flags & 0x0008) == 0: 

5694 geometry_info.sigma = 1.0 

5695 elif buitin in ('ring',): 

5696 if (flags & 0x0001) == 0: 

5697 geometry_info.xi = 1.0 

5698 elif buitin in ('rectangle',): 

5699 if (flags & 0x0004) == 0: 

5700 geometry_info.rho = geometry_info.sigma 

5701 if geometry_info.rho < 1.0: 

5702 geometry_info.rho = 3.0 

5703 if geometry_info.sigma < 1.0: 

5704 geometry_info.sigma = geometry_info.rho 

5705 if (flags & 0x0001) == 0: 

5706 geometry_info.xi = (geometry_info.rho - 1.0) / 2.0 

5707 if (flags & 0x0002) == 0: 

5708 geometry_info.psi = (geometry_info.sigma - 1.0) / 2.0 

5709 elif buitin in ('chebyshev', 'manhattan', 'octagonal', 

5710 'euclidean'): 

5711 if (flags & 0x0008) == 0: 

5712 geometry_info.sigma = 100.0 

5713 elif (flags & 0x2000) != 0: 

5714 geometry_info.sigma = (float(self.quantum_range) / 

5715 (geometry_info.sigma + 1.0)) 

5716 elif (flags & 0x1000) != 0: 

5717 geometry_info.sigma *= float(self.quantum_range) / 100.0 

5718 if MAGICK_VERSION_NUMBER < 0x700: 

5719 kernel_info = libmagick.AcquireKernelBuiltIn( 

5720 kernel_idx, 

5721 ctypes.byref(geometry_info) 

5722 ) 

5723 else: # pragma: no cover 

5724 kernel_info = libmagick.AcquireKernelBuiltIn( 

5725 kernel_idx, 

5726 ctypes.byref(geometry_info), 

5727 exception_info 

5728 ) 

5729 elif kernel: 

5730 if MAGICK_VERSION_NUMBER < 0x700: 

5731 kernel_info = libmagick.AcquireKernelInfo( 

5732 binary(kernel) 

5733 ) 

5734 else: # pragma: no cover 

5735 kernel_info = libmagick.AcquireKernelInfo( 

5736 binary(kernel), 

5737 exception_info 

5738 ) 

5739 r = None 

5740 exception_info = libmagick.DestroyExceptionInfo(exception_info) 

5741 if kernel_info: 

5742 method_idx = MORPHOLOGY_METHODS.index(method) 

5743 if channel is None: 

5744 r = library.MagickMorphologyImage(self.wand, method_idx, 

5745 iterations, kernel_info) 

5746 else: 

5747 channel_ch = self._channel_to_mask(channel) 

5748 if MAGICK_VERSION_NUMBER < 0x700: 

5749 r = library.MagickMorphologyImageChannel(self.wand, 

5750 channel_ch, 

5751 method_idx, 

5752 iterations, 

5753 kernel_info) 

5754 else: # pragma: no cover 

5755 mask = library.MagickSetImageChannelMask(self.wand, 

5756 channel_ch) 

5757 r = library.MagickMorphologyImage(self.wand, method_idx, 

5758 iterations, kernel_info) 

5759 library.MagickSetImageChannelMask(self.wand, mask) 

5760 kernel_info = libmagick.DestroyKernelInfo(kernel_info) 

5761 else: 

5762 raise ValueError('Unable to parse kernel info for ' + 

5763 repr(kernel)) 

5764 return r 

5765 

5766 @manipulative 

5767 @trap_exception 

5768 def motion_blur(self, radius=0.0, sigma=0.0, angle=0.0, channel=None): 

5769 """Apply a Gaussian blur along an ``angle`` direction. This 

5770 simulates motion movement. 

5771 

5772 :param radius: Aperture size of the Gaussian operator. 

5773 :type radius: :class:`numbers.Real` 

5774 :param sigma: Standard deviation of the Gaussian operator. 

5775 :type sigma: :class:`numbers.Real` 

5776 :param angle: Apply the effect along this angle. 

5777 :type angle: :class:`numbers.Real` 

5778 

5779 .. versionadded:: 0.5.4 

5780 """ 

5781 assertions.assert_real(radius=radius, sigma=sigma, angle=angle) 

5782 if channel is None: 

5783 r = library.MagickMotionBlurImage(self.wand, radius, sigma, angle) 

5784 else: 

5785 ch_const = self._channel_to_mask(channel) 

5786 if MAGICK_VERSION_NUMBER < 0x700: 

5787 r = library.MagickMotionBlurImageChannel(self.wand, 

5788 ch_const, 

5789 radius, 

5790 sigma, 

5791 angle) 

5792 else: # pragma: no cover 

5793 # Set active channel, and capture mask to restore. 

5794 channel_mask = library.MagickSetImageChannelMask(self.wand, 

5795 ch_const) 

5796 r = library.MagickMotionBlurImage(self.wand, radius, sigma, 

5797 angle) 

5798 # Restore original state of channels 

5799 library.MagickSetImageChannelMask(self.wand, channel_mask) 

5800 return r 

5801 

5802 @manipulative 

5803 @trap_exception 

5804 def negate(self, grayscale=False, channel=None): 

5805 """Negate the colors in the reference image. 

5806 

5807 :param grayscale: if set, only negate grayscale pixels in the image. 

5808 :type grayscale: :class:`bool` 

5809 :param channel: the channel type. available values can be found 

5810 in the :const:`CHANNELS` mapping. If ``None``, 

5811 negate all channels. 

5812 :type channel: :class:`basestring` 

5813 

5814 .. versionadded:: 0.3.8 

5815 

5816 """ 

5817 if channel is None: 

5818 r = library.MagickNegateImage(self.wand, grayscale) 

5819 else: 

5820 ch_const = self._channel_to_mask(channel) 

5821 if library.MagickNegateImageChannel: 

5822 r = library.MagickNegateImageChannel(self.wand, ch_const, 

5823 grayscale) 

5824 else: # pragma: no cover 

5825 # Set active channel, and capture mask to restore. 

5826 channel_mask = library.MagickSetImageChannelMask(self.wand, 

5827 ch_const) 

5828 r = library.MagickNegateImage(self.wand, grayscale) 

5829 # Restore original state of channels 

5830 library.MagickSetImageChannelMask(self.wand, channel_mask) 

5831 return r 

5832 

5833 @manipulative 

5834 @trap_exception 

5835 def noise(self, noise_type='uniform', attenuate=1.0, channel=None): 

5836 """Adds noise to image. 

5837 

5838 :param noise_type: type of noise to apply. See :const:`NOISE_TYPES`. 

5839 :type noise_type: :class:`basestring` 

5840 :param attenuate: rate of distribution. Only available in 

5841 ImageMagick-7. Default is ``1.0``. 

5842 :type attenuate: :class:`numbers.Real` 

5843 :param channel: Optionally target a color channel to apply noise to. 

5844 See :const:`CHANNELS`. 

5845 :type channel: :class:`basestring` 

5846 

5847 .. versionadded:: 0.5.3 

5848 

5849 .. versionchanged:: 0.5.5 

5850 Added optional ``channel`` argument. 

5851 """ 

5852 assertions.string_in_list(NOISE_TYPES, 'wand.image.NOISE_TYPES', 

5853 noise_type=noise_type) 

5854 assertions.assert_real(attenuate=attenuate) 

5855 noise_type_idx = NOISE_TYPES.index(noise_type) 

5856 if MAGICK_VERSION_NUMBER < 0x700: 

5857 if channel is None: 

5858 r = library.MagickAddNoiseImage(self.wand, noise_type_idx) 

5859 else: 

5860 channel_ch = self._channel_to_mask(channel) 

5861 r = library.MagickAddNoiseImageChannel(self.wand, 

5862 channel_ch, 

5863 noise_type_idx) 

5864 else: # pragma: no cover 

5865 if channel is None: 

5866 r = library.MagickAddNoiseImage(self.wand, noise_type_idx, 

5867 attenuate) 

5868 else: 

5869 channel_ch = self._channel_to_mask(channel) 

5870 mask = library.MagickSetImageChannelMask(self.wand, 

5871 channel_ch) 

5872 r = library.MagickAddNoiseImage(self.wand, noise_type_idx, 

5873 attenuate) 

5874 library.MagickSetImageChannelMask(self.wand, mask) 

5875 return r 

5876 

5877 @manipulative 

5878 @trap_exception 

5879 def normalize(self, channel=None): 

5880 """Normalize color channels. 

5881 

5882 :param channel: the channel type. available values can be found 

5883 in the :const:`CHANNELS` mapping. If ``None``, 

5884 normalize all channels. 

5885 :type channel: :class:`basestring` 

5886 

5887 """ 

5888 if channel is None: 

5889 r = library.MagickNormalizeImage(self.wand) 

5890 else: 

5891 ch_const = self._channel_to_mask(channel) 

5892 if library.MagickNormalizeImageChannel: 

5893 r = library.MagickNormalizeImageChannel(self.wand, ch_const) 

5894 else: # pragma: no cover 

5895 with Image(image=self) as mask: 

5896 # Set active channel, and capture mask to restore. 

5897 channel_mask = library.MagickSetImageChannelMask(mask.wand, 

5898 ch_const) 

5899 r = library.MagickNormalizeImage(mask.wand) 

5900 # Restore original state of channels. 

5901 library.MagickSetImageChannelMask(mask.wand, 

5902 channel_mask) 

5903 # Copy adjusted mask over original value. 

5904 copy_mask = COMPOSITE_OPERATORS.index('copy_' + channel) 

5905 library.MagickCompositeImage(self.wand, 

5906 mask.wand, 

5907 copy_mask, 

5908 False, 

5909 0, 

5910 0) 

5911 return r 

5912 

5913 @manipulative 

5914 @trap_exception 

5915 def oil_paint(self, radius=0.0, sigma=0.0): 

5916 """Simulates an oil painting by replace each pixel with most frequent 

5917 surrounding color. 

5918 

5919 :param radius: The size of the surrounding neighbors. 

5920 :type radius: :class:`numbers.Real` 

5921 :param sigma: The standard deviation used by the Gaussian operator. 

5922 This is only available with ImageMagick-7. 

5923 :type sigma: :class:`numbers.Real` 

5924 

5925 .. versionadded:: 0.5.4 

5926 """ 

5927 assertions.assert_real(radius=radius, sigma=sigma) 

5928 if MAGICK_VERSION_NUMBER < 0x700: 

5929 r = library.MagickOilPaintImage(self.wand, radius) 

5930 else: # pragma: no cover 

5931 r = library.MagickOilPaintImage(self.wand, radius, sigma) 

5932 return r 

5933 

5934 @manipulative 

5935 @trap_exception 

5936 def opaque_paint(self, target=None, fill=None, fuzz=0.0, invert=False, 

5937 channel=None): 

5938 """Replace any color that matches ``target`` with ``fill``. Use 

5939 ``fuzz`` to control the threshold of the target match. 

5940 The ``invert`` will replace all colors *but* the pixels matching 

5941 the ``target`` color. 

5942 

5943 :param target: The color to match. 

5944 :type target: :class:`wand.color.Color` 

5945 :param fill: The color to paint with. 

5946 :type fill: :class:`wand.color.Color` 

5947 :param fuzz: Normalized real number between `0.0` and 

5948 :attr:`quantum_range`. Default is `0.0`. 

5949 :type fuzz: class:`numbers.Real` 

5950 :param invert: Replace all colors that do not match target. 

5951 Default is ``False``. 

5952 :type invert: :class:`bool` 

5953 :param channel: Optional color channel to target. See 

5954 :const:`CHANNELS` 

5955 :type channel: :class:`basestring` 

5956 

5957 .. versionadded:: 0.5.4 

5958 

5959 .. versionchanged:: 0.5.5 

5960 Added ``channel`` paramater. 

5961 """ 

5962 if isinstance(target, string_type): 

5963 target = Color(target) 

5964 if isinstance(fill, string_type): 

5965 fill = Color(fill) 

5966 assertions.assert_color(target=target, fill=fill) 

5967 assertions.assert_real(fuzz=fuzz) 

5968 assertions.assert_bool(invert=invert) 

5969 with target: 

5970 with fill: 

5971 if channel is None: 

5972 r = library.MagickOpaquePaintImage(self.wand, 

5973 target.resource, 

5974 fill.resource, 

5975 fuzz, 

5976 invert) 

5977 else: 

5978 channel_ch = self._channel_to_mask(channel) 

5979 if MAGICK_VERSION_NUMBER < 0x700: 

5980 r = library.MagickOpaquePaintImageChannel( 

5981 self.wand, channel_ch, target.resource, 

5982 fill.resource, fuzz, invert 

5983 ) 

5984 else: # pragma: no cover 

5985 mask = library.MagickSetImageChannelMask(self.wand, 

5986 channel_ch) 

5987 r = library.MagickOpaquePaintImage(self.wand, 

5988 target.resource, 

5989 fill.resource, 

5990 fuzz, 

5991 invert) 

5992 library.MagickSetImageChannelMask(self.wand, mask) 

5993 return r 

5994 

5995 @manipulative 

5996 @trap_exception 

5997 def optimize_layers(self): 

5998 """Attempts to crop each frame to the smallest image without altering 

5999 the animation. For best results, call 

6000 :meth:`Image.coalesce() <wand.image.BaseImage.coalesce>` before 

6001 manipulating any frames. For timing accuracy, any 

6002 :attr:`SingleImage.delay <wand.sequence.SingleImage.delay>` overwrites 

6003 must be applied after optimizing layers. 

6004 

6005 .. note:: 

6006 

6007 This will only affect ``GIF`` image formates. 

6008 

6009 .. versionadded:: 0.5.0 

6010 """ 

6011 r = library.MagickOptimizeImageLayers(self.wand) 

6012 if r: 

6013 self.wand = r 

6014 self.reset_sequence() 

6015 return bool(r) 

6016 

6017 @manipulative 

6018 @trap_exception 

6019 def optimize_transparency(self): 

6020 """Iterates over frames, and sets transparent values for each 

6021 pixel unchanged by previous frame. 

6022 

6023 .. note:: 

6024 

6025 This will only affect ``GIF`` image formates. 

6026 

6027 .. versionadded:: 0.5.0 

6028 """ 

6029 if library.MagickOptimizeImageTransparency: 

6030 return library.MagickOptimizeImageTransparency(self.wand) 

6031 else: # pragma: no cover 

6032 raise AttributeError('`MagickOptimizeImageTransparency\' not ' 

6033 'available on current version of MagickWand ' 

6034 'library.') 

6035 

6036 @manipulative 

6037 @trap_exception 

6038 def ordered_dither(self, threshold_map='threshold', channel=None): 

6039 """Executes a ordered-based dither operations based on predetermined 

6040 threshold maps. 

6041 

6042 +-----------+-------+-----------------------------+ 

6043 | Map | Alias | Description | 

6044 +===========+=======+=============================+ 

6045 | threshold | 1x1 | Threshold 1x1 (non-dither) | 

6046 +-----------+-------+-----------------------------+ 

6047 | checks | 2x1 | Checkerboard 2x1 (dither) | 

6048 +-----------+-------+-----------------------------+ 

6049 | o2x2 | 2x2 | Ordered 2x2 (dispersed) | 

6050 +-----------+-------+-----------------------------+ 

6051 | o3x3 | 3x3 | Ordered 3x3 (dispersed) | 

6052 +-----------+-------+-----------------------------+ 

6053 | o4x4 | 4x4 | Ordered 4x4 (dispersed) | 

6054 +-----------+-------+-----------------------------+ 

6055 | o8x8 | 8x8 | Ordered 8x8 (dispersed) | 

6056 +-----------+-------+-----------------------------+ 

6057 | h4x4a | 4x1 | Halftone 4x4 (angled) | 

6058 +-----------+-------+-----------------------------+ 

6059 | h6x6a | 6x1 | Halftone 6x6 (angled) | 

6060 +-----------+-------+-----------------------------+ 

6061 | h8x8a | 8x1 | Halftone 8x8 (angled) | 

6062 +-----------+-------+-----------------------------+ 

6063 | h4x4o | | Halftone 4x4 (orthogonal) | 

6064 +-----------+-------+-----------------------------+ 

6065 | h6x6o | | Halftone 6x6 (orthogonal) | 

6066 +-----------+-------+-----------------------------+ 

6067 | h8x8o | | Halftone 8x8 (orthogonal) | 

6068 +-----------+-------+-----------------------------+ 

6069 | h16x16o | | Halftone 16x16 (orthogonal) | 

6070 +-----------+-------+-----------------------------+ 

6071 | c5x5b | c5x5 | Circles 5x5 (black) | 

6072 +-----------+-------+-----------------------------+ 

6073 | c5x5w | | Circles 5x5 (white) | 

6074 +-----------+-------+-----------------------------+ 

6075 | c6x6b | c6x6 | Circles 6x6 (black) | 

6076 +-----------+-------+-----------------------------+ 

6077 | c6x6w | | Circles 6x6 (white) | 

6078 +-----------+-------+-----------------------------+ 

6079 | c7x7b | c7x7 | Circles 7x7 (black) | 

6080 +-----------+-------+-----------------------------+ 

6081 | c7x7w | | Circles 7x7 (white) | 

6082 +-----------+-------+-----------------------------+ 

6083 

6084 :param threshold_map: Name of threshold dither to use, followed by 

6085 optional arguments. 

6086 :type threshold_map: :class:`basestring` 

6087 :param channel: Optional argument to apply dither to specific color 

6088 channel. See :const:`CHANNELS`. 

6089 :type channel: :class:`basestring` 

6090 

6091 .. versionadded:: 0.5.7 

6092 """ 

6093 assertions.assert_string(threshold_map=threshold_map) 

6094 bmap = binary(threshold_map) 

6095 if MAGICK_VERSION_NUMBER <= 0x700: 

6096 if channel is None: 

6097 r = library.MagickOrderedPosterizeImage(self.wand, bmap) 

6098 else: 

6099 channel_ch = self._channel_to_mask(channel) 

6100 r = library.MagickOrderedPosterizeImageChannel(self.wand, 

6101 channel_ch, 

6102 bmap) 

6103 else: # pragma: no cover 

6104 if channel is None: 

6105 r = library.MagickOrderedDitherImage(self.wand, bmap) 

6106 else: 

6107 channel_ch = self._channel_to_mask(channel) 

6108 mask = library.MagickSetImageChannelMask(self.wand, 

6109 channel_ch) 

6110 r = library.MagickOrderedDitherImage(self.wand, bmap) 

6111 library.MagickSetImageChannelMask(self.wand, mask) 

6112 return r 

6113 

6114 def parse_meta_geometry(self, geometry): 

6115 """Helper method to translate geometry format, and calculate 

6116 meta-characters against image dimensions. 

6117 

6118 See "Image Geometry" definitions & examples for more info: 

6119 https://imagemagick.org/script/command-line-processing.php#geometry 

6120 

6121 :param geometry: user string following ImageMagick's geometry format. 

6122 :type geometry: :class:`basestring` 

6123 :returns: Calculated width, height, offset-x, & offset-y. 

6124 :rtype: :class:`tuple` 

6125 :raises ValueError: If given geometry can not be parsed. 

6126 

6127 .. versionadded:: 0.5.6 

6128 """ 

6129 assertions.assert_string(geometry=geometry) 

6130 x = ctypes.c_ssize_t(0) 

6131 y = ctypes.c_ssize_t(0) 

6132 width = ctypes.c_size_t(self.width) 

6133 height = ctypes.c_size_t(self.height) 

6134 r = libmagick.ParseMetaGeometry(binary(geometry), 

6135 ctypes.byref(x), 

6136 ctypes.byref(y), 

6137 ctypes.byref(width), 

6138 ctypes.byref(height)) 

6139 if not bool(r): 

6140 raise ValueError('Unable to parse geometry') 

6141 return (width.value, height.value, x.value, y.value) 

6142 

6143 def percent_escape(self, string_format): 

6144 """Convenience method that expands ImageMagick's `Percent Escape`_ 

6145 characters into image attribute values. 

6146 

6147 .. _Percent Escape: https://imagemagick.org/script/escape.php 

6148 

6149 .. code:: 

6150 

6151 with wand.image import Image 

6152 

6153 with Image(filename='tests/assets/sasha.jpg') as img: 

6154 print(img.percent_escape('%f %wx%h')) 

6155 #=> sasha.jpg 204x247 

6156 

6157 .. note:: 

6158 

6159 Not all percent escaped values can be populated as I/O operations 

6160 are managed by Python, and not the CLI utility. 

6161 

6162 :param string_format: The precent escaped string to be translated. 

6163 :type string_format: :class:`basestring` 

6164 :returns: String of expanded values. 

6165 :rtype: :class:`basestring` 

6166 

6167 .. versionadded:: 0.5.6 

6168 """ 

6169 local_overwrites = { 

6170 '%m': self.format, 

6171 '%[magick]': self.format 

6172 } 

6173 for k, v in local_overwrites.items(): 

6174 string_format = string_format.replace(k, v) 

6175 self.options['format'] = string_format 

6176 return text(self.make_blob('INFO')) 

6177 

6178 @manipulative 

6179 @trap_exception 

6180 def polaroid(self, angle=0.0, caption=None, font=None, method='undefined'): 

6181 """Creates a special effect simulating a Polaroid photo. 

6182 

6183 :param angle: applies a shadow effect along this angle. 

6184 :type angle: :class:`numbers.Real` 

6185 :param caption: Writes a message at the bottom of the photo's border. 

6186 :type caption: :class:`basestring` 

6187 :param font: Specify font style. 

6188 :type font: :class:`wand.font.Font` 

6189 :param method: Interpolation method. ImageMagick-7 only. 

6190 :type method: :class:`basestring` 

6191 

6192 .. versionadded:: 0.5.4 

6193 """ 

6194 assertions.assert_real(angle=angle) 

6195 assertions.string_in_list(PIXEL_INTERPOLATE_METHODS, 

6196 'wand.image.PIXEL_INTERPOLATE_METHODS', 

6197 method=method) 

6198 ctx_ptr = library.NewDrawingWand() 

6199 if caption: 

6200 assertions.assert_string(caption=caption) 

6201 caption = binary(caption) 

6202 library.MagickSetImageProperty(self.wand, b'Caption', 

6203 caption) 

6204 if isinstance(font, Font): 

6205 if font.path: 

6206 library.DrawSetFont(ctx_ptr, binary(font.path)) 

6207 if font.size: 

6208 library.DrawSetFontSize(ctx_ptr, font.size) 

6209 if font.color: 

6210 with font.color: 

6211 library.DrawSetFillColor(ctx_ptr, font.color.resource) 

6212 library.DrawSetTextAntialias(ctx_ptr, font.antialias) 

6213 if font.stroke_color: 

6214 with font.stroke_color: 

6215 library.DrawSetStrokeColor(ctx_ptr, 

6216 font.stroke_color.resource) 

6217 if font.stroke_width: 

6218 library.DrawSetStrokeWidth(ctx_ptr, font.stroke_width) 

6219 elif font: 

6220 raise TypeError('font must be in instance of ' 

6221 'wand.font.Font, not ' + repr(font)) 

6222 if MAGICK_VERSION_NUMBER < 0x700: 

6223 r = library.MagickPolaroidImage(self.wand, ctx_ptr, angle) 

6224 else: # pragma: no cover 

6225 method_idx = PIXEL_INTERPOLATE_METHODS.index(method) 

6226 r = library.MagickPolaroidImage(self.wand, ctx_ptr, caption, angle, 

6227 method_idx) 

6228 ctx_ptr = library.DestroyDrawingWand(ctx_ptr) 

6229 return r 

6230 

6231 @manipulative 

6232 @trap_exception 

6233 def polynomial(self, arguments): 

6234 """Replace image with the sum of all images in a sequence by 

6235 calculating the pixel values a coefficient-weight value, and a 

6236 polynomial-exponent. 

6237 

6238 For example:: 

6239 

6240 with Image(filename='rose:') as img: 

6241 img.polynomial(arguments=[0.5, 1.0]) 

6242 

6243 The output image will be calculated as: 

6244 

6245 .. math:: 

6246 

6247 output = 0.5 * image ^ {1.0} 

6248 

6249 This can work on multiple images in a sequence by calculating across 

6250 each frame in the image stack. 

6251 

6252 .. code:: 

6253 

6254 with Image(filename='2frames.gif') as img: 

6255 img.polynomial(arguments=[0.5, 1.0, 0.25, 1.25]) 

6256 

6257 Where the results would be calculated as: 

6258 

6259 .. math:: 

6260 

6261 output = 0.5 * frame1 ^ {1.0} + 0.25 * frame2 ^ {1.25} 

6262 

6263 .. warning:: 

6264 

6265 This class method is only available with ImageMagick 7.0.8-41, or 

6266 greater. 

6267 

6268 :param arguments: A list of real numbers where at least two numbers 

6269 (weight & exponent) are need for each image in the 

6270 sequence. 

6271 :type arguments: :class:`collections.abc.Sequence` 

6272 :raises WandLibraryVersionError: If system's version of ImageMagick 

6273 does not support this method. 

6274 

6275 .. versionadded:: 0.5.5 

6276 """ 

6277 if library.MagickPolynomialImage is None: 

6278 msg = 'Method requires ImageMagick version 7.0.8-41 or greater.' 

6279 raise WandLibraryVersionError(msg) 

6280 if not isinstance(arguments, abc.Sequence): 

6281 raise TypeError('expected sequence of doubles, not ' + 

6282 repr(arguments)) 

6283 argc = len(arguments) 

6284 argv = (ctypes.c_double * argc)(*arguments) 

6285 return library.MagickPolynomialImage(self.wand, (argc >> 1), argv) 

6286 

6287 @manipulative 

6288 @trap_exception 

6289 def posterize(self, levels=None, dither='no'): 

6290 """Reduce color levels per channel. 

6291 

6292 :param levels: Number of levels per channel. 

6293 :type levels: :class:`numbers.Integral` 

6294 :param dither: Dither method to apply. 

6295 See :const:`DITHER_METHODS`. 

6296 :type dither: `basestring` 

6297 

6298 .. versionadded:: 0.5.0 

6299 """ 

6300 assertions.assert_integer(levels=levels) 

6301 assertions.string_in_list(DITHER_METHODS, 'wand.image.DITHER_METHODS', 

6302 dither=dither) 

6303 dither_idx = DITHER_METHODS.index(dither) 

6304 return library.MagickPosterizeImage(self.wand, levels, dither_idx) 

6305 

6306 @manipulative 

6307 @trap_exception 

6308 def quantize(self, number_colors, colorspace_type=None, 

6309 treedepth=0, dither=False, measure_error=False): 

6310 """`quantize` analyzes the colors within a sequence of images and 

6311 chooses a fixed number of colors to represent the image. The goal of 

6312 the algorithm is to minimize the color difference between the input and 

6313 output image while minimizing the processing time. 

6314 

6315 :param number_colors: The target number of colors to reduce the image. 

6316 :type number_colors: :class:`numbers.Integral` 

6317 :param colorspace_type: Available value can be found 

6318 in the :const:`COLORSPACE_TYPES`. Defaults 

6319 :attr:`colorspace`. 

6320 :type colorspace_type: :class:`basestring` 

6321 :param treedepth: A value between ``0`` & ``8`` where ``0`` will 

6322 allow ImageMagick to calculate the optimal depth 

6323 with ``Log4(number_colors)``. Default value is ``0``. 

6324 :type treedepth: :class:`numbers.Integral` 

6325 :param dither: Perform dither operation between neighboring pixel 

6326 values. If using ImageMagick-6, this can be a value 

6327 of ``True``, or ``False``. With ImageMagick-7, use 

6328 a string from :const:`DITHER_METHODS`. Default 

6329 ``False``. 

6330 :type dither: :class:`bool`, or :class:`basestring` 

6331 :param measure_error: Include total quantization error of all pixels 

6332 in an image & quantized value. 

6333 :type measure_error: :class:`bool` 

6334 

6335 .. versionadded:: 0.4.2 

6336 

6337 .. versionchanged:: 0.5.9 

6338 Fixed ImageMagick-7 ``dither`` argument, and added keyword defaults. 

6339 """ 

6340 assertions.assert_integer(number_colors=number_colors) 

6341 if colorspace_type is None: 

6342 colorspace_type = self.colorspace 

6343 assertions.string_in_list(COLORSPACE_TYPES, 

6344 'wand.image.COLORSPACE_TYPES', 

6345 colorspace_type=colorspace_type) 

6346 assertions.assert_integer(treedepth=treedepth) 

6347 if MAGICK_VERSION_NUMBER < 0x700: 

6348 assertions.assert_bool(dither=dither) 

6349 else: # pragma: no cover 

6350 if dither is False: 

6351 dither = 'no' 

6352 elif dither is True: 

6353 dither = 'riemersma' 

6354 assertions.string_in_list(DITHER_METHODS, 

6355 'wand.image.DITHER_METHODS', 

6356 dither=dither) 

6357 dither = DITHER_METHODS.index(dither) 

6358 assertions.assert_bool(measure_error=measure_error) 

6359 return library.MagickQuantizeImage( 

6360 self.wand, number_colors, 

6361 COLORSPACE_TYPES.index(colorspace_type), 

6362 treedepth, dither, measure_error 

6363 ) 

6364 

6365 @manipulative 

6366 @trap_exception 

6367 def random_threshold(self, low=0.0, high=1.0, channel=None): 

6368 """Performs a random dither to force a pixel into a binary black & 

6369 white state. Each color channel operarates independently from each 

6370 other. 

6371 

6372 :param low: bottom threshold. Any pixel value below the given value 

6373 will be rendered "0", or no value. Given threshold value 

6374 can be between ``0.0`` & ``1.0``, or ``0`` & 

6375 :attr:`quantum_range`. 

6376 :type low: :class:`numbers.Real` 

6377 :param high: top threshold. Any pixel value above the given value 

6378 will be rendered as max quantum value. Given threshold 

6379 value can be between ``0.0`` & ``1.0``, or ``0`` & 

6380 :attr:`quantum_range`. 

6381 :type high: :class:`numbers.Real` 

6382 :param channel: Optional argument to apply dither to specific color 

6383 channel. See :const:`CHANNELS`. 

6384 :type channel: :class:`basestring` 

6385 

6386 .. versionadded:: 0.5.7 

6387 """ 

6388 assertions.assert_real(low=low, high=high) 

6389 if 0 < low <= 1.0: 

6390 low *= self.quantum_range 

6391 if 0 < high <= 1.0: 

6392 high *= self.quantum_range 

6393 if channel is None: 

6394 r = library.MagickRandomThresholdImage(self.wand, low, high) 

6395 else: 

6396 ch_channel = self._channel_to_mask(channel) 

6397 if MAGICK_VERSION_NUMBER < 0x700: 

6398 r = library.MagickRandomThresholdImageChannel(self.wand, 

6399 ch_channel, 

6400 low, 

6401 high) 

6402 else: # pragma: no cover 

6403 # Set active channel, and capture mask to restore. 

6404 channel_mask = library.MagickSetImageChannelMask(self.wand, 

6405 ch_channel) 

6406 r = library.MagickRandomThresholdImage(self.wand, low, high) 

6407 # Restore original state of channels 

6408 library.MagickSetImageChannelMask(self.wand, channel_mask) 

6409 return r 

6410 

6411 def range_channel(self, channel='default_channels'): 

6412 """Calculate the minimum and maximum of quantum values in image. 

6413 

6414 .. code:: python 

6415 

6416 from wand.image import Image 

6417 

6418 with Image(filename='input.jpg') as img: 

6419 minima, maxima = img.range_channel() 

6420 

6421 :param channel: Select which color channel to evaluate. See 

6422 :const:`CHANNELS`. Default ``'default_channels'``. 

6423 :type channel: :class:`basestring` 

6424 :returns: Tuple of :attr:`minima` & :attr:`maxima` 

6425 values. Each value will be between 0.0 & 

6426 :attr:`quantum_range`. 

6427 :rtype: :class:`tuple` 

6428 

6429 .. versionadded:: 0.5.3 

6430 """ 

6431 ch_channel = self._channel_to_mask(channel) 

6432 min_color = ctypes.c_double(0.0) 

6433 max_color = ctypes.c_double(0.0) 

6434 if MAGICK_VERSION_NUMBER < 0x700: 

6435 library.MagickGetImageChannelRange(self.wand, ch_channel, 

6436 ctypes.byref(min_color), 

6437 ctypes.byref(max_color)) 

6438 else: # pragma: no cover 

6439 # Set active channel, and capture mask to restore. 

6440 channel_mask = library.MagickSetImageChannelMask(self.wand, 

6441 ch_channel) 

6442 library.MagickGetImageRange(self.wand, 

6443 ctypes.byref(min_color), 

6444 ctypes.byref(max_color)) 

6445 # Restore original state of channels 

6446 library.MagickSetImageChannelMask(self.wand, channel_mask) 

6447 return min_color.value, max_color.value 

6448 

6449 @manipulative 

6450 @trap_exception 

6451 def range_threshold(self, low_black=0.0, low_white=None, high_white=None, 

6452 high_black=None): 

6453 """Applies soft & hard thresholding. 

6454 

6455 For a soft thresholding, parameters should be monotonically increasing: 

6456 

6457 with Image(filename='text.png') as img: 

6458 img.range_threshold(0.2, 0.4, 0.6, 0.8) 

6459 

6460 For a hard thresholding, parameters should be the same: 

6461 

6462 with Image(filename='text.png') as img: 

6463 img.range_threshold(0.4, 0.4, 0.6, 0.6) 

6464 

6465 .. warning:: 

6466 

6467 This class method is only available with ImageMagick 7.0.8-41, or 

6468 greater. 

6469 

6470 :param low_black: Define the minimum threshold value. 

6471 :type low_black: :class:`numbers.Real` 

6472 :param low_white: Define the minimum threshold value. 

6473 :type low_white: :class:`numbers.Real` 

6474 :param high_white: Define the maximum threshold value. 

6475 :type high_white: :class:`numbers.Real` 

6476 :param high_black: Define the maximum threshold value. 

6477 :type high_black: :class:`numbers.Real` 

6478 :raises WandLibraryVersionError: If system's version of ImageMagick 

6479 does not support this method. 

6480 

6481 .. versionadded:: 0.5.5 

6482 """ 

6483 if library.MagickRangeThresholdImage is None: 

6484 msg = 'Method requires ImageMagick version 7.0.8-41 or greater.' 

6485 raise WandLibraryVersionError(msg) 

6486 # Populate defaults to follow CLI behavior 

6487 if low_white is None: 

6488 low_white = low_black 

6489 if high_white is None: 

6490 high_white = low_white 

6491 if high_black is None: 

6492 high_black = high_white 

6493 assertions.assert_real(low_black=low_black, low_white=low_white, 

6494 high_white=high_white, high_black=high_black) 

6495 if 0 < low_black <= 1.0: 

6496 low_black *= self.quantum_range 

6497 if 0 < low_white <= 1.0: 

6498 low_white *= self.quantum_range 

6499 if 0 < high_white <= 1.0: 

6500 high_white *= self.quantum_range 

6501 if 0 < high_black <= 1.0: 

6502 high_black *= self.quantum_range 

6503 return library.MagickRangeThresholdImage(self.wand, 

6504 low_black, low_white, 

6505 high_white, high_black) 

6506 

6507 @trap_exception 

6508 def read_mask(self, clip_mask=None): 

6509 """Sets the read mask where the gray values of the clip mask 

6510 are used to blend during composite operations. Call this method with 

6511 a ``None`` argument to clear any previously set masks. 

6512 

6513 This method is also useful for :meth:`compare` method for limiting 

6514 region of interest. 

6515 

6516 .. warning:: 

6517 This method is only available with ImageMagick-7. 

6518 

6519 :param clip_mask: Image to reference as blend mask. 

6520 :type clip_mask: :class:`BaseImage` 

6521 

6522 .. versionadded:: 0.5.7 

6523 """ 

6524 r = False 

6525 ReadPixelMask = 0x000001 

6526 if library.MagickSetImageMask is None: 

6527 raise WandLibraryVersionError('Method requires ImageMagick-7.') 

6528 else: # pragma: no cover 

6529 if clip_mask is None: 

6530 r = library.MagickSetImageMask(self.wand, ReadPixelMask, None) 

6531 elif isinstance(clip_mask, BaseImage): 

6532 r = library.MagickSetImageMask(self.wand, ReadPixelMask, 

6533 clip_mask.wand) 

6534 return r 

6535 

6536 @manipulative 

6537 @trap_exception 

6538 def remap(self, affinity=None, method='no'): 

6539 """Rebuild image palette with closest color from given affinity image. 

6540 

6541 :param affinity: reference image. 

6542 :type affinity: :class:`BaseImage` 

6543 :param method: dither method. See :const:`DITHER_METHODS`. 

6544 Default is ``'no'`` dither. 

6545 :type method: :class:`basestring` 

6546 

6547 .. versionadded:: 0.5.3 

6548 """ 

6549 if not isinstance(affinity, BaseImage): 

6550 raise TypeError('Expecting affinity to be a BaseImage, not ' + 

6551 repr(affinity)) 

6552 assertions.string_in_list(DITHER_METHODS, 'wand.image.DITHER_METHODS', 

6553 method=method) 

6554 method_idx = DITHER_METHODS.index(method) 

6555 return library.MagickRemapImage(self.wand, affinity.wand, method_idx) 

6556 

6557 @manipulative 

6558 @trap_exception 

6559 def resample(self, x_res=None, y_res=None, filter='undefined', blur=1): 

6560 """Adjust the number of pixels in an image so that when displayed at 

6561 the given Resolution or Density the image will still look the same size 

6562 in real world terms. 

6563 

6564 :param x_res: the X resolution (density) in the scaled image. default 

6565 is the original resolution. 

6566 :type x_res: :class:`numbers.Real` 

6567 :param y_res: the Y resolution (density) in the scaled image. default 

6568 is the original resolution. 

6569 :type y_res: :class:`numbers.Real` 

6570 :param filter: a filter type to use for resizing. choose one in 

6571 :const:`FILTER_TYPES`. default is ``'undefined'`` 

6572 which means IM will try to guess best one to use. 

6573 :type filter: :class:`basestring`, :class:`numbers.Integral` 

6574 :param blur: the blur factor where > 1 is blurry, < 1 is sharp. 

6575 default is 1 

6576 :type blur: :class:`numbers.Real` 

6577 

6578 .. versionadded:: 0.4.5 

6579 """ 

6580 if x_res is None: 

6581 x_res, _ = self.resolution 

6582 if y_res is None: 

6583 _, y_res = self.resolution 

6584 assertions.assert_real(x_res=x_res, y_res=y_res, blur=blur) 

6585 if x_res < 1: 

6586 raise ValueError('x_res must be a Real number, not ' + 

6587 repr(x_res)) 

6588 elif y_res < 1: 

6589 raise ValueError('y_res must be a Real number, not ' + 

6590 repr(y_res)) 

6591 elif not isinstance(filter, (string_type, numbers.Integral)): 

6592 raise TypeError('filter must be one string defined in wand.image.' 

6593 'FILTER_TYPES or an integer, not ' + repr(filter)) 

6594 if isinstance(filter, string_type): 

6595 try: 

6596 filter = FILTER_TYPES.index(filter) 

6597 except IndexError: 

6598 raise ValueError(repr(filter) + ' is an invalid filter type; ' 

6599 'choose on in ' + repr(FILTER_TYPES)) 

6600 elif (isinstance(filter, numbers.Integral) and 

6601 not (0 <= filter < len(FILTER_TYPES))): 

6602 raise ValueError(repr(filter) + ' is an invalid filter type') 

6603 blur = ctypes.c_double(float(blur)) 

6604 if self.animation: 

6605 self.wand = library.MagickCoalesceImages(self.wand) 

6606 self.reset_sequence() 

6607 library.MagickSetLastIterator(self.wand) 

6608 n = library.MagickGetIteratorIndex(self.wand) 

6609 library.MagickResetIterator(self.wand) 

6610 for i in xrange(n + 1): 

6611 library.MagickSetIteratorIndex(self.wand, i) 

6612 r = library.MagickResampleImage(self.wand, x_res, y_res, 

6613 filter, blur) 

6614 else: 

6615 r = library.MagickResampleImage(self.wand, x_res, y_res, 

6616 filter, blur) 

6617 return r 

6618 

6619 def reset_coords(self): 

6620 """Reset the coordinate frame of the image so to the upper-left corner 

6621 is (0, 0) again (crop and rotate operations change it). 

6622 

6623 .. versionadded:: 0.2.0 

6624 

6625 """ 

6626 library.MagickResetImagePage(self.wand, None) 

6627 

6628 def reset_sequence(self): 

6629 """Abstract method prototype. 

6630 See :meth:`wand.image.Image.reset_sequence()`. 

6631 

6632 .. versionadded:: 0.6.0 

6633 """ 

6634 pass 

6635 

6636 @manipulative 

6637 @trap_exception 

6638 def resize(self, width=None, height=None, filter='undefined', blur=1): 

6639 """Resizes the image. 

6640 

6641 :param width: the width in the scaled image. default is the original 

6642 width 

6643 :type width: :class:`numbers.Integral` 

6644 :param height: the height in the scaled image. default is the original 

6645 height 

6646 :type height: :class:`numbers.Integral` 

6647 :param filter: a filter type to use for resizing. choose one in 

6648 :const:`FILTER_TYPES`. default is ``'undefined'`` 

6649 which means IM will try to guess best one to use 

6650 :type filter: :class:`basestring`, :class:`numbers.Integral` 

6651 :param blur: the blur factor where > 1 is blurry, < 1 is sharp. 

6652 default is 1 

6653 :type blur: :class:`numbers.Real` 

6654 

6655 .. versionchanged:: 0.2.1 

6656 The default value of ``filter`` has changed from ``'triangle'`` 

6657 to ``'undefined'`` instead. 

6658 

6659 .. versionchanged:: 0.1.8 

6660 The ``blur`` parameter changed to take :class:`numbers.Real` 

6661 instead of :class:`numbers.Rational`. 

6662 

6663 .. versionadded:: 0.1.1 

6664 

6665 """ 

6666 if width is None: 

6667 width = self.width 

6668 if height is None: 

6669 height = self.height 

6670 assertions.assert_counting_number(width=width, height=height) 

6671 assertions.assert_real(blur=blur) 

6672 if not isinstance(filter, (string_type, numbers.Integral)): 

6673 raise TypeError('filter must be one string defined in wand.image.' 

6674 'FILTER_TYPES or an integer, not ' + repr(filter)) 

6675 if isinstance(filter, string_type): 

6676 try: 

6677 filter = FILTER_TYPES.index(filter) 

6678 except IndexError: 

6679 raise ValueError(repr(filter) + ' is an invalid filter type; ' 

6680 'choose on in ' + repr(FILTER_TYPES)) 

6681 elif (isinstance(filter, numbers.Integral) and 

6682 not (0 <= filter < len(FILTER_TYPES))): 

6683 raise ValueError(repr(filter) + ' is an invalid filter type') 

6684 blur = ctypes.c_double(float(blur)) 

6685 if self.animation: 

6686 self.wand = library.MagickCoalesceImages(self.wand) 

6687 self.reset_sequence() 

6688 library.MagickSetLastIterator(self.wand) 

6689 n = library.MagickGetIteratorIndex(self.wand) 

6690 library.MagickResetIterator(self.wand) 

6691 for i in xrange(n + 1): 

6692 library.MagickSetIteratorIndex(self.wand, i) 

6693 r = library.MagickResizeImage(self.wand, width, height, 

6694 filter, blur) 

6695 library.MagickSetSize(self.wand, width, height) 

6696 else: 

6697 r = library.MagickResizeImage(self.wand, width, height, 

6698 filter, blur) 

6699 library.MagickSetSize(self.wand, width, height) 

6700 return r 

6701 

6702 @manipulative 

6703 @trap_exception 

6704 def rotate(self, degree, background=None, reset_coords=True): 

6705 """Rotates the image right. It takes a ``background`` color 

6706 for ``degree`` that isn't a multiple of 90. 

6707 

6708 :param degree: a degree to rotate. multiples of 360 affect nothing 

6709 :type degree: :class:`numbers.Real` 

6710 :param background: an optional background color. 

6711 default is transparent 

6712 :type background: :class:`wand.color.Color` 

6713 :param reset_coords: optional flag. If set, after the rotation, the 

6714 coordinate frame will be relocated to the upper-left corner of 

6715 the new image. By default is `True`. 

6716 :type reset_coords: :class:`bool` 

6717 

6718 .. versionadded:: 0.2.0 

6719 The ``reset_coords`` parameter. 

6720 

6721 .. versionadded:: 0.1.8 

6722 

6723 """ 

6724 if background is None: 

6725 background = Color('transparent') 

6726 elif isinstance(background, string_type): 

6727 background = Color(background) 

6728 assertions.assert_color(background=background) 

6729 assertions.assert_real(degree=degree) 

6730 with background: 

6731 if self.animation: 

6732 self.wand = library.MagickCoalesceImages(self.wand) 

6733 self.reset_sequence() 

6734 library.MagickSetLastIterator(self.wand) 

6735 n = library.MagickGetIteratorIndex(self.wand) 

6736 library.MagickResetIterator(self.wand) 

6737 for i in range(0, n + 1): 

6738 library.MagickSetIteratorIndex(self.wand, i) 

6739 result = library.MagickRotateImage(self.wand, 

6740 background.resource, 

6741 degree) 

6742 if reset_coords: 

6743 library.MagickResetImagePage(self.wand, None) 

6744 else: 

6745 result = library.MagickRotateImage(self.wand, 

6746 background.resource, 

6747 degree) 

6748 if reset_coords: 

6749 self.reset_coords() 

6750 return result 

6751 

6752 @manipulative 

6753 @trap_exception 

6754 def rotational_blur(self, angle=0.0, channel=None): 

6755 """Blur an image in a radius around the center of an image. 

6756 

6757 .. warning:: Requires ImageMagick-6.8.8 or greater. 

6758 

6759 :param angle: Degrees of rotation to blur with. 

6760 :type angle: :class:`numbers.Real` 

6761 :param channel: Optional channel to apply the effect against. See 

6762 :const:`CHANNELS` for a list of possible values. 

6763 :type channel: :class:`basestring` 

6764 :raises WandLibraryVersionError: If system's version of ImageMagick 

6765 does not support this method. 

6766 

6767 .. versionadded:: 0.5.4 

6768 """ 

6769 if not library.MagickRotationalBlurImage: # pragma: no cover 

6770 msg = ("Method `rotational_blur` not available on installed " 

6771 "version of ImageMagick library. ") 

6772 raise WandLibraryVersionError(msg) 

6773 assertions.assert_real(angle=angle) 

6774 if channel: 

6775 channel_ch = self._channel_to_mask(channel) 

6776 if MAGICK_VERSION_NUMBER < 0x700: 

6777 r = library.MagickRotationalBlurImageChannel(self.wand, 

6778 channel_ch, 

6779 angle) 

6780 else: # pragma: no cover 

6781 channel_mask = library.MagickSetImageChannelMask(self.wand, 

6782 channel_ch) 

6783 r = library.MagickRotationalBlurImage(self.wand, angle) 

6784 library.MagickSetImageChannelMask(self.wand, channel_mask) 

6785 else: 

6786 r = library.MagickRotationalBlurImage(self.wand, angle) 

6787 return r 

6788 

6789 @manipulative 

6790 @trap_exception 

6791 def sample(self, width=None, height=None): 

6792 """Resizes the image by sampling the pixels. It's basically quicker 

6793 than :meth:`resize()` except less quality as a trade-off. 

6794 

6795 :param width: the width in the scaled image. default is the original 

6796 width 

6797 :type width: :class:`numbers.Integral` 

6798 :param height: the height in the scaled image. default is the original 

6799 height 

6800 :type height: :class:`numbers.Integral` 

6801 

6802 .. versionadded:: 0.3.4 

6803 

6804 """ 

6805 if width is None: 

6806 width = self.width 

6807 if height is None: 

6808 height = self.height 

6809 assertions.assert_counting_number(width=width, height=height) 

6810 if self.animation: 

6811 self.wand = library.MagickCoalesceImages(self.wand) 

6812 self.reset_sequence() 

6813 library.MagickSetLastIterator(self.wand) 

6814 n = library.MagickGetIteratorIndex(self.wand) 

6815 library.MagickResetIterator(self.wand) 

6816 for i in xrange(n + 1): 

6817 library.MagickSetIteratorIndex(self.wand, i) 

6818 r = library.MagickSampleImage(self.wand, width, height) 

6819 library.MagickSetSize(self.wand, width, height) 

6820 else: 

6821 r = library.MagickSampleImage(self.wand, width, height) 

6822 library.MagickSetSize(self.wand, width, height) 

6823 return bool(r) 

6824 

6825 @manipulative 

6826 @trap_exception 

6827 def scale(self, columns=1, rows=1): 

6828 """Increase image size by scaling each pixel value by given ``columns`` 

6829 and ``rows``. 

6830 

6831 :param columns: The number of columns, in pixels, to scale the image 

6832 horizontally. 

6833 :type columns: :class:`numbers.Integral` 

6834 :param rows: The number of rows, in pixels, to scale the image 

6835 vertically. 

6836 :type rows: :class:`numbers.Integral` 

6837 

6838 .. versionadded:: 0.5.7 

6839 """ 

6840 assertions.assert_counting_number(columns=columns, rows=rows) 

6841 return library.MagickScaleImage(self.wand, columns, rows) 

6842 

6843 @manipulative 

6844 @trap_exception 

6845 def selective_blur(self, radius=0.0, sigma=0.0, threshold=0.0, 

6846 channel=None): 

6847 """Blur an image within a given threshold. 

6848 

6849 For best effects, use a value between 10% and 50% of 

6850 :attr:`quantum_range` 

6851 

6852 .. code:: 

6853 

6854 from wand.image import Image 

6855 

6856 with Image(filename='photo.jpg') as img: 

6857 # Apply 8x3 blur with a 10% threshold 

6858 img.selective_blur(8.0, 3.0, 0.1 * img.quantum_range) 

6859 

6860 :param radius: Size of gaussian aperture. 

6861 :type radius: :class:`numbers.Real` 

6862 :param sigma: Standard deviation of gaussian operator. 

6863 :type sigma: :class:`numbers.Real` 

6864 :param threshold: Only pixels within contrast threshold are effected. 

6865 Value should be between ``0.0`` and 

6866 :attr:`quantum_range`. 

6867 :type threshold: :class:`numbers.Real` 

6868 :param channel: Optional color channel to target. See 

6869 :const:`CHANNELS` 

6870 :type channel: :class:`basestring` 

6871 

6872 .. versionadded:: 0.5.3 

6873 

6874 .. versionchanged:: 0.5.5 

6875 Added ``channel`` argument. 

6876 """ 

6877 assertions.assert_real(radius=radius, sigma=sigma, threshold=threshold) 

6878 if channel is None: 

6879 r = library.MagickSelectiveBlurImage(self.wand, 

6880 radius, 

6881 sigma, 

6882 threshold) 

6883 else: 

6884 channel_ch = self._channel_to_mask(channel) 

6885 if MAGICK_VERSION_NUMBER < 0x700: 

6886 r = library.MagickSelectiveBlurImageChannel(self.wand, 

6887 channel_ch, 

6888 radius, 

6889 sigma, 

6890 threshold) 

6891 else: # pragma: no cover 

6892 mask = library.MagickSetImageChannelMask(self.wand, channel_ch) 

6893 r = library.MagickSelectiveBlurImage(self.wand, 

6894 radius, 

6895 sigma, 

6896 threshold) 

6897 library.MagickSetImageChannelMask(self.wand, mask) 

6898 return r 

6899 

6900 @manipulative 

6901 @trap_exception 

6902 def sepia_tone(self, threshold=0.8): 

6903 """Creates a Sepia Tone special effect similar to a darkroom chemical 

6904 toning. 

6905 

6906 :param threshold: The extent of the toning. Value can be between ``0`` 

6907 & :attr:`quantum_range`, or ``0`` & ``1.0``. 

6908 Default value is ``0.8`` or "80%". 

6909 :type threshold: :class:`numbers.Real` 

6910 

6911 .. versionadded:: 0.5.7 

6912 """ 

6913 assertions.assert_real(threshold=threshold) 

6914 if 0.0 < threshold <= 1.0: 

6915 threshold *= self.quantum_range 

6916 return library.MagickSepiaToneImage(self.wand, threshold) 

6917 

6918 @manipulative 

6919 @trap_exception 

6920 def shade(self, gray=False, azimuth=0.0, elevation=0.0): 

6921 """Creates a 3D effect by simulating a light from an 

6922 elevated angle. 

6923 

6924 :param gray: Isolate the effect on pixel intensity. 

6925 Default is False. 

6926 :type gray: :class:`bool` 

6927 :param azimuth: Angle from x-axis. 

6928 :type azimuth: :class:`numbers.Real` 

6929 :param elevation: Amount of pixels from the z-axis. 

6930 :type elevation: :class:`numbers.Real` 

6931 

6932 .. versionadded:: 0.5.0 

6933 """ 

6934 assertions.assert_real(azimuth=azimuth, elevation=elevation) 

6935 return library.MagickShadeImage(self.wand, gray, 

6936 azimuth, elevation) 

6937 

6938 @manipulative 

6939 @trap_exception 

6940 def shadow(self, alpha=0.0, sigma=0.0, x=0, y=0): 

6941 """Generates an image shadow. 

6942 

6943 :param alpha: Ratio of transparency. 

6944 :type alpha: :class:`numbers.Real` 

6945 :param sigma: Standard deviation of the gaussian filter. 

6946 :type sigma: :class:`numbers.Real` 

6947 :param x: x-offset. 

6948 :type x: :class:`numbers.Integral` 

6949 :param y: y-offset. 

6950 :type y: :class:`numbers.Integral` 

6951 

6952 .. versionadded:: 0.5.0 

6953 """ 

6954 assertions.assert_real(alpha=alpha, sigma=sigma) 

6955 assertions.assert_integer(x=x, y=y) 

6956 return library.MagickShadowImage(self.wand, alpha, sigma, x, y) 

6957 

6958 @manipulative 

6959 @trap_exception 

6960 def sharpen(self, radius=0.0, sigma=0.0, channel=None): 

6961 """Applies a gaussian effect to enhance the sharpness of an 

6962 image. 

6963 

6964 .. note:: 

6965 

6966 For best results, ensure ``radius`` is larger than 

6967 ``sigma``. 

6968 

6969 Defaults values of zero will have ImageMagick attempt 

6970 to auto-select suitable values. 

6971 

6972 :param radius: size of gaussian aperture. 

6973 :type radius: :class:`numbers.Real` 

6974 :param sigma: Standard deviation of the gaussian filter. 

6975 :type sigma: :class:`numbers.Real` 

6976 :param channel: Optional color channel to target. See 

6977 :const:`CHANNELS`. 

6978 :type channel: :class:`basestring` 

6979 

6980 .. versionadded:: 0.5.0 

6981 

6982 .. versionchanged:: 0.5.5 

6983 Added ``channel`` argument. 

6984 """ 

6985 assertions.assert_real(radius=radius, sigma=sigma) 

6986 if channel is None: 

6987 r = library.MagickSharpenImage(self.wand, radius, sigma) 

6988 else: 

6989 channel_ch = self._channel_to_mask(channel) 

6990 if MAGICK_VERSION_NUMBER < 0x700: 

6991 r = library.MagickSharpenImageChannel(self.wand, 

6992 channel_ch, 

6993 radius, sigma) 

6994 else: # pragma: no cover 

6995 mask = library.MagickSetImageChannelMask(self.wand, channel_ch) 

6996 r = library.MagickSharpenImage(self.wand, radius, sigma) 

6997 library.MagickSetImageChannelMask(self.wand, mask) 

6998 return r 

6999 

7000 @manipulative 

7001 @trap_exception 

7002 def shave(self, columns=0, rows=0): 

7003 """Remove pixels from the edges. 

7004 

7005 :param columns: amount to shave off both sides of the x-axis. 

7006 :type columns: :class:`numbers.Integral` 

7007 :param rows: amount to shave off both sides of the y-axis. 

7008 :type rows: :class:`numbers.Integral` 

7009 

7010 .. versionadded:: 0.5.0 

7011 """ 

7012 assertions.assert_integer(columns=columns, row=rows) 

7013 return library.MagickShaveImage(self.wand, columns, rows) 

7014 

7015 @manipulative 

7016 @trap_exception 

7017 def shear(self, background='WHITE', x=0.0, y=0.0): 

7018 """Shears the image to create a parallelogram, and fill the space 

7019 created with a ``background`` color. 

7020 

7021 :param background: Color to fill the void created by shearing the 

7022 image. 

7023 :type background: :class:`wand.color.Color` 

7024 :param x: Slide the image along the X-axis. 

7025 :type x: :class:`numbers.Real` 

7026 :param y: Slide the image along the Y-axis. 

7027 :type y: :class:`numbers.Real` 

7028 

7029 .. versionadded:: 0.5.4 

7030 """ 

7031 if isinstance(background, string_type): 

7032 background = Color(background) 

7033 assertions.assert_color(background=background) 

7034 assertions.assert_real(x=x, y=y) 

7035 with background: 

7036 r = library.MagickShearImage(self.wand, background.resource, x, y) 

7037 return r 

7038 

7039 @manipulative 

7040 @trap_exception 

7041 def sigmoidal_contrast(self, sharpen=True, strength=0.0, midpoint=0.0, 

7042 channel=None): 

7043 """Modifies the contrast of the image by applying non-linear sigmoidal 

7044 algorithm. 

7045 

7046 .. code:: python 

7047 

7048 with Image(filename='photo.jpg') as img: 

7049 img.sigmoidal_contrast(sharpen=True, 

7050 strength=3, 

7051 midpoint=0.65 * img.quantum_range) 

7052 

7053 :param sharpen: Increase the contrast when ``True`` (default), else 

7054 reduces contrast. 

7055 :type sharpen: :class:`bool` 

7056 :param strength: How much to adjust the contrast. Where a value of 

7057 ``0.0`` has no effect, ``3.0`` is typical, and 

7058 ``20.0`` is extreme. 

7059 :type strength: :class:`numbers.Real` 

7060 :param midpoint: Normalized value between `0.0` & :attr:`quantum_range` 

7061 :type midpoint: :class:`numbers.Real` 

7062 :param channel: Optional color channel to target. See 

7063 :const:`CHANNELS`. 

7064 :type channel: :class:`basestring` 

7065 

7066 .. versionadded:: 0.5.4 

7067 

7068 .. versionchanged:: 0.5.5 

7069 Added ``channel`` argument. 

7070 """ 

7071 assertions.assert_bool(sharpen=sharpen) 

7072 assertions.assert_real(strength=strength, midpoint=midpoint) 

7073 if channel is None: 

7074 r = library.MagickSigmoidalContrastImage(self.wand, 

7075 sharpen, 

7076 strength, 

7077 midpoint) 

7078 else: 

7079 channel_ch = self._channel_to_mask(channel) 

7080 if MAGICK_VERSION_NUMBER < 0x700: 

7081 r = library.MagickSigmoidalContrastImageChannel( 

7082 self.wand, channel_ch, sharpen, strength, midpoint 

7083 ) 

7084 else: # pragma: no cover 

7085 mask = library.MagickSetImageChannelMask(self.wand, channel_ch) 

7086 r = library.MagickSigmoidalContrastImage(self.wand, 

7087 sharpen, 

7088 strength, 

7089 midpoint) 

7090 library.MagickSetImageChannelMask(self.wand, mask) 

7091 return r 

7092 

7093 def similarity(self, reference, threshold=0.0, 

7094 metric='undefined'): 

7095 """Scan image for best matching ``reference`` image, and 

7096 return location & similarity. 

7097 

7098 Use parameter ``threshold`` to stop subimage scanning if the matching 

7099 similarity value is below the given value. This is the same as the CLI 

7100 ``-similarity-threshold`` option. 

7101 

7102 This method will always return a location & the lowest computed 

7103 similarity value. Users are responsible for checking the similarity 

7104 value to determine if a matching location is valid. Traditionally, a 

7105 similarity value greater than `0.3183099` is considered dissimilar. 

7106 

7107 .. code:: python 

7108 

7109 from wand.image import Image 

7110 

7111 dissimilarity_threshold = 0.318 

7112 similarity_threshold = 0.05 

7113 with Image(filename='subject.jpg') as img: 

7114 with Image(filename='object.jpg') as reference: 

7115 location, diff = img.similarity(reference, 

7116 similarity_threshold) 

7117 if diff > dissimilarity_threshold: 

7118 print('Images too dissimilar to match') 

7119 elif diff <= similarity_threshold: 

7120 print('First match @ {left}x{top}'.format(**location)) 

7121 else: 

7122 print('Best match @ {left}x{top}'.format(**location)) 

7123 

7124 .. warning:: 

7125 

7126 This operation can be slow to complete. 

7127 

7128 :param reference: Image to search for. 

7129 :type reference: :class:`wand.image.Image` 

7130 :param threshold: Stop scanning if reference similarity is 

7131 below given threshold. Value can be between ``0.0`` 

7132 and :attr:`quantum_range`. Default is ``0.0``. 

7133 :type threshold: :class:`numbers.Real` 

7134 :param metric: specify which comparison algorithm to use. See 

7135 :const:`COMPARE_METRICS` for a list of values. 

7136 Only used by ImageMagick-7. 

7137 :type metric: :class:`basestring` 

7138 :returns: List of location & similarity value. Location being a 

7139 dictionary of ``width``, ``height``, ``left``, & ``top``. 

7140 The similarity value is the compare distance, so a value of 

7141 ``0.0`` means an exact match. 

7142 :rtype: :class:`tuple` (:class:`dict`, :class:`numbers.Real`) 

7143 

7144 .. versionadded:: 0.5.4 

7145 

7146 has been added. 

7147 """ 

7148 assertions.assert_real(threshold=threshold) 

7149 if not isinstance(reference, BaseImage): 

7150 raise TypeError('reference must be in instance of ' 

7151 'wand.image.Image, not ' + repr(reference)) 

7152 rio = RectangleInfo(0, 0, 0, 0) 

7153 diff = ctypes.c_double(0.0) 

7154 if MAGICK_VERSION_NUMBER < 0x700: 

7155 artifact_value = binary(str(threshold)) # FIXME 

7156 library.MagickSetImageArtifact(self.wand, 

7157 b'compare:similarity-threshold', 

7158 artifact_value) 

7159 r = library.MagickSimilarityImage(self.wand, 

7160 reference.wand, 

7161 ctypes.byref(rio), 

7162 ctypes.byref(diff)) 

7163 else: # pragma: no cover 

7164 assertions.string_in_list(COMPARE_METRICS, 

7165 'wand.image.COMPARE_METRICS', 

7166 metric=metric) 

7167 metric_idx = COMPARE_METRICS.index(metric) 

7168 r = library.MagickSimilarityImage(self.wand, 

7169 reference.wand, 

7170 metric_idx, 

7171 threshold, 

7172 ctypes.byref(rio), 

7173 ctypes.byref(diff)) 

7174 if not r: # pragma: no cover 

7175 self.raise_exception() 

7176 else: 

7177 r = library.DestroyMagickWand(r) 

7178 location = dict(width=rio.width, height=rio.height, 

7179 top=rio.y, left=rio.x) 

7180 return (location, diff.value) 

7181 

7182 @manipulative 

7183 @trap_exception 

7184 def sketch(self, radius=0.0, sigma=0.0, angle=0.0): 

7185 """Simulates a pencil sketch effect. For best results, ``radius`` 

7186 value should be larger than ``sigma``. 

7187 

7188 :param radius: size of Gaussian aperture. 

7189 :type radius: :class:`numbers.Real` 

7190 :param sigma: standard deviation of the Gaussian operator. 

7191 :type sigma: :class:`numbers.Real` 

7192 :param angle: direction of blur. 

7193 :type angle: :class:`numbers.Real` 

7194 

7195 .. versionadded:: 0.5.3 

7196 """ 

7197 assertions.assert_real(radius=radius, sigma=sigma, angle=angle) 

7198 return library.MagickSketchImage(self.wand, radius, sigma, angle) 

7199 

7200 @trap_exception 

7201 def smush(self, stacked=False, offset=0): 

7202 """Appends all images together. Similar behavior to :meth:`concat`, 

7203 but with an optional offset between images. 

7204 

7205 :param stacked: If True, will join top-to-bottom. If False, join images 

7206 from left-to-right (default). 

7207 :type stacked: :class:`bool` 

7208 :param offset: Minimum space (in pixels) between each join. 

7209 :type offset: :class:`numbers.Integral` 

7210 

7211 .. versionadded:: 0.5.3 

7212 """ 

7213 assertions.assert_integer(offset=offset) 

7214 library.MagickResetIterator(self.wand) 

7215 result = library.MagickSmushImages(self.wand, bool(stacked), offset) 

7216 if result: 

7217 self.wand = result 

7218 self.reset_sequence() 

7219 return bool(result) 

7220 

7221 @manipulative 

7222 @trap_exception 

7223 def solarize(self, threshold=0.0, channel=None): 

7224 """Simulates extreme overexposure. 

7225 

7226 :param threshold: between ``0.0`` and :attr:`quantum_range`. 

7227 :type threshold: :class:`numbers.Real` 

7228 :param channel: Optional color channel to target. See 

7229 :const:`CHANNELS` 

7230 :type channel: :class:`basestring` 

7231 

7232 .. versionadded:: 0.5.3 

7233 

7234 .. versionchanged:: 0.5.5 

7235 Added ``channel`` argument. 

7236 """ 

7237 assertions.assert_real(threshold=threshold) 

7238 if channel is None: 

7239 r = library.MagickSolarizeImage(self.wand, threshold) 

7240 else: 

7241 channel_ch = self._channel_to_mask(channel) 

7242 if MAGICK_VERSION_NUMBER < 0x700: 

7243 r = library.MagickSolarizeImageChannel(self.wand, 

7244 channel_ch, 

7245 threshold) 

7246 else: # pragma: no cover 

7247 mask = library.MagickSetImageChannelMask(self.wand, channel_ch) 

7248 r = library.MagickSolarizeImage(self.wand, threshold) 

7249 library.MagickSetImageChannelMask(self.wand, mask) 

7250 return r 

7251 

7252 @manipulative 

7253 @trap_exception 

7254 def sparse_color(self, method, colors, channel_mask=0x7): 

7255 """Interpolates color values between points on an image. 

7256 

7257 The ``colors`` argument should be a dict mapping 

7258 :class:`~wand.color.Color` keys to coordinate tuples. 

7259 

7260 For example:: 

7261 

7262 from wand.color import Color 

7263 from wand.image import Image 

7264 

7265 colors = { 

7266 Color('RED'): (10, 50), 

7267 Color('YELLOW'): (174, 32), 

7268 Color('ORANGE'): (74, 123) 

7269 } 

7270 with Image(filename='input.png') as img: 

7271 img.sparse_colors('bilinear', colors) 

7272 

7273 The available interpolate methods are: 

7274 

7275 - ``'barycentric'`` 

7276 - ``'bilinear'`` 

7277 - ``'shepards'`` 

7278 - ``'voronoi'`` 

7279 - ``'inverse'`` 

7280 - ``'manhattan'`` 

7281 

7282 You can control which color channels are effected by building a custom 

7283 channel mask. For example:: 

7284 

7285 from wand.image import Image, CHANNELS 

7286 

7287 with Image(filename='input.png') as img: 

7288 colors = { 

7289 img[50, 50]: (50, 50), 

7290 img[100, 50]: (100, 50), 

7291 img[50, 75]: (50, 75), 

7292 img[100, 100]: (100, 100) 

7293 } 

7294 # Only apply Voronoi to Red & Alpha channels 

7295 mask = CHANNELS['red'] | CHANNELS['alpha'] 

7296 img.sparse_colors('voronoi', colors, channel_mask=mask) 

7297 

7298 :param method: Interpolate method. See :const:`SPARSE_COLOR_METHODS` 

7299 :type method: :class:`basestring` 

7300 :param colors: A dictionary of :class:`~wand.color.Color` keys mapped 

7301 to an (x, y) coordinate tuple. 

7302 :type colors: :class:`abc.Mapping` 

7303 { :class:`~wand.color.Color`: (int, int) } 

7304 :param channel_mask: Isolate specific color channels to apply 

7305 interpolation. Default to RGB channels. 

7306 :type channel_mask: :class:`numbers.Integral` 

7307 

7308 .. versionadded:: 0.5.3 

7309 """ 

7310 assertions.string_in_list(SPARSE_COLOR_METHODS, 

7311 'wand.image.SPARSE_COLOR_METHODS', 

7312 method=method) 

7313 if not isinstance(colors, abc.Mapping): 

7314 raise TypeError('Colors must be a dict, not' + repr(colors)) 

7315 assertions.assert_unsigned_integer(channel_mask=channel_mask) 

7316 method_idx = SPARSE_COLOR_METHODS[method] 

7317 arguments = list() 

7318 for color, point in colors.items(): 

7319 if isinstance(color, string_type): 

7320 color = Color(color) 

7321 x, y = point 

7322 arguments.append(x) 

7323 arguments.append(y) 

7324 with color as c: 

7325 if channel_mask & CHANNELS['red']: 

7326 arguments.append(c.red) 

7327 if channel_mask & CHANNELS['green']: 

7328 arguments.append(c.green) 

7329 if channel_mask & CHANNELS['blue']: 

7330 arguments.append(c.blue) 

7331 if channel_mask & CHANNELS['alpha']: 

7332 arguments.append(c.alpha) 

7333 argc = len(arguments) 

7334 args = (ctypes.c_double * argc)(*arguments) 

7335 if MAGICK_VERSION_NUMBER < 0x700: 

7336 r = library.MagickSparseColorImage(self.wand, 

7337 channel_mask, 

7338 method_idx, 

7339 argc, 

7340 args) 

7341 else: # pragma: no cover 

7342 # Set active channel, and capture mask to restore. 

7343 channel_mask = library.MagickSetImageChannelMask(self.wand, 

7344 channel_mask) 

7345 r = library.MagickSparseColorImage(self.wand, 

7346 method_idx, 

7347 argc, 

7348 args) 

7349 # Restore original state of channels 

7350 library.MagickSetImageChannelMask(self.wand, channel_mask) 

7351 return r 

7352 

7353 @manipulative 

7354 @trap_exception 

7355 def splice(self, width=None, height=None, x=None, y=None): 

7356 """Partitions image by splicing a ``width`` x ``height`` rectangle at 

7357 (``x``, ``y``) offset coordinate. The space inserted will be replaced 

7358 by the :attr:`background_color` value. 

7359 

7360 :param width: number of pixel columns. 

7361 :type width: :class:`numbers.Integral` 

7362 :param height: number of pixel rows. 

7363 :type height: :class:`numbers.Integral` 

7364 :param x: offset on the X-axis. 

7365 :type x: :class:`numbers.Integral` 

7366 :param y: offset on the Y-axis. 

7367 :type y: :class:`numbers.Integral` 

7368 

7369 .. versionadded:: 0.5.3 

7370 """ 

7371 assertions.assert_integer(width=width, height=height, x=x, y=y) 

7372 return library.MagickSpliceImage(self.wand, width, height, x, y) 

7373 

7374 @manipulative 

7375 @trap_exception 

7376 def spread(self, radius=0.0, method='undefined'): 

7377 """Randomly displace pixels within a defined radius. 

7378 

7379 :param radius: Distance a pixel can be displaced from source. Default 

7380 value is ``0.0``, which will allow ImageMagick to auto 

7381 select a radius. 

7382 :type radius: :class:`numbers.Real` 

7383 :param method: Interpolation method. Only available with ImageMagick-7. 

7384 See :const:`PIXEL_INTERPOLATE_METHODS`. 

7385 

7386 .. versionadded:: 0.5.3 

7387 

7388 .. versionchanged:: 0.5.7 

7389 Added default value to ``radius``. 

7390 """ 

7391 assertions.assert_real(radius=radius) 

7392 assertions.string_in_list(PIXEL_INTERPOLATE_METHODS, 

7393 'wand.image.PIXEL_INTERPOLATE_METHODS', 

7394 method=method) 

7395 method_idx = PIXEL_INTERPOLATE_METHODS.index(method) 

7396 if MAGICK_VERSION_NUMBER < 0x700: 

7397 r = library.MagickSpreadImage(self.wand, radius) 

7398 else: # pragma: no cover 

7399 r = library.MagickSpreadImage(self.wand, method_idx, radius) 

7400 return r 

7401 

7402 @manipulative 

7403 @trap_exception 

7404 def statistic(self, stat='undefined', width=None, height=None, 

7405 channel=None): 

7406 """Replace each pixel with the statistic results from neighboring pixel 

7407 values. The ``width`` & ``height`` defines the size, or aperture, of 

7408 the neighboring pixels. 

7409 

7410 :param stat: The type of statistic to calculate. See 

7411 :const:`STATISTIC_TYPES`. 

7412 :type stat: :class:`basestring` 

7413 :param width: The size of neighboring pixels on the X-axis. 

7414 :type width: :class:`numbers.Integral` 

7415 :param height: The size of neighboring pixels on the Y-axis. 

7416 :type height: :class:`numbers.Integral` 

7417 :param channel: Optional color channel to target. See 

7418 :const:`CHANNELS` 

7419 :type channel: :class:`basestring` 

7420 

7421 .. versionadded:: 0.5.3 

7422 

7423 .. versionchanged:: 0.5.5 

7424 Added optional ``channel`` argument. 

7425 """ 

7426 assertions.string_in_list(STATISTIC_TYPES, 

7427 'wand.image.STATISTIC_TYPES', 

7428 statistic=stat) 

7429 assertions.assert_integer(width=width, height=height) 

7430 stat_idx = STATISTIC_TYPES.index(stat) 

7431 if channel is None: 

7432 r = library.MagickStatisticImage(self.wand, stat_idx, 

7433 width, height) 

7434 else: 

7435 channel_ch = self._channel_to_mask(channel) 

7436 if MAGICK_VERSION_NUMBER < 0x700: 

7437 r = library.MagickStatisticImageChannel(self.wand, 

7438 channel_ch, 

7439 stat_idx, 

7440 width, 

7441 height) 

7442 else: # pragma: no cover 

7443 mask = library.MagickSetImageChannelMask(self.wand, 

7444 channel_ch) 

7445 r = library.MagickStatisticImage(self.wand, stat_idx, 

7446 width, height) 

7447 library.MagickSetImageChannelMask(self.wand, mask) 

7448 return r 

7449 

7450 @manipulative 

7451 @trap_exception 

7452 def stegano(self, watermark, offset=0): 

7453 """Hide a digital watermark of an image within the image. 

7454 

7455 .. code-block:: python 

7456 

7457 from wand.image import Image 

7458 

7459 # Embed watermark 

7460 with Image(filename='source.png') as img: 

7461 with Image(filename='gray_watermark.png') as watermark: 

7462 print('watermark size (for recovery)', watermark.size) 

7463 img.stegano(watermark) 

7464 img.save(filename='public.png') 

7465 

7466 # Recover watermark 

7467 with Image(width=w, height=h, pseudo='stegano:public.png') as img: 

7468 img.save(filename='recovered_watermark.png') 

7469 

7470 :param watermark: Image to hide within image. 

7471 :type watermark: :class:`wand.image.Image` 

7472 :param offset: Start embedding image after a number of pixels. 

7473 :type offset: :class:`numbers.Integral` 

7474 

7475 .. versionadded:: 0.5.4 

7476 """ 

7477 if not isinstance(watermark, BaseImage): 

7478 raise TypeError('Watermark image must be in instance of ' 

7479 'wand.image.Image, not ' + repr(watermark)) 

7480 assertions.assert_integer(offset=offset) 

7481 new_wand = library.MagickSteganoImage(self.wand, watermark.wand, 

7482 offset) 

7483 if new_wand: 

7484 self.wand = new_wand 

7485 self.reset_sequence() 

7486 return bool(new_wand) 

7487 

7488 @trap_exception 

7489 def strip(self): 

7490 """Strips an image of all profiles and comments. 

7491 

7492 .. versionadded:: 0.2.0 

7493 """ 

7494 return library.MagickStripImage(self.wand) 

7495 

7496 @manipulative 

7497 @trap_exception 

7498 def swirl(self, degree=0.0, method="undefined"): 

7499 """Swirls pixels around the center of the image. The larger the degree 

7500 the more pixels will be effected. 

7501 

7502 :param degree: Defines the amount of pixels to be effected. Value 

7503 between ``-360.0`` and ``360.0``. 

7504 :type degree: :class:`numbers.Real` 

7505 :param method: Controls interpolation of the effected pixels. Only 

7506 available for ImageMagick-7. See 

7507 :const:`PIXEL_INTERPOLATE_METHODS`. 

7508 :type method: :class:`basestring` 

7509 

7510 .. versionadded:: 0.5.7 

7511 """ 

7512 assertions.assert_real(degree=degree) 

7513 if MAGICK_VERSION_NUMBER < 0x700: 

7514 r = library.MagickSwirlImage(self.wand, degree) 

7515 else: # pragma: no cover 

7516 assertions.string_in_list(PIXEL_INTERPOLATE_METHODS, 

7517 'wand.image.PIXEL_INTERPOLATE_METHODS', 

7518 method=method) 

7519 method_idx = PIXEL_INTERPOLATE_METHODS.index(method) 

7520 r = library.MagickSwirlImage(self.wand, degree, method_idx) 

7521 return r 

7522 

7523 @manipulative 

7524 @trap_exception 

7525 def texture(self, tile): 

7526 """Repeat tile-image across the width & height of the image. 

7527 

7528 .. code:: python 

7529 

7530 from wand.image import Image 

7531 

7532 with Image(width=100, height=100) as canvas: 

7533 with Image(filename='tile.png') as tile: 

7534 canvas.texture(tile) 

7535 canvas.save(filename='output.png') 

7536 

7537 :param tile: image to repeat across canvas. 

7538 :type tile: :class:`Image <wand.image.BaseImage>` 

7539 

7540 .. versionadded:: 0.5.4 

7541 """ 

7542 if not isinstance(tile, BaseImage): 

7543 raise TypeError('Tile image must be an instance of ' 

7544 'wand.image.Image, not ' + repr(tile)) 

7545 r = library.MagickTextureImage(self.wand, tile.wand) 

7546 if r: 

7547 self.wand = r 

7548 return bool(r) 

7549 

7550 @manipulative 

7551 @trap_exception 

7552 def threshold(self, threshold=0.5, channel=None): 

7553 """Changes the value of individual pixels based on the intensity 

7554 of each pixel compared to threshold. The result is a high-contrast, 

7555 two color image. It manipulates the image in place. 

7556 

7557 :param threshold: threshold as a factor of quantum. A normalized float 

7558 between ``0.0`` and ``1.0``. 

7559 :type threshold: :class:`numbers.Real` 

7560 :param channel: the channel type. available values can be found 

7561 in the :const:`CHANNELS` mapping. If ``None``, 

7562 threshold all channels. 

7563 :type channel: :class:`basestring` 

7564 

7565 .. versionadded:: 0.3.10 

7566 

7567 """ 

7568 assertions.assert_real(threshold=threshold) 

7569 threshold *= self.quantum_range + 1 

7570 if channel is None: 

7571 r = library.MagickThresholdImage(self.wand, threshold) 

7572 else: 

7573 ch_const = self._channel_to_mask(channel) 

7574 r = library.MagickThresholdImageChannel( 

7575 self.wand, ch_const, 

7576 threshold 

7577 ) 

7578 return r 

7579 

7580 @manipulative 

7581 @trap_exception 

7582 def thumbnail(self, width=None, height=None): 

7583 """Changes the size of an image to the given dimensions and removes any 

7584 associated profiles. The goal is to produce small low cost thumbnail 

7585 images suited for display on the web. 

7586 

7587 :param width: the width in the scaled image. default is the original 

7588 width 

7589 :type width: :class:`numbers.Integral` 

7590 :param height: the height in the scaled image. default is the original 

7591 height 

7592 :type height: :class:`numbers.Integral` 

7593 

7594 .. versionadded:: 0.5.4 

7595 """ 

7596 if width is None: 

7597 width = self.width 

7598 if height is None: 

7599 height = self.height 

7600 assertions.assert_unsigned_integer(width=width, height=height) 

7601 return library.MagickThumbnailImage(self.wand, width, height) 

7602 

7603 @manipulative 

7604 @trap_exception 

7605 def tint(self, color=None, alpha=None): 

7606 """Applies a color vector to each pixel in the image. 

7607 

7608 :param color: Color to calculate midtone. 

7609 :type color: :class:`~wand.color.Color` 

7610 :param alpha: Determine how to blend. 

7611 :type alpha: :class:`~wand.color.Color` 

7612 

7613 .. versionadded:: 0.5.3 

7614 """ 

7615 if isinstance(color, string_type): 

7616 color = Color(color) 

7617 if isinstance(alpha, string_type): 

7618 alpha = Color(alpha) 

7619 assertions.assert_color(color=color, alpha=alpha) 

7620 with color: 

7621 with alpha: 

7622 r = library.MagickTintImage(self.wand, 

7623 color.resource, 

7624 alpha.resource) 

7625 return r 

7626 

7627 @manipulative 

7628 @trap_exception 

7629 def transform(self, crop='', resize=''): 

7630 """Transforms the image using :c:func:`MagickTransformImage`, 

7631 which is a convenience function accepting geometry strings to 

7632 perform cropping and resizing. Cropping is performed first, 

7633 followed by resizing. Either or both arguments may be omitted 

7634 or given an empty string, in which case the corresponding action 

7635 will not be performed. Geometry specification strings are 

7636 defined as follows: 

7637 

7638 A geometry string consists of a size followed by an optional offset. 

7639 The size is specified by one of the options below, 

7640 where **bold** terms are replaced with appropriate integer values: 

7641 

7642 **scale**\\ ``%`` 

7643 Height and width both scaled by specified percentage 

7644 

7645 **scale-x**\\ ``%x``\\ \\ **scale-y**\\ ``%`` 

7646 Height and width individually scaled by specified percentages. 

7647 Only one % symbol is needed. 

7648 

7649 **width** 

7650 Width given, height automagically selected to preserve aspect ratio. 

7651 

7652 ``x``\\ \\ **height** 

7653 Height given, width automagically selected to preserve aspect ratio. 

7654 

7655 **width**\\ ``x``\\ **height** 

7656 Maximum values of width and height given; aspect ratio preserved. 

7657 

7658 **width**\\ ``x``\\ **height**\\ ``!`` 

7659 Width and height emphatically given; original aspect ratio ignored. 

7660 

7661 **width**\\ ``x``\\ **height**\\ ``>`` 

7662 Shrinks images with dimension(s) larger than the corresponding 

7663 width and/or height dimension(s). 

7664 

7665 **width**\\ ``x``\\ **height**\\ ``<`` 

7666 Enlarges images with dimensions smaller than the corresponding 

7667 width and/or height dimension(s). 

7668 

7669 **area**\\ ``@`` 

7670 Resize image to have the specified area in pixels. 

7671 Aspect ratio is preserved. 

7672 

7673 The offset, which only applies to the cropping geometry string, 

7674 is given by ``{+-}``\\ **x**\\ ``{+-}``\\ **y**\\ , that is, 

7675 one plus or minus sign followed by an **x** offset, 

7676 followed by another plus or minus sign, followed by a **y** offset. 

7677 Offsets are in pixels from the upper left corner of the image. 

7678 Negative offsets will cause the corresponding number of pixels to 

7679 be removed from the right or bottom edge of the image, meaning the 

7680 cropped size will be the computed size minus the absolute value 

7681 of the offset. 

7682 

7683 For example, if you want to crop your image to 300x300 pixels 

7684 and then scale it by 2x for a final size of 600x600 pixels, 

7685 you can call:: 

7686 

7687 image.transform('300x300', '200%') 

7688 

7689 This method is a fairly thin wrapper for the C API, and does not 

7690 perform any additional checking of the parameters except insofar as 

7691 verifying that they are of the correct type. Thus, like the C 

7692 API function, the method is very permissive in terms of what 

7693 it accepts for geometry strings; unrecognized strings and 

7694 trailing characters will be ignored rather than raising an error. 

7695 

7696 :param crop: A geometry string defining a subregion of the image 

7697 to crop to 

7698 :type crop: :class:`basestring` 

7699 :param resize: A geometry string defining the final size of the image 

7700 :type resize: :class:`basestring` 

7701 

7702 .. seealso:: 

7703 

7704 `ImageMagick Geometry Specifications`__ 

7705 Cropping and resizing geometry for the ``transform`` method are 

7706 specified according to ImageMagick's geometry string format. 

7707 The ImageMagick documentation provides more information about 

7708 geometry strings. 

7709 

7710 __ http://www.imagemagick.org/script/command-line-processing.php#geometry 

7711 

7712 .. versionadded:: 0.2.2 

7713 .. versionchanged:: 0.5.0 

7714 Will call :meth:`crop()` followed by :meth:`resize()` in the event 

7715 that :c:func:`MagickTransformImage` is not available. 

7716 .. deprecated:: 0.6.0 

7717 Use :meth:`crop()` and :meth:`resize()` instead. 

7718 """ # noqa 

7719 # Check that the values given are the correct types. ctypes will do 

7720 # this automatically, but we can make the error message more friendly 

7721 # here. 

7722 assertions.assert_string(crop=crop, resize=resize) 

7723 # Also verify that only ASCII characters are included 

7724 try: 

7725 crop = crop.encode('ascii') 

7726 except UnicodeEncodeError: 

7727 raise ValueError('crop must only contain ascii-encodable ' + 

7728 'characters.') 

7729 try: 

7730 resize = resize.encode('ascii') 

7731 except UnicodeEncodeError: 

7732 raise ValueError('resize must only contain ascii-encodable ' + 

7733 'characters.') 

7734 if not library.MagickTransformImage: # pragma: no cover 

7735 # Method removed from ImageMagick-7. 

7736 if crop: 

7737 x = ctypes.c_ssize_t(0) 

7738 y = ctypes.c_ssize_t(0) 

7739 width = ctypes.c_size_t(self.width) 

7740 height = ctypes.c_size_t(self.height) 

7741 libmagick.GetGeometry(crop, 

7742 ctypes.byref(x), 

7743 ctypes.byref(y), 

7744 ctypes.byref(width), 

7745 ctypes.byref(height)) 

7746 self.crop(top=y.value, 

7747 left=x.value, 

7748 width=width.value, 

7749 height=height.value, 

7750 reset_coords=False) 

7751 if resize: 

7752 x = ctypes.c_ssize_t() 

7753 y = ctypes.c_ssize_t() 

7754 width = ctypes.c_size_t(self.width) 

7755 height = ctypes.c_size_t(self.height) 

7756 libmagick.ParseMetaGeometry(resize, 

7757 ctypes.byref(x), 

7758 ctypes.byref(y), 

7759 ctypes.byref(width), 

7760 ctypes.byref(height)) 

7761 self.resize(width=width.value, 

7762 height=height.value) 

7763 # Both `BaseImage.crop` & `BaseImage.resize` will handle 

7764 # animation & error handling, so we can stop here. 

7765 return True 

7766 if self.animation: 

7767 new_wand = library.MagickCoalesceImages(self.wand) 

7768 length = len(self.sequence) 

7769 for i in xrange(length): 

7770 library.MagickSetIteratorIndex(new_wand, i) 

7771 if i: 

7772 library.MagickAddImage( 

7773 new_wand, 

7774 library.MagickTransformImage(new_wand, crop, resize) 

7775 ) 

7776 else: 

7777 new_wand = library.MagickTransformImage(new_wand, 

7778 crop, 

7779 resize) 

7780 self.sequence.instances = [] 

7781 else: 

7782 new_wand = library.MagickTransformImage(self.wand, crop, resize) 

7783 if new_wand: 

7784 self.wand = new_wand 

7785 return bool(new_wand) 

7786 

7787 @manipulative 

7788 @trap_exception 

7789 def transform_colorspace(self, colorspace_type): 

7790 """Transform image's colorspace. 

7791 

7792 :param colorspace_type: colorspace_type. available value can be found 

7793 in the :const:`COLORSPACE_TYPES` 

7794 :type colorspace_type: :class:`basestring` 

7795 

7796 .. versionadded:: 0.4.2 

7797 

7798 """ 

7799 assertions.string_in_list(COLORSPACE_TYPES, 

7800 'wand.image.COLORSPACE_TYPES', 

7801 colorspace=colorspace_type) 

7802 return library.MagickTransformImageColorspace( 

7803 self.wand, 

7804 COLORSPACE_TYPES.index(colorspace_type) 

7805 ) 

7806 

7807 @manipulative 

7808 @trap_exception 

7809 def transparent_color(self, color, alpha, fuzz=0, invert=False): 

7810 """Makes the color ``color`` a transparent color with a tolerance of 

7811 fuzz. The ``alpha`` parameter specify the transparency level and the 

7812 parameter ``fuzz`` specify the tolerance. 

7813 

7814 :param color: The color that should be made transparent on the image, 

7815 color object 

7816 :type color: :class:`wand.color.Color` 

7817 :param alpha: the level of transparency: 1.0 is fully opaque 

7818 and 0.0 is fully transparent. 

7819 :type alpha: :class:`numbers.Real` 

7820 :param fuzz: By default target must match a particular pixel color 

7821 exactly. However, in many cases two colors may differ 

7822 by a small amount. The fuzz member of image defines how 

7823 much tolerance is acceptable to consider two colors as the 

7824 same. For example, set fuzz to 10 and the color red at 

7825 intensities of 100 and 102 respectively are now 

7826 interpreted as the same color for the color. 

7827 :type fuzz: :class:`numbers.Integral` 

7828 :param invert: Boolean to tell to paint the inverse selection. 

7829 :type invert: :class:`bool` 

7830 

7831 .. versionadded:: 0.3.0 

7832 

7833 """ 

7834 assertions.assert_real(alpha=alpha) 

7835 assertions.assert_integer(fuzz=fuzz) 

7836 if isinstance(color, string_type): 

7837 color = Color(color) 

7838 assertions.assert_color(color=color) 

7839 with color: 

7840 r = library.MagickTransparentPaintImage(self.wand, color.resource, 

7841 alpha, fuzz, invert) 

7842 return r 

7843 

7844 @manipulative 

7845 def transparentize(self, transparency): 

7846 """Makes the image transparent by subtracting some percentage of 

7847 the black color channel. The ``transparency`` parameter specifies the 

7848 percentage. 

7849 

7850 :param transparency: the percentage fade that should be performed on 

7851 the image, from 0.0 to 1.0 

7852 :type transparency: :class:`numbers.Real` 

7853 

7854 .. versionadded:: 0.2.0 

7855 

7856 """ 

7857 if transparency: 

7858 t = ctypes.c_double(float(self.quantum_range * 

7859 float(transparency))) 

7860 if t.value > self.quantum_range or t.value < 0: 

7861 raise ValueError('transparency must be a numbers.Real value ' + 

7862 'between 0.0 and 1.0') 

7863 # Set the wand to image zero, in case there are multiple images 

7864 # in it 

7865 library.MagickSetIteratorIndex(self.wand, 0) 

7866 # Change the pixel representation of the image 

7867 # to RGB with an alpha channel 

7868 if MAGICK_VERSION_NUMBER < 0x700: 

7869 image_type = 'truecolormatte' 

7870 else: # pragma: no cover 

7871 image_type = 'truecoloralpha' 

7872 library.MagickSetImageType(self.wand, 

7873 IMAGE_TYPES.index(image_type)) 

7874 # Perform the black channel subtraction 

7875 self.evaluate(operator='subtract', 

7876 value=t.value, 

7877 channel='opacity') 

7878 self.raise_exception() 

7879 

7880 @manipulative 

7881 @trap_exception 

7882 def transpose(self): 

7883 """Creates a vertical mirror image by reflecting the pixels around 

7884 the central x-axis while rotating them 90-degrees. 

7885 

7886 .. versionadded:: 0.4.1 

7887 """ 

7888 return library.MagickTransposeImage(self.wand) 

7889 

7890 @manipulative 

7891 @trap_exception 

7892 def transverse(self): 

7893 """Creates a horizontal mirror image by reflecting the pixels around 

7894 the central y-axis while rotating them 270-degrees. 

7895 

7896 .. versionadded:: 0.4.1 

7897 """ 

7898 return library.MagickTransverseImage(self.wand) 

7899 

7900 @manipulative 

7901 @trap_exception 

7902 def trim(self, color=None, fuzz=0.0, reset_coords=False): 

7903 """Remove solid border from image. Uses top left pixel as a guide 

7904 by default, or you can also specify the ``color`` to remove. 

7905 

7906 :param color: the border color to remove. 

7907 if it's omitted top left pixel is used by default 

7908 :type color: :class:`~wand.color.Color` 

7909 :param fuzz: Defines how much tolerance is acceptable to consider 

7910 two colors as the same. Value can be between ``0.0``, 

7911 and :attr:`quantum_range`. 

7912 :type fuzz: :class:`numbers.Real` 

7913 :param reset_coords: Reset coordinates after triming image. Default 

7914 ``False``. 

7915 :type reset_coords: :class:`bool` 

7916 

7917 

7918 .. versionadded:: 0.2.1 

7919 

7920 .. versionchanged:: 0.3.0 

7921 Optional ``color`` and ``fuzz`` parameters. 

7922 

7923 .. versionchanged:: 0.5.2 

7924 The ``color`` parameter may except color-compliant strings. 

7925 

7926 .. versionchanged:: 0.6.0 

7927 Optional ``reset_coords`` parameter added. 

7928 """ 

7929 if color is None: 

7930 color = self[0, 0] 

7931 elif isinstance(color, string_type): 

7932 color = Color(color) 

7933 assertions.assert_color(color=color) 

7934 assertions.assert_real(fuzz=fuzz) 

7935 assertions.assert_bool(reset_coords=reset_coords) 

7936 with color: 

7937 self.border(color, 1, 1, compose="copy") 

7938 r = library.MagickTrimImage(self.wand, fuzz) 

7939 if reset_coords: 

7940 self.reset_coords() 

7941 else: 

7942 # Re-calculate page coordinates as we added a 1x1 border before 

7943 # applying the trim. 

7944 adjusted_coords = list(self.page) 

7945 # Width & height are unsigned. 

7946 adjusted_coords[0] = max(adjusted_coords[0] - 2, 0) 

7947 adjusted_coords[1] = max(adjusted_coords[1] - 2, 0) 

7948 # X & Y are signed. It's common for page offsets to be negative. 

7949 adjusted_coords[2] -= 1 

7950 adjusted_coords[3] -= 1 

7951 self.page = adjusted_coords 

7952 return r 

7953 

7954 @manipulative 

7955 @trap_exception 

7956 def unique_colors(self): 

7957 """Discards all duplicate pixels, and rebuilds the image 

7958 as a single row. 

7959 

7960 .. versionadded:: 0.5.0 

7961 """ 

7962 return library.MagickUniqueImageColors(self.wand) 

7963 

7964 @manipulative 

7965 @trap_exception 

7966 def unsharp_mask(self, radius=0.0, sigma=1.0, amount=1.0, threshold=0.0, 

7967 channel=None): 

7968 """Sharpens the image using unsharp mask filter. We convolve the image 

7969 with a Gaussian operator of the given ``radius`` and standard deviation 

7970 (``sigma``). For reasonable results, ``radius`` should be larger than 

7971 ``sigma``. Use a radius of 0 and :meth:`unsharp_mask()` selects 

7972 a suitable radius for you. 

7973 

7974 :param radius: the radius of the Gaussian, in pixels, 

7975 not counting the center pixel 

7976 :type radius: :class:`numbers.Real` 

7977 :param sigma: the standard deviation of the Gaussian, in pixels 

7978 :type sigma: :class:`numbers.Real` 

7979 :param amount: the percentage of the difference between the original 

7980 and the blur image that is added back into the original 

7981 :type amount: :class:`numbers.Real` 

7982 :param threshold: the threshold in pixels needed to apply 

7983 the difference amount. 

7984 :type threshold: :class:`numbers.Real` 

7985 :param channel: Optional color channel to target. See 

7986 :const:`CHANNELS` 

7987 :type channel: :class:`basestring` 

7988 

7989 .. versionadded:: 0.3.4 

7990 

7991 .. versionchanged:: 0.5.5 

7992 Added optional ``channel`` argument. 

7993 

7994 .. versionchanged:: 0.5.7 

7995 Added default values to match CLI behavior. 

7996 """ 

7997 assertions.assert_real(radius=radius, sigma=sigma, 

7998 amount=amount, threshold=threshold) 

7999 if channel is None: 

8000 r = library.MagickUnsharpMaskImage(self.wand, radius, sigma, 

8001 amount, threshold) 

8002 else: 

8003 channel_ch = self._channel_to_mask(channel) 

8004 if MAGICK_VERSION_NUMBER < 0x700: 

8005 r = library.MagickUnsharpMaskImageChannel( 

8006 self.wand, channel_ch, radius, sigma, amount, threshold 

8007 ) 

8008 else: # pragma: no cover 

8009 mask = library.MagickSetImageChannelMask(self.wand, channel_ch) 

8010 r = library.MagickUnsharpMaskImage(self.wand, radius, sigma, 

8011 amount, threshold) 

8012 library.MagickSetImageChannelMask(self.wand, mask) 

8013 return r 

8014 

8015 @manipulative 

8016 @trap_exception 

8017 def vignette(self, radius=0.0, sigma=0.0, x=0, y=0): 

8018 """Creates a soft vignette style effect on the image. 

8019 

8020 :param radius: the radius of the Gaussian blur effect. 

8021 :type radius: :class:`numbers.Real` 

8022 :param sigma: the standard deviation of the Gaussian effect. 

8023 :type sigma: :class:`numbers.Real` 

8024 :param x: Number of pixels to offset inward from the top & bottom of 

8025 the image before drawing effect. 

8026 :type x: :class:`numbers.Integral` 

8027 :param y: Number of pixels to offset inward from the left & right of 

8028 the image before drawing effect. 

8029 :type y: :class:`numbers.Integral` 

8030 

8031 .. versionadded:: 0.5.2 

8032 """ 

8033 assertions.assert_real(radius=radius, sigma=sigma) 

8034 return library.MagickVignetteImage(self.wand, radius, sigma, x, y) 

8035 

8036 @manipulative 

8037 def watermark(self, image, transparency=0.0, left=0, top=0): 

8038 """Transparentized the supplied ``image`` and places it over the 

8039 current image, with the top left corner of ``image`` at coordinates 

8040 ``left``, ``top`` of the current image. The dimensions of the 

8041 current image are not changed. 

8042 

8043 :param image: the image placed over the current image 

8044 :type image: :class:`wand.image.Image` 

8045 :param transparency: the percentage fade that should be performed on 

8046 the image, from 0.0 to 1.0 

8047 :type transparency: :class:`numbers.Real` 

8048 :param left: the x-coordinate where `image` will be placed 

8049 :type left: :class:`numbers.Integral` 

8050 :param top: the y-coordinate where `image` will be placed 

8051 :type top: :class:`numbers.Integral` 

8052 

8053 .. versionadded:: 0.2.0 

8054 

8055 """ 

8056 with image.clone() as watermark_image: 

8057 watermark_image.transparentize(transparency) 

8058 watermark_image.clamp() 

8059 self.composite(watermark_image, left=left, top=top) 

8060 self.raise_exception() 

8061 

8062 @manipulative 

8063 @trap_exception 

8064 def wave(self, amplitude=0.0, wave_length=0.0, method='undefined'): 

8065 """Creates a ripple effect within the image. 

8066 

8067 :param amplitude: height of wave form. 

8068 :type amplitude: :class:`numbers.Real` 

8069 :param wave_length: width of wave form. 

8070 :type wave_length: :class:`numbers.Real` 

8071 :param method: pixel interpolation method. Only available with 

8072 ImageMagick-7. See :const:`PIXEL_INTERPOLATE_METHODS` 

8073 :type method: :class:`basestring` 

8074 

8075 .. versionadded:: 0.5.2 

8076 """ 

8077 assertions.assert_real(amplitude=amplitude, wave_length=wave_length) 

8078 assertions.string_in_list(PIXEL_INTERPOLATE_METHODS, 

8079 'wand.image.PIXEL_INTERPOLATE_METHODS', 

8080 method=method) 

8081 if MAGICK_VERSION_NUMBER < 0x700: 

8082 r = library.MagickWaveImage(self.wand, amplitude, wave_length) 

8083 else: # pragma: no cover 

8084 method_idx = PIXEL_INTERPOLATE_METHODS.index(method) 

8085 r = library.MagickWaveImage(self.wand, amplitude, wave_length, 

8086 method_idx) 

8087 return r 

8088 

8089 @manipulative 

8090 @trap_exception 

8091 def wavelet_denoise(self, threshold=0.0, softness=0.0): 

8092 """Removes noise by applying a `wavelet transform`_. 

8093 

8094 .. _`wavelet transform`: 

8095 https://en.wikipedia.org/wiki/Wavelet_transform 

8096 

8097 .. warning:: 

8098 

8099 This class method is only available with ImageMagick 7.0.8-41, or 

8100 greater. 

8101 

8102 :param threshold: Smoothing limit. 

8103 :type threshold: :class:`numbers.Real` 

8104 :param softness: Attenuate of the smoothing threshold. 

8105 :type softness: :class:`numbers.Real` 

8106 :raises WandLibraryVersionError: If system's version of ImageMagick 

8107 does not support this method. 

8108 

8109 .. versionadded:: 0.5.5 

8110 """ 

8111 if library.MagickWaveletDenoiseImage is None: 

8112 msg = 'Method requires ImageMagick version 7.0.8-41 or greater.' 

8113 raise WandLibraryVersionError(msg) 

8114 assertions.assert_real(threshold=threshold, softness=softness) 

8115 if 0.0 < threshold <= 1.0: 

8116 threshold *= self.quantum_range 

8117 if 0.0 < softness <= 1.0: 

8118 softness *= self.quantum_range 

8119 return library.MagickWaveletDenoiseImage(self.wand, threshold, 

8120 softness) 

8121 

8122 @manipulative 

8123 @trap_exception 

8124 def white_threshold(self, threshold): 

8125 """Forces all pixels above a given color as white. Leaves pixels 

8126 below threshold unaltered. 

8127 

8128 :param threshold: Color to be referenced as a threshold. 

8129 :type threshold: :class:`Color` 

8130 

8131 .. versionadded:: 0.5.2 

8132 """ 

8133 if isinstance(threshold, string_type): 

8134 threshold = Color(threshold) 

8135 assertions.assert_color(threshold=threshold) 

8136 with threshold: 

8137 r = library.MagickWhiteThresholdImage(self.wand, 

8138 threshold.resource) 

8139 return r 

8140 

8141 @trap_exception 

8142 def write_mask(self, clip_mask=None): 

8143 """Sets the write mask which prevents pixel-value updates to the image. 

8144 Call this method with a ``None`` argument to clear any previously set 

8145 masks. 

8146 

8147 .. warning:: 

8148 This method is only available with ImageMagick-7. 

8149 

8150 :param clip_mask: Image to reference as blend mask. 

8151 :type clip_mask: :class:`BaseImage` 

8152 

8153 .. versionadded:: 0.5.7 

8154 """ 

8155 r = False 

8156 WritePixelMask = 0x000002 

8157 if library.MagickSetImageMask is None: 

8158 raise WandLibraryVersionError('Method requires ImageMagick-7.') 

8159 else: # pragma: no cover 

8160 if clip_mask is None: 

8161 r = library.MagickSetImageMask(self.wand, WritePixelMask, None) 

8162 elif isinstance(clip_mask, BaseImage): 

8163 r = library.MagickSetImageMask(self.wand, WritePixelMask, 

8164 clip_mask.wand) 

8165 return r 

8166 

8167 

8168class Image(BaseImage): 

8169 """An image object. 

8170 

8171 :param image: makes an exact copy of the ``image`` 

8172 :type image: :class:`Image` 

8173 :param blob: opens an image of the ``blob`` byte array 

8174 :type blob: :class:`bytes` 

8175 :param file: opens an image of the ``file`` object 

8176 :type file: file object 

8177 :param filename: opens an image of the ``filename`` string. Additional 

8178 :ref:`read_mods` are supported. 

8179 :type filename: :class:`basestring` 

8180 :param format: forces filename to buffer. ``format`` to help 

8181 ImageMagick detect the file format. Used only in 

8182 ``blob`` or ``file`` cases 

8183 :type format: :class:`basestring` 

8184 :param width: the width of new blank image or an image loaded from raw 

8185 data. 

8186 :type width: :class:`numbers.Integral` 

8187 :param height: the height of new blank image or an image loaded from 

8188 raw data. 

8189 :type height: :class:`numbers.Integral` 

8190 :param depth: the depth used when loading raw data. 

8191 :type depth: :class:`numbers.Integral` 

8192 :param background: an optional background color. 

8193 default is transparent 

8194 :type background: :class:`wand.color.Color` 

8195 :param resolution: set a resolution value (dpi), 

8196 useful for vectorial formats (like pdf) 

8197 :type resolution: :class:`collections.abc.Sequence`, 

8198 :Class:`numbers.Integral` 

8199 :param colorspace: sets the stack's default colorspace value before 

8200 reading any images. 

8201 See :const:`COLORSPACE_TYPES`. 

8202 :type colorspace: :class:`basestring`, 

8203 :param units: paired with ``resolution`` for defining an image's pixel 

8204 density. See :const:`UNIT_TYPES`. 

8205 :type units: :class:`basestring` 

8206 

8207 .. versionadded:: 0.1.5 

8208 The ``file`` parameter. 

8209 

8210 .. versionadded:: 0.1.1 

8211 The ``blob`` parameter. 

8212 

8213 .. versionadded:: 0.2.1 

8214 The ``format`` parameter. 

8215 

8216 .. versionadded:: 0.2.2 

8217 The ``width``, ``height``, ``background`` parameters. 

8218 

8219 .. versionadded:: 0.3.0 

8220 The ``resolution`` parameter. 

8221 

8222 .. versionadded:: 0.4.2 

8223 The ``depth`` parameter. 

8224 

8225 .. versionchanged:: 0.4.2 

8226 The ``depth``, ``width`` and ``height`` parameters can be used 

8227 with the ``filename``, ``file`` and ``blob`` parameters to load 

8228 raw pixel data. 

8229 

8230 .. versionadded:: 0.5.0 

8231 The ``pseudo`` parameter. 

8232 

8233 .. versionchanged:: 0.5.4 

8234 Read constructor no longer sets "transparent" background by default. 

8235 Use the ``background`` paramater to specify canvas color when reading 

8236 in image. 

8237 

8238 .. versionchanged:: 0.5.7 

8239 Added the ``colorspace`` & ``units`` parameter. 

8240 

8241 .. describe:: [left:right, top:bottom] 

8242 

8243 Crops the image by its ``left``, ``right``, ``top`` and ``bottom``, 

8244 and then returns the cropped one. :: 

8245 

8246 with img[100:200, 150:300] as cropped: 

8247 # manipulated the cropped image 

8248 pass 

8249 

8250 Like other subscriptable objects, default is 0 or its width/height:: 

8251 

8252 img[:, :] #--> just clone 

8253 img[:100, 200:] #--> equivalent to img[0:100, 200:img.height] 

8254 

8255 Negative integers count from the end (width/height):: 

8256 

8257 img[-70:-50, -20:-10] 

8258 #--> equivalent to img[width-70:width-50, height-20:height-10] 

8259 

8260 :returns: the cropped image 

8261 :rtype: :class:`Image` 

8262 

8263 .. versionadded:: 0.1.2 

8264 

8265 """ 

8266 

8267 #: (:class:`ArtifactTree`) A dict mapping to image artifacts. 

8268 #: Similar to :attr:`metadata`, but used to alter behavior of various 

8269 #: internal operations. 

8270 #: 

8271 #: .. versionadded:: 0.5.0 

8272 artifacts = None 

8273 

8274 #: (:class:`ChannelImageDict`) The mapping of separated channels 

8275 #: from the image. :: 

8276 #: 

8277 #: with image.channel_images['red'] as red_image: 

8278 #: display(red_image) 

8279 channel_images = None 

8280 

8281 #: (:class:`ChannelDepthDict`) The mapping of channels to their depth. 

8282 #: Read only. 

8283 #: 

8284 #: .. versionadded:: 0.3.0 

8285 channel_depths = None 

8286 

8287 #: (:class:`Metadata`) The metadata mapping of the image. Read only. 

8288 #: 

8289 #: .. versionadded:: 0.3.0 

8290 metadata = None 

8291 

8292 #: (:class:`ProfileDict`) The mapping of image profiles. 

8293 #: 

8294 #: .. versionadded:: 0.5.1 

8295 profiles = None 

8296 

8297 def __init__(self, image=None, blob=None, file=None, filename=None, 

8298 format=None, width=None, height=None, depth=None, 

8299 background=None, resolution=None, pseudo=None, 

8300 colorspace=None, units=None): 

8301 new_args = width, height, background, depth 

8302 open_args = blob, file, filename 

8303 if any(a is not None for a in new_args) and image is not None: 

8304 raise TypeError("blank image parameters can't be used with image " 

8305 'parameter') 

8306 if sum(a is not None for a in open_args + (image,)) > 1: 

8307 raise TypeError(', '.join(open_args) + 

8308 ' and image parameters are exclusive each other; ' 

8309 'use only one at once') 

8310 if not (format is None): 

8311 if not isinstance(format, string_type): 

8312 raise TypeError('format must be a string, not ' + repr(format)) 

8313 if not any(a is not None for a in open_args): 

8314 raise TypeError('format can only be used with the blob, file ' 

8315 'or filename parameter') 

8316 if depth not in [None, 8, 16, 32]: 

8317 raise ValueError('Depth must be 8, 16 or 32') 

8318 with self.allocate(): 

8319 if image is None: 

8320 wand = library.NewMagickWand() 

8321 super(Image, self).__init__(wand) 

8322 if image is not None: 

8323 if not isinstance(image, BaseImage): 

8324 raise TypeError('image must be a wand.image.Image ' 

8325 'instance, not ' + repr(image)) 

8326 wand = library.CloneMagickWand(image.wand) 

8327 super(Image, self).__init__(wand) 

8328 elif any(a is not None for a in open_args): 

8329 if format: 

8330 format = binary(format) 

8331 if background: 

8332 if isinstance(background, string_type): 

8333 background = Color(background) 

8334 assertions.assert_color(background=background) 

8335 with background: 

8336 r = library.MagickSetBackgroundColor( 

8337 self.wand, 

8338 background.resource 

8339 ) 

8340 if not r: 

8341 self.raise_exception() 

8342 if colorspace is not None: 

8343 assertions.string_in_list( 

8344 COLORSPACE_TYPES, 

8345 'wand.image.COLORSPACE_TYPES', 

8346 colorspace=colorspace 

8347 ) 

8348 colorspace_idx = COLORSPACE_TYPES.index(colorspace) 

8349 library.MagickSetColorspace(self.wand, 

8350 colorspace_idx) 

8351 if width is not None and height is not None: 

8352 assertions.assert_counting_number(width=width, 

8353 height=height) 

8354 library.MagickSetSize(self.wand, width, height) 

8355 if depth is not None: 

8356 library.MagickSetDepth(self.wand, depth) 

8357 if format: 

8358 library.MagickSetFormat(self.wand, format) 

8359 if not filename: 

8360 library.MagickSetFilename(self.wand, 

8361 b'buffer.' + format) 

8362 if file is not None: 

8363 self.read(file=file, resolution=resolution, units=units) 

8364 elif blob is not None: 

8365 self.read(blob=blob, resolution=resolution, units=units) 

8366 elif filename is not None: 

8367 self.read(filename=filename, resolution=resolution, 

8368 units=units) 

8369 # clear the wand format, otherwise any subsequent call to 

8370 # MagickGetImageBlob will silently change the image to this 

8371 # format again. 

8372 library.MagickSetFormat(self.wand, binary("")) 

8373 elif width is not None and height is not None: 

8374 if pseudo is None: 

8375 self.blank(width, height, background) 

8376 else: 

8377 self.pseudo(width, height, pseudo) 

8378 if depth: 

8379 r = library.MagickSetImageDepth(self.wand, depth) 

8380 if not r: 

8381 raise self.raise_exception() 

8382 self.metadata = Metadata(self) 

8383 self.artifacts = ArtifactTree(self) 

8384 from .sequence import Sequence 

8385 self.sequence = Sequence(self) 

8386 self.profiles = ProfileDict(self) 

8387 self.raise_exception() 

8388 

8389 def __repr__(self): 

8390 return super(Image, self).__repr__( 

8391 extra_format=' {self.format!r} ({self.width}x{self.height})' 

8392 ) 

8393 

8394 def _repr_png_(self): 

8395 with self.convert('png') as cloned: 

8396 return cloned.make_blob() 

8397 

8398 @classmethod 

8399 def from_array(cls, array, channel_map=None, storage=None): 

8400 """Create an image instance from a :mod:`numpy` array, or any other datatype 

8401 that implements `__array_interface__`__ protocol. 

8402 

8403 .. code:: 

8404 

8405 import numpy 

8406 from wand.image import Image 

8407 

8408 matrix = numpy.random.rand(100, 100, 3) 

8409 with Image.from_array(matrix) as img: 

8410 img.save(filename='noise.png') 

8411 

8412 Use the optional ``channel_map`` & ``storage`` arguments to specify 

8413 the order of color channels & data size. If ``channel_map`` is omitted, 

8414 this method will will guess ``"RGB"``, ``"I"``, or ``"CMYK"`` based on 

8415 array shape. If ``storage`` is omitted, this method will reference the 

8416 array's ``typestr`` value, and raise a :class:`ValueError` if 

8417 storage-type can not be mapped. 

8418 

8419 Float values must be normalized between `0.0` and `1.0`, and signed 

8420 integers should be converted to unsigned values between `0` and 

8421 max value of type. 

8422 

8423 Instances of :class:`Image` can also be exported to numpy arrays:: 

8424 

8425 with Image(filename='rose:') as img: 

8426 matrix = numpy.array(img) 

8427 

8428 __ https://docs.scipy.org/doc/numpy/reference/arrays.interface.html 

8429 

8430 :param array: Numpy array of pixel values. 

8431 :type array: :class:`numpy.array` 

8432 :param channel_map: Color channel layout. 

8433 :type channel_map: :class:`basestring` 

8434 :param storage: Datatype per pixel part. 

8435 :type storage: :class:`basestring` 

8436 :returns: New instance of an image. 

8437 :rtype: :class:`~wand.image.Image` 

8438 

8439 .. versionadded:: 0.5.3 

8440 .. versionchanged:: 0.6.0 

8441 Input ``array`` now expects the :attr:`shape` property to be defined 

8442 as ```( 'height', 'width', 'channels' )```. 

8443 """ 

8444 arr_itr = array.__array_interface__ 

8445 typestr = arr_itr['typestr'] # Required by interface. 

8446 shape = arr_itr['shape'] # Required by interface. 

8447 if storage is None: 

8448 # Attempt to guess storage 

8449 storage_map = dict(u1='char', i1='char', 

8450 u2='short', i2='short', 

8451 u4='integer', i4='integer', 

8452 u8='long', i8='integer', 

8453 f4='float', f8='double') 

8454 for token in storage_map: 

8455 if token in typestr: 

8456 storage = storage_map[token] 

8457 break 

8458 if storage is None: 

8459 raise ValueError('Unable to determine storage type.') 

8460 if channel_map is None: 

8461 # Attempt to guess channel map 

8462 if len(shape) == 3: 

8463 if shape[2] < 5: 

8464 channel_map = 'RGBA'[0:shape[2]] 

8465 else: 

8466 channel_map = 'CMYKA'[0:shape[2]] 

8467 else: 

8468 channel_map = 'I' 

8469 if hasattr(array, 'ctypes'): 

8470 data_ptr = array.ctypes.data_as(ctypes.c_void_p) 

8471 elif hasattr(array, 'tobytes'): 

8472 data_ptr = array.tobytes() 

8473 elif hasattr(array, 'tostring'): 

8474 data_ptr = array.tostring() 

8475 else: 

8476 data_ptr, _ = arr_itr.get('data') 

8477 storage_idx = STORAGE_TYPES.index(storage) 

8478 height, width = shape[:2] 

8479 wand = library.NewMagickWand() 

8480 instance = cls(BaseImage(wand)) 

8481 r = library.MagickConstituteImage(instance.wand, 

8482 width, 

8483 height, 

8484 binary(channel_map), 

8485 storage_idx, 

8486 data_ptr) 

8487 if not r: 

8488 instance.raise_exception(cls) 

8489 return instance 

8490 

8491 @classmethod 

8492 def ping(cls, file=None, filename=None, blob=None, resolution=None, 

8493 format=None): 

8494 """Ping image header into Image() object, but without any pixel data. 

8495 This is useful for inspecting image meta-data without decoding the 

8496 whole image. 

8497 

8498 :param blob: reads an image from the ``blob`` byte array 

8499 :type blob: :class:`bytes` 

8500 :param file: reads an image from the ``file`` object 

8501 :type file: file object 

8502 :param filename: reads an image from the ``filename`` string 

8503 :type filename: :class:`basestring` 

8504 :param resolution: set a resolution value (DPI), 

8505 useful for vector formats (like PDF) 

8506 :type resolution: :class:`collections.abc.Sequence`, 

8507 :class:`numbers.Integral` 

8508 :param format: suggest image file format when reading from a ``blob``, 

8509 or ``file`` property. 

8510 :type format: :class:`basestring` 

8511 

8512 .. versionadded:: 0.5.6 

8513 

8514 """ 

8515 r = None 

8516 instance = cls() 

8517 # Resolution must be set after image reading. 

8518 if resolution is not None: 

8519 if (isinstance(resolution, abc.Sequence) and 

8520 len(resolution) == 2): 

8521 library.MagickSetResolution(instance.wand, *resolution) 

8522 elif isinstance(resolution, numbers.Integral): 

8523 library.MagickSetResolution(instance.wand, resolution, 

8524 resolution) 

8525 else: 

8526 raise TypeError('resolution must be a (x, y) pair or an ' 

8527 'integer of the same x/y') 

8528 if format: 

8529 library.MagickSetFormat(instance.wand, format) 

8530 if not filename: 

8531 library.MagickSetFilename(instance.wand, 

8532 b'buffer.' + format) 

8533 if file is not None: 

8534 if (isinstance(file, file_types) and 

8535 hasattr(libc, 'fdopen') and hasattr(file, 'mode')): 

8536 fd = libc.fdopen(file.fileno(), file.mode) 

8537 r = library.MagickPingImageFile(instance.wand, fd) 

8538 elif not callable(getattr(file, 'read', None)): 

8539 raise TypeError('file must be a readable file object' 

8540 ', but the given object does not ' 

8541 'have read() method') 

8542 else: 

8543 blob = file.read() 

8544 file = None 

8545 if blob is not None: 

8546 if not isinstance(blob, abc.Iterable): 

8547 raise TypeError('blob must be iterable, not ' + 

8548 repr(blob)) 

8549 if not isinstance(blob, binary_type): 

8550 blob = b''.join(blob) 

8551 r = library.MagickPingImageBlob(instance.wand, blob, len(blob)) 

8552 elif filename is not None: 

8553 filename = encode_filename(filename) 

8554 r = library.MagickPingImage(instance.wand, filename) 

8555 if not r: 

8556 instance.raise_exception() 

8557 msg = ('MagickPingImage returns false, but did raise ImageMagick ' 

8558 'exception. This can occur when a delegate is missing, or ' 

8559 'returns EXIT_SUCCESS without generating a raster.') 

8560 raise WandRuntimeError(msg) 

8561 else: 

8562 instance.metadata = Metadata(instance) 

8563 instance.artifacts = ArtifactTree(instance) 

8564 from .sequence import Sequence 

8565 instance.sequence = Sequence(instance) 

8566 instance.profiles = ProfileDict(instance) 

8567 return instance 

8568 

8569 @classmethod 

8570 def stereogram(cls, left, right): 

8571 """Create a new stereogram image from two existing images. 

8572 

8573 :param left: Left-eye image. 

8574 :type left: :class:`wand.image.Image` 

8575 :param right: Right-eye image. 

8576 :type right: :class:`wand.image.Image` 

8577 

8578 .. versionadded:: 0.5.4 

8579 """ 

8580 if not isinstance(left, BaseImage): 

8581 raise TypeError('Left image must be in instance of ' 

8582 'wand.image.Image, not ' + repr(left)) 

8583 if not isinstance(right, BaseImage): 

8584 raise TypeError('Right image must be in instance of ' 

8585 'wand.image.Image, not ' + repr(right)) 

8586 wand = library.MagickStereoImage(left.wand, right.wand) 

8587 if not wand: # pragma: no cover 

8588 left.raise_exception() 

8589 return cls(BaseImage(wand)) 

8590 

8591 @property 

8592 def animation(self): 

8593 is_gif = self.mimetype in ('image/gif', 'image/x-gif') 

8594 frames = library.MagickGetNumberImages(self.wand) 

8595 return is_gif and frames > 1 

8596 

8597 @property 

8598 def mimetype(self): 

8599 """(:class:`basestring`) The MIME type of the image 

8600 e.g. ``'image/jpeg'``, ``'image/png'``. 

8601 

8602 .. versionadded:: 0.1.7 

8603 

8604 """ 

8605 rp = libmagick.MagickToMime(binary(self.format)) 

8606 if not bool(rp): 

8607 self.raise_exception() 

8608 mimetype = rp.value 

8609 return text(mimetype) 

8610 

8611 def blank(self, width, height, background=None): 

8612 """Creates blank image. 

8613 

8614 :param width: the width of new blank image. 

8615 :type width: :class:`numbers.Integral` 

8616 :param height: the height of new blank image. 

8617 :type height: :class:`numbers.Integral` 

8618 :param background: an optional background color. 

8619 default is transparent 

8620 :type background: :class:`wand.color.Color` 

8621 :returns: blank image 

8622 :rtype: :class:`Image` 

8623 

8624 .. versionadded:: 0.3.0 

8625 

8626 """ 

8627 assertions.assert_counting_number(width=width, height=height) 

8628 if background is None: 

8629 background = Color('transparent') 

8630 elif isinstance(background, string_type): 

8631 background = Color(background) 

8632 assertions.assert_color(background=background) 

8633 with background: 

8634 r = library.MagickNewImage(self.wand, width, height, 

8635 background.resource) 

8636 if not r: 

8637 self.raise_exception() 

8638 return self 

8639 

8640 def clear(self): 

8641 """Clears resources associated with the image, leaving the image blank, 

8642 and ready to be used with new image. 

8643 

8644 .. versionadded:: 0.3.0 

8645 

8646 """ 

8647 library.ClearMagickWand(self.wand) 

8648 

8649 def close(self): 

8650 """Closes the image explicitly. If you use the image object in 

8651 :keyword:`with` statement, it was called implicitly so don't have to 

8652 call it. 

8653 

8654 .. note:: 

8655 

8656 It has the same functionality of :attr:`destroy()` method. 

8657 

8658 """ 

8659 self.destroy() 

8660 

8661 def compare_layers(self, method): 

8662 """Generates new images showing the delta pixels between 

8663 layers. Similar pixels are converted to transparent. 

8664 Useful for debugging complex animations. :: 

8665 

8666 with img.compare_layers('compareany') as delta: 

8667 delta.save(filename='framediff_%02d.png') 

8668 

8669 .. note:: 

8670 

8671 May not work as expected if animations are already 

8672 optimized. 

8673 

8674 :param method: Can be ``'compareany'``, 

8675 ``'compareclear'``, or ``'compareoverlay'`` 

8676 :type method: :class:`basestring` 

8677 :returns: new image stack. 

8678 :rtype: :class:`Image` 

8679 

8680 .. versionadded:: 0.5.0 

8681 """ 

8682 if not isinstance(method, string_type): 

8683 raise TypeError('method must be a string from IMAGE_LAYER_METHOD, ' 

8684 'not ' + repr(method)) 

8685 if method not in ('compareany', 'compareclear', 'compareoverlay'): 

8686 raise ValueError('method can only be \'compareany\', ' 

8687 '\'compareclear\', or \'compareoverlay\'') 

8688 r = None 

8689 m = IMAGE_LAYER_METHOD.index(method) 

8690 if MAGICK_VERSION_NUMBER >= 0x700: # pragma: no cover 

8691 r = library.MagickCompareImagesLayers(self.wand, m) 

8692 elif library.MagickCompareImageLayers: 

8693 r = library.MagickCompareImageLayers(self.wand, m) 

8694 elif library.MagickCompareImagesLayers: # pragma: no cover 

8695 r = library.MagickCompareImagesLayers(self.wand, m) 

8696 else: 

8697 raise AttributeError('MagickCompareImageLayers method ' 

8698 'not available on system.') 

8699 if not r: 

8700 self.raise_exception() 

8701 return Image(image=BaseImage(r)) 

8702 

8703 def convert(self, format): 

8704 """Converts the image format with the original image maintained. 

8705 It returns a converted image instance which is new. :: 

8706 

8707 with img.convert('png') as converted: 

8708 converted.save(filename='converted.png') 

8709 

8710 :param format: image format to convert to 

8711 :type format: :class:`basestring` 

8712 :returns: a converted image 

8713 :rtype: :class:`Image` 

8714 :raises ValueError: when the given ``format`` is unsupported 

8715 

8716 .. versionadded:: 0.1.6 

8717 

8718 """ 

8719 cloned = self.clone() 

8720 cloned.format = format 

8721 return cloned 

8722 

8723 def make_blob(self, format=None): 

8724 """Makes the binary string of the image. 

8725 

8726 :param format: the image format to write e.g. ``'png'``, ``'jpeg'``. 

8727 it is omittable 

8728 :type format: :class:`basestring` 

8729 :returns: a blob (bytes) string 

8730 :rtype: :class:`bytes` 

8731 :raises ValueError: when ``format`` is invalid 

8732 

8733 .. versionchanged:: 0.1.6 

8734 Removed a side effect that changes the image :attr:`format` 

8735 silently. 

8736 

8737 .. versionadded:: 0.1.5 

8738 The ``format`` parameter became optional. 

8739 

8740 .. versionadded:: 0.1.1 

8741 

8742 """ 

8743 if format is not None: 

8744 with self.convert(format) as converted: 

8745 return converted.make_blob() 

8746 library.MagickResetIterator(self.wand) 

8747 length = ctypes.c_size_t() 

8748 blob_p = None 

8749 if len(self.sequence) > 1: 

8750 blob_p = library.MagickGetImagesBlob(self.wand, 

8751 ctypes.byref(length)) 

8752 else: 

8753 blob_p = library.MagickGetImageBlob(self.wand, 

8754 ctypes.byref(length)) 

8755 if blob_p and length.value: 

8756 blob = ctypes.string_at(blob_p, length.value) 

8757 library.MagickRelinquishMemory(blob_p) 

8758 return blob 

8759 else: # pragma: no cover 

8760 self.raise_exception() 

8761 

8762 def pseudo(self, width, height, pseudo='xc:'): 

8763 """Creates a new image from ImageMagick's internal protocol coders. 

8764 

8765 :param width: Total columns of the new image. 

8766 :type width: :class:`numbers.Integral` 

8767 :param height: Total rows of the new image. 

8768 :type height: :class:`numbers.Integral` 

8769 :param pseudo: The protocol & arguments for the pseudo image. 

8770 :type pseudo: :class:`basestring` 

8771 

8772 .. versionadded:: 0.5.0 

8773 """ 

8774 assertions.assert_counting_number(width=width, height=height) 

8775 assertions.assert_string(pseudo=pseudo) 

8776 r = library.MagickSetSize(self.wand, width, height) 

8777 if not r: 

8778 self.raise_exception() 

8779 r = library.MagickReadImage(self.wand, encode_filename(pseudo)) 

8780 if not r: 

8781 self.raise_exception() 

8782 

8783 def read(self, file=None, filename=None, blob=None, resolution=None, 

8784 units=None): 

8785 """Read new image into Image() object. 

8786 

8787 :param blob: reads an image from the ``blob`` byte array 

8788 :type blob: :class:`bytes` 

8789 :param file: reads an image from the ``file`` object 

8790 :type file: file object 

8791 :param filename: reads an image from the ``filename`` string. 

8792 Additional :ref:`read_mods` are supported. 

8793 :type filename: :class:`basestring` 

8794 :param resolution: set a resolution value (DPI), 

8795 useful for vectorial formats (like PDF) 

8796 :type resolution: :class:`collections.abc.Sequence`, 

8797 :class:`numbers.Integral` 

8798 :param units: used with ``resolution``, can either be 

8799 ``'pixelperinch'``, or ``'pixelpercentimeter'``. 

8800 :type units: :class:`basestring` 

8801 

8802 .. versionadded:: 0.3.0 

8803 

8804 .. versionchanged:: 0.5.7 

8805 Added ``units`` parameter. 

8806 """ 

8807 r = None 

8808 # Resolution must be set after image reading. 

8809 if resolution is not None: 

8810 if (isinstance(resolution, abc.Sequence) and 

8811 len(resolution) == 2): 

8812 library.MagickSetResolution(self.wand, *resolution) 

8813 elif isinstance(resolution, numbers.Integral): 

8814 library.MagickSetResolution(self.wand, resolution, resolution) 

8815 else: 

8816 raise TypeError('resolution must be a (x, y) pair or an ' 

8817 'integer of the same x/y') 

8818 if file is not None: 

8819 if (isinstance(file, file_types) and 

8820 hasattr(libc, 'fdopen') and hasattr(file, 'mode')): 

8821 fd = libc.fdopen(file.fileno(), file.mode) 

8822 r = library.MagickReadImageFile(self.wand, fd) 

8823 elif not callable(getattr(file, 'read', None)): 

8824 raise TypeError('file must be a readable file object' 

8825 ', but the given object does not ' 

8826 'have read() method') 

8827 else: 

8828 blob = file.read() 

8829 file = None 

8830 if blob is not None: 

8831 if not isinstance(blob, abc.Iterable): 

8832 raise TypeError('blob must be iterable, not ' + 

8833 repr(blob)) 

8834 if not isinstance(blob, binary_type): 

8835 blob = b''.join(blob) 

8836 r = library.MagickReadImageBlob(self.wand, blob, len(blob)) 

8837 elif filename is not None: 

8838 filename = encode_filename(filename) 

8839 r = library.MagickReadImage(self.wand, filename) 

8840 if not r: 

8841 self.raise_exception() 

8842 msg = ('MagickReadImage returns false, but did not raise ' 

8843 'ImageMagick exception. This can occur when a delegate ' 

8844 'is missing, or returns EXIT_SUCCESS without generating a ' 

8845 'raster.') 

8846 raise WandRuntimeError(msg) 

8847 else: 

8848 if units is not None: 

8849 self.units = units 

8850 

8851 def reset_sequence(self): 

8852 """Remove any previously allocated :class:`~wand.sequence.SingleImage` 

8853 instances in :attr:`sequence` attribute. 

8854 

8855 .. versionadded:: 0.6.0 

8856 """ 

8857 for instance in self.sequence.instances: 

8858 if hasattr(instance, 'destroy'): 

8859 instance.destroy() 

8860 self.sequence.instances = [] 

8861 

8862 def save(self, file=None, filename=None, adjoin=True): 

8863 """Saves the image into the ``file`` or ``filename``. It takes 

8864 only one argument at a time. 

8865 

8866 :param file: a file object to write to 

8867 :type file: file object 

8868 :param filename: a filename string to write to 

8869 :type filename: :class:`basestring` 

8870 :param adjoin: write all images to a single multi-image file. Only 

8871 available if file format supports frames, layers, & etc. 

8872 :type adjoin: :class:`bool` 

8873 

8874 .. versionadded:: 0.1.1 

8875 

8876 .. versionchanged:: 0.1.5 

8877 The ``file`` parameter was added. 

8878 

8879 .. versionchanged:: 6.0.0 

8880 The ``adjoin`` parameter was added. 

8881 

8882 """ 

8883 if file is None and filename is None: 

8884 raise TypeError('expected an argument') 

8885 elif file is not None and filename is not None: 

8886 raise TypeError('expected only one argument; but two passed') 

8887 elif file is not None: 

8888 if isinstance(file, string_type): 

8889 raise TypeError('file must be a writable file object, ' 

8890 'but {0!r} is a string; did you want ' 

8891 '.save(filename={0!r})?'.format(file)) 

8892 elif isinstance(file, file_types) and hasattr(libc, 'fdopen'): 

8893 fd = libc.fdopen(file.fileno(), file.mode) 

8894 if library.MagickGetNumberImages(self.wand) > 1: 

8895 r = library.MagickWriteImagesFile(self.wand, fd) 

8896 else: 

8897 r = library.MagickWriteImageFile(self.wand, fd) 

8898 libc.fflush(fd) 

8899 if not r: 

8900 self.raise_exception() 

8901 else: 

8902 if not callable(getattr(file, 'write', None)): 

8903 raise TypeError('file must be a writable file object, ' 

8904 'but it does not have write() method: ' + 

8905 repr(file)) 

8906 file.write(self.make_blob()) 

8907 else: 

8908 if not isinstance(filename, string_type): 

8909 if not hasattr(filename, '__fspath__'): 

8910 raise TypeError('filename must be a string, not ' + 

8911 repr(filename)) 

8912 filename = encode_filename(filename) 

8913 if library.MagickGetNumberImages(self.wand) > 1: 

8914 r = library.MagickWriteImages(self.wand, filename, adjoin) 

8915 else: 

8916 r = library.MagickWriteImage(self.wand, filename) 

8917 if not r: 

8918 self.raise_exception() 

8919 

8920 

8921class Iterator(Resource, abc.Iterator): 

8922 """Row iterator for :class:`Image`. It shouldn't be instantiated 

8923 directly; instead, it can be acquired through :class:`Image` instance:: 

8924 

8925 assert isinstance(image, wand.image.Image) 

8926 iterator = iter(image) 

8927 

8928 It doesn't iterate every pixel, but rows. For example:: 

8929 

8930 for row in image: 

8931 for col in row: 

8932 assert isinstance(col, wand.color.Color) 

8933 print(col) 

8934 

8935 Every row is a :class:`collections.abc.Sequence` which consists of 

8936 one or more :class:`wand.color.Color` values. 

8937 

8938 :param image: the image to get an iterator 

8939 :type image: :class:`Image` 

8940 

8941 .. versionadded:: 0.1.3 

8942 

8943 """ 

8944 

8945 c_is_resource = library.IsPixelIterator 

8946 c_destroy_resource = library.DestroyPixelIterator 

8947 c_get_exception = library.PixelGetIteratorException 

8948 c_clear_exception = library.PixelClearIteratorException 

8949 

8950 def __init__(self, image=None, iterator=None): 

8951 if image is not None and iterator is not None: 

8952 raise TypeError('it takes only one argument at a time') 

8953 with self.allocate(): 

8954 if image is not None: 

8955 if not isinstance(image, Image): 

8956 raise TypeError('expected a wand.image.Image instance, ' 

8957 'not ' + repr(image)) 

8958 self.resource = library.NewPixelIterator(image.wand) 

8959 self.height = image.height 

8960 else: 

8961 if not isinstance(iterator, Iterator): 

8962 raise TypeError('expected a wand.image.Iterator instance, ' 

8963 'not ' + repr(iterator)) 

8964 self.resource = library.ClonePixelIterator(iterator.resource) 

8965 self.height = iterator.height 

8966 self.raise_exception() 

8967 self.cursor = 0 

8968 

8969 def __iter__(self): 

8970 return self 

8971 

8972 def seek(self, y): 

8973 assertions.assert_unsigned_integer(seek=y) 

8974 if y > self.height: 

8975 raise ValueError('can not be greater than height') 

8976 self.cursor = y 

8977 if y == 0: 

8978 library.PixelSetFirstIteratorRow(self.resource) 

8979 else: 

8980 if not library.PixelSetIteratorRow(self.resource, y - 1): 

8981 self.raise_exception() 

8982 

8983 def __next__(self, x=None): 

8984 if self.cursor >= self.height: 

8985 self.destroy() 

8986 raise StopIteration() 

8987 self.cursor += 1 

8988 width = ctypes.c_size_t() 

8989 pixels = library.PixelGetNextIteratorRow(self.resource, 

8990 ctypes.byref(width)) 

8991 if x is None: 

8992 r_pixels = [None] * width.value 

8993 for x in xrange(width.value): 

8994 r_pixels[x] = Color.from_pixelwand(pixels[x]) 

8995 return r_pixels 

8996 return Color.from_pixelwand(pixels[x]) if pixels else None 

8997 

8998 next = __next__ # Python 2 compatibility 

8999 

9000 def clone(self): 

9001 """Clones the same iterator. 

9002 

9003 """ 

9004 return type(self)(iterator=self) 

9005 

9006 

9007class ImageProperty(object): 

9008 """The mixin class to maintain a weak reference to the parent 

9009 :class:`Image` object. 

9010 

9011 .. versionadded:: 0.3.0 

9012 

9013 """ 

9014 

9015 def __init__(self, image): 

9016 if not isinstance(image, BaseImage): 

9017 raise TypeError('expected a wand.image.BaseImage instance, ' 

9018 'not ' + repr(image)) 

9019 self._image = weakref.ref(image) 

9020 

9021 @property 

9022 def image(self): 

9023 """(:class:`Image`) The parent image. 

9024 

9025 It ensures that the parent :class:`Image`, which is held in a weak 

9026 reference, still exists. Returns the dereferenced :class:`Image` 

9027 if it does exist, or raises a :exc:`ClosedImageError` otherwise. 

9028 

9029 :exc: `ClosedImageError` when the parent Image has been destroyed 

9030 

9031 """ 

9032 # Dereference our weakref and check that the parent Image still exists 

9033 image = self._image() 

9034 if image is not None: 

9035 return image 

9036 raise ClosedImageError( 

9037 'parent Image of {0!r} has been destroyed'.format(self) 

9038 ) 

9039 

9040 

9041class OptionDict(ImageProperty, abc.MutableMapping): 

9042 """Free-form mutable mapping of global internal settings. 

9043 

9044 .. versionadded:: 0.3.0 

9045 

9046 .. versionchanged:: 0.5.0 

9047 Remove key check to :const:`OPTIONS`. Image properties are specific to 

9048 vendor, and this library should not attempt to manage the 100+ options 

9049 in a whitelist. 

9050 """ 

9051 

9052 def __iter__(self): 

9053 return iter(OPTIONS) 

9054 

9055 def __len__(self): 

9056 return len(OPTIONS) 

9057 

9058 def __getitem__(self, key): 

9059 assertions.assert_string(key=key) 

9060 image = self.image 

9061 return text(library.MagickGetOption(image.wand, binary(key))) 

9062 

9063 def __setitem__(self, key, value): 

9064 assertions.assert_string(key=key, value=value) 

9065 image = self.image 

9066 library.MagickSetOption(image.wand, binary(key), binary(value)) 

9067 

9068 def __delitem__(self, key): 

9069 self[key] = '' 

9070 

9071 

9072class Metadata(ImageProperty, abc.MutableMapping): 

9073 """Class that implements dict-like read-only access to image metadata 

9074 like EXIF or IPTC headers. Most WRITE encoders will ignore properties 

9075 assigned here. 

9076 

9077 :param image: an image instance 

9078 :type image: :class:`Image` 

9079 

9080 .. note:: 

9081 

9082 You don't have to use this by yourself. 

9083 Use :attr:`Image.metadata` property instead. 

9084 

9085 .. versionadded:: 0.3.0 

9086 

9087 """ 

9088 

9089 def __init__(self, image): 

9090 if not isinstance(image, Image): 

9091 raise TypeError('expected a wand.image.Image instance, ' 

9092 'not ' + repr(image)) 

9093 super(Metadata, self).__init__(image) 

9094 

9095 def __getitem__(self, k): 

9096 """ 

9097 :param k: Metadata header name string. 

9098 :type k: :class:`basestring` 

9099 :returns: a header value string 

9100 :rtype: :class:`str` 

9101 """ 

9102 assertions.assert_string(key=k) 

9103 image = self.image 

9104 v = library.MagickGetImageProperty(image.wand, binary(k)) 

9105 if bool(v) is False: 

9106 raise KeyError(k) 

9107 value = v.value 

9108 return text(value) 

9109 

9110 def __setitem__(self, k, v): 

9111 """ 

9112 :param k: Metadata header name string. 

9113 :type k: :class:`basestring` 

9114 :param v: Value to assign. 

9115 :type v: :class:`basestring` 

9116 

9117 .. versionadded: 0.5.0 

9118 """ 

9119 assertions.assert_string(key=k, value=v) 

9120 image = self.image 

9121 r = library.MagickSetImageProperty(image.wand, binary(k), binary(v)) 

9122 if not r: 

9123 image.raise_exception() 

9124 return v 

9125 

9126 def __delitem__(self, k): 

9127 """ 

9128 :param k: Metadata header name string. 

9129 :type k: :class:`basestring` 

9130 

9131 .. versionadded: 0.5.0 

9132 """ 

9133 assertions.assert_string(key=k) 

9134 image = self.image 

9135 r = library.MagickDeleteImageProperty(image.wand, binary(k)) 

9136 if not r: 

9137 image.raise_exception() 

9138 

9139 def __iter__(self): 

9140 image = self.image 

9141 num = ctypes.c_size_t() 

9142 props_p = library.MagickGetImageProperties(image.wand, b'', num) 

9143 props = [text(props_p[i]) for i in xrange(num.value)] 

9144 library.MagickRelinquishMemory(props_p) 

9145 return iter(props) 

9146 

9147 def __len__(self): 

9148 image = self.image 

9149 num = ctypes.c_size_t() 

9150 props_p = library.MagickGetImageProperties(image.wand, b'', num) 

9151 library.MagickRelinquishMemory(props_p) 

9152 return num.value 

9153 

9154 

9155class ArtifactTree(ImageProperty, abc.MutableMapping): 

9156 """Splay tree to map image artifacts. Values defined here 

9157 are intended to be used elseware, and will not be written 

9158 to the encoded image. 

9159 

9160 For example:: 

9161 

9162 # Omit timestamp from PNG file headers. 

9163 with Image(filename='input.png') as img: 

9164 img.artifacts['png:exclude-chunks'] = 'tIME' 

9165 img.save(filename='output.png') 

9166 

9167 :param image: an image instance 

9168 :type image: :class:`Image` 

9169 

9170 .. note:: 

9171 

9172 You don't have to use this by yourself. 

9173 Use :attr:`Image.artifacts` property instead. 

9174 

9175 .. versionadded:: 0.5.0 

9176 """ 

9177 

9178 def __init__(self, image): 

9179 if not isinstance(image, Image): 

9180 raise TypeError('expected a wand.image.Image instance, ' 

9181 'not ' + repr(image)) 

9182 super(ArtifactTree, self).__init__(image) 

9183 

9184 def __getitem__(self, k): 

9185 """ 

9186 :param k: Metadata header name string. 

9187 :type k: :class:`basestring` 

9188 :returns: a header value string 

9189 :rtype: :class:`str` 

9190 

9191 .. versionadded: 0.5.0 

9192 """ 

9193 assertions.assert_string(key=k) 

9194 image = self.image 

9195 v = library.MagickGetImageArtifact(image.wand, binary(k)) 

9196 if bool(v) is False: 

9197 try: 

9198 v = library.MagickGetImageProperty(image.wand, binary(k)) 

9199 value = v.value 

9200 except KeyError: # pragma: no cover 

9201 value = "" 

9202 else: 

9203 value = v.value 

9204 return text(value) 

9205 

9206 def __setitem__(self, k, v): 

9207 """ 

9208 :param k: Metadata header name string. 

9209 :type k: :class:`basestring` 

9210 :param v: Value to assign. 

9211 :type v: :class:`basestring` 

9212 

9213 .. versionadded: 0.5.0 

9214 """ 

9215 assertions.assert_string(key=k, value=v) 

9216 image = self.image 

9217 r = library.MagickSetImageArtifact(image.wand, binary(k), binary(v)) 

9218 if not r: # pragma: no cover 

9219 image.raise_exception() 

9220 return v 

9221 

9222 def __delitem__(self, k): 

9223 """ 

9224 :param k: Metadata header name string. 

9225 :type k: :class:`basestring` 

9226 

9227 .. versionadded: 0.5.0 

9228 """ 

9229 assertions.assert_string(key=k) 

9230 image = self.image 

9231 r = library.MagickDeleteImageArtifact(image.wand, binary(k)) 

9232 if not r: # pragma: no cover 

9233 image.raise_exception() 

9234 

9235 def __iter__(self): 

9236 image = self.image 

9237 num = ctypes.c_size_t() 

9238 props_p = library.MagickGetImageArtifacts(image.wand, b'', num) 

9239 props = [text(props_p[i]) for i in xrange(num.value)] 

9240 library.MagickRelinquishMemory(props_p) 

9241 return iter(props) 

9242 

9243 def __len__(self): 

9244 image = self.image 

9245 num = ctypes.c_size_t() 

9246 props_p = library.MagickGetImageArtifacts(image.wand, b'', num) 

9247 library.MagickRelinquishMemory(props_p) 

9248 return num.value 

9249 

9250 

9251class ProfileDict(ImageProperty, abc.MutableMapping): 

9252 """The mapping table of embedded image profiles. 

9253 

9254 Use this to get, set, and delete whole profile payloads on an image. Each 

9255 payload is a raw binary string. 

9256 

9257 For example:: 

9258 

9259 with Image(filename='photo.jpg') as img: 

9260 # Extract EXIF 

9261 with open('exif.bin', 'wb') as payload: 

9262 payload.write(img.profiles['exif']) 

9263 # Import ICC 

9264 with open('color_profile.icc', 'rb') as payload: 

9265 img.profiles['icc'] = payload.read() 

9266 # Remove XMP 

9267 del imp.profiles['xmp'] 

9268 

9269 .. seealso:: 

9270 

9271 `Embedded Image Profiles`__ for a list of supported profiles. 

9272 

9273 __ https://imagemagick.org/script/formats.php#embedded 

9274 

9275 .. versionadded:: 0.5.1 

9276 """ 

9277 def __init__(self, image): 

9278 if not isinstance(image, Image): 

9279 raise TypeError('expected a wand.image.Image instance, ' 

9280 'not ' + repr(image)) 

9281 super(ProfileDict, self).__init__(image) 

9282 

9283 def __delitem__(self, k): 

9284 assertions.assert_string(key=k) 

9285 num = ctypes.c_size_t(0) 

9286 profile_p = library.MagickRemoveImageProfile(self.image.wand, 

9287 binary(k), num) 

9288 library.MagickRelinquishMemory(profile_p) 

9289 

9290 def __getitem__(self, k): 

9291 assertions.assert_string(key=k) 

9292 num = ctypes.c_size_t(0) 

9293 profile_p = library.MagickGetImageProfile(self.image.wand, 

9294 binary(k), num) 

9295 if num.value > 0: 

9296 if PY3: 

9297 return_profile = bytes(profile_p[0:num.value]) 

9298 else: 

9299 return_profile = str(bytearray(profile_p[0:num.value])) 

9300 library.MagickRelinquishMemory(profile_p) 

9301 else: 

9302 return_profile = None 

9303 return return_profile 

9304 

9305 def __iter__(self): 

9306 num = ctypes.c_size_t(0) 

9307 profiles_p = library.MagickGetImageProfiles(self.image.wand, b'', num) 

9308 profiles = [text(profiles_p[i]) for i in xrange(num.value)] 

9309 library.MagickRelinquishMemory(profiles_p) 

9310 return iter(profiles) 

9311 

9312 def __len__(self): 

9313 num = ctypes.c_size_t(0) 

9314 profiles_p = library.MagickGetImageProfiles(self.image.wand, b'', num) 

9315 library.MagickRelinquishMemory(profiles_p) 

9316 return num.value 

9317 

9318 def __setitem__(self, k, v): 

9319 assertions.assert_string(key=k) 

9320 if not isinstance(v, binary_type): 

9321 raise TypeError('value must be a binary string, not ' + repr(v)) 

9322 r = library.MagickSetImageProfile(self.image.wand, 

9323 binary(k), v, len(v)) 

9324 if not r: 

9325 self.image.raise_exception() 

9326 

9327 

9328class ChannelImageDict(ImageProperty, abc.Mapping): 

9329 """The mapping table of separated images of the particular channel 

9330 from the image. 

9331 

9332 :param image: an image instance 

9333 :type image: :class:`Image` 

9334 

9335 .. note:: 

9336 

9337 You don't have to use this by yourself. 

9338 Use :attr:`Image.channel_images` property instead. 

9339 

9340 .. versionadded:: 0.3.0 

9341 

9342 """ 

9343 

9344 def __iter__(self): 

9345 return iter(CHANNELS) 

9346 

9347 def __len__(self): 

9348 return len(CHANNELS) 

9349 

9350 def __getitem__(self, channel): 

9351 c = CHANNELS[channel] 

9352 img = self.image.clone() 

9353 if library.MagickSeparateImageChannel: 

9354 succeeded = library.MagickSeparateImageChannel(img.wand, c) 

9355 else: 

9356 succeeded = library.MagickSeparateImage(img.wand, c) 

9357 if not succeeded: 

9358 try: 

9359 img.raise_exception() 

9360 except WandException: 

9361 img.close() 

9362 raise 

9363 return img 

9364 

9365 

9366class ChannelDepthDict(ImageProperty, abc.Mapping): 

9367 """The mapping table of channels to their depth. 

9368 

9369 :param image: an image instance 

9370 :type image: :class:`Image` 

9371 

9372 .. note:: 

9373 

9374 You don't have to use this by yourself. 

9375 Use :attr:`Image.channel_depths` property instead. 

9376 

9377 .. versionadded:: 0.3.0 

9378 

9379 """ 

9380 

9381 def __iter__(self): 

9382 return iter(CHANNELS) 

9383 

9384 def __len__(self): 

9385 return len(CHANNELS) 

9386 

9387 def __getitem__(self, channel): 

9388 c = CHANNELS[channel] 

9389 if library.MagickGetImageChannelDepth: 

9390 depth = library.MagickGetImageChannelDepth(self.image.wand, c) 

9391 else: 

9392 mask = 0 

9393 if c != 0: 

9394 mask = library.MagickSetImageChannelMask(self.image.wand, c) 

9395 depth = library.MagickGetImageDepth(self.image.wand) 

9396 if mask != 0: 

9397 library.MagickSetImageChannelMask(self.image.wand, mask) 

9398 return int(depth) 

9399 

9400 

9401class HistogramDict(abc.Mapping): 

9402 """Specialized mapping object to represent color histogram. 

9403 Keys are colors, and values are the number of pixels. 

9404 

9405 :param image: the image to get its histogram 

9406 :type image: :class:`BaseImage` 

9407 

9408 .. versionadded:: 0.3.0 

9409 

9410 """ 

9411 

9412 def __init__(self, image): 

9413 self.size = ctypes.c_size_t() 

9414 self.pixels = library.MagickGetImageHistogram( 

9415 image.wand, 

9416 ctypes.byref(self.size) 

9417 ) 

9418 self.counts = None 

9419 

9420 def __del__(self): 

9421 if self.pixels: 

9422 self.pixels = library.DestroyPixelWands(self.pixels, 

9423 self.size.value) 

9424 

9425 def __len__(self): 

9426 if self.counts is None: 

9427 return self.size.value 

9428 return len(self.counts) 

9429 

9430 def __iter__(self): 

9431 if self.counts is None: 

9432 self._build_counts() 

9433 return iter(self.counts) 

9434 

9435 def __getitem__(self, color): 

9436 if self.counts is None: 

9437 self._build_counts() 

9438 if isinstance(color, string_type): 

9439 color = Color(color) 

9440 assertions.assert_color(color=color) 

9441 return self.counts[color] 

9442 

9443 def _build_counts(self): 

9444 self.counts = {} 

9445 for i in xrange(self.size.value): 

9446 color_count = library.PixelGetColorCount(self.pixels[i]) 

9447 color = Color.from_pixelwand(self.pixels[i]) 

9448 self.counts[color] = color_count 

9449 

9450 

9451class ConnectedComponentObject(object): 

9452 """Generic Python wrapper to translate 

9453 :c:type:`CCObjectInfo` structure into a class describing objects found 

9454 within an image. This class is generated by 

9455 :meth:`Image.connected_components() 

9456 <wand.image.BaseImage.connected_components>` method. 

9457 

9458 .. versionadded:: 0.5.5 

9459 """ 

9460 #: (:class:`numbers.Integral`) Serialized object identifier 

9461 #: starting at `0`. 

9462 _id = None 

9463 

9464 #: (:class:`numbers.Integral`) Width of objects minimum 

9465 #: bounding rectangle. 

9466 width = None 

9467 

9468 #: (:class:`numbers.Integral`) Height of objects minimum 

9469 #: bounding rectangle. 

9470 height = None 

9471 

9472 #: (:class:`numbers.Integral`) X offset of objects minimum 

9473 #: bounding rectangle. 

9474 left = None 

9475 

9476 #: (:class:`numbers.Integral`) Y offset of objects minimum 

9477 #: bounding rectangle. 

9478 top = None 

9479 

9480 #: (:class:`numbers.Real`) X offset of objects centroid. 

9481 center_x = None 

9482 

9483 #: (:class:`numbers.Real`) Y offset of objects centroid. 

9484 center_y = None 

9485 

9486 #: (:class:`numbers.Real`) Quantity of pixels that make-up 

9487 #: the objects shape. 

9488 area = None 

9489 

9490 #: (:class:`~wand.color.Color`) The average color of the 

9491 #: shape. 

9492 mean_color = None 

9493 

9494 def __init__(self, cc_object=None): 

9495 if isinstance(cc_object, CCObjectInfo): 

9496 self.clone_from_cc_object_info(cc_object) 

9497 

9498 @property 

9499 def size(self): 

9500 """(:class:`tuple` (:attr:`width`, :attr:`height`)) 

9501 Minimum bounding rectangle.""" 

9502 return self.width, self.height 

9503 

9504 @property 

9505 def offset(self): 

9506 """(:class:`tuple` (:attr:`left`, :attr:`top`)) 

9507 Position of objects minimum bounding rectangle.""" 

9508 return self.left, self.top 

9509 

9510 @property 

9511 def centroid(self): 

9512 """(:class:`tuple` (:attr:`center_x`, :attr:`center_y`)) 

9513 Center of object.""" 

9514 return self.center_x, self.center_y 

9515 

9516 def clone_from_cc_object_info(self, cc_object): 

9517 """Copy data from :class:`~wand.cdefs.structures.CCObjectInfo`.""" 

9518 self._id = cc_object._id 

9519 self.width = cc_object.bounding_box.width 

9520 self.height = cc_object.bounding_box.height 

9521 self.left = cc_object.bounding_box.x 

9522 self.top = cc_object.bounding_box.y 

9523 self.center_x = cc_object.centroid.x 

9524 self.center_y = cc_object.centroid.y 

9525 self.area = cc_object.area 

9526 pinfo_size = ctypes.sizeof(PixelInfo) 

9527 raw_buffer = ctypes.create_string_buffer(pinfo_size) 

9528 ctypes.memmove(raw_buffer, 

9529 ctypes.byref(cc_object.color), 

9530 pinfo_size) 

9531 self.mean_color = Color(raw=raw_buffer) 

9532 

9533 def __repr__(self): 

9534 fmt = ("{name}({_id}: {width}x{height}+{left}+{top} {center_x:.2f}," 

9535 "{center_y:.2f} {area:.0f} {mean_color})") 

9536 return fmt.format(name=self.__class__.__name__, **self.__dict__) 

9537 

9538 

9539class ClosedImageError(DestroyedResourceError): 

9540 """An error that rises when some code tries access to an already closed 

9541 image. 

9542 

9543 """