Utilisez Python pour la caricature d’images WMF
Créez des applications Python pour caricaturer des images et des photos WMF via les API du serveur
Comment caricaturer des images et des photos WMF avec Python
Nous réagissons automatiquement aux images de dessins animés en raison de leur capacité à évoquer un sentiment de nostalgie. Dans le domaine du design graphique, les images de style dessin animé constituent des éléments essentiels souvent observés dans les articles marketing. Cet effet Cartoonify implique la conversion de portraits photo en rendus dessinés à la main, l’ajustement de la luminosité, la conversion en noir et blanc, le jeu avec les palettes de couleurs et la fusion de diverses techniques d’édition pour créer des effets visuels complexes. Une suite de filtres d’image, notamment « AdjustBrightness », « BinarizeFixed », « Filter », « ReplaceColor » et « ApplyMask », permet aux utilisateurs de réaliser ces transformations. Ces filtres peuvent être utilisés sur les images au format original et les photos téléchargées. Les images de style dessin animé conviennent à des fins d’illustration sur diverses pages Web, injectant de la vitalité dans les articles scientifiques et rendant le contenu plus attrayant pour les utilisateurs, générant ainsi un trafic accru vers le site. Pour générer des effets de dessin animé à l’aide d’images WMF, nous utiliserons Aspose.Imaging for Python via .NET API qui est une API de manipulation et de conversion d’images riche en fonctionnalités, puissante et facile à utiliser pour la plate-forme Python. Vous pouvez l’installer en utilisant la commande suivante à partir de votre commande système.
La ligne de commande système
>> pip install aspose-imaging-python-net
Étapes pour Cartoonify WMFs via Python
Vous avez besoin de aspose-imaging-python-net pour essayer le workflow suivant dans votre propre environnement.
- Charger les fichiers WMF avec la méthode Image.Load
- Dessiner des images ;
- Enregistrer l’image compressée sur le disque au format pris en charge par Aspose.Imaging
Configuration requise
Aspose.Imaging pour Python est pris en charge sur tous les principaux systèmes d’exploitation. Assurez-vous simplement que vous disposez des prérequis suivants.
- Microsoft Windows/Linux avec .NET Core Runtime.
- Gestionnaire de packages Python et PyPi.
Dessiner des images WMF - Python
using Aspose.Imaging; | |
using Aspose.Imaging.FileFormats.Png; | |
using Aspose.Imaging.ImageFilters.FilterOptions; | |
using Aspose.Imaging.ImageOptions; | |
using Aspose.Imaging.Masking; | |
using Aspose.Imaging.Masking.Options; | |
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
string templatesFolder = @"c:\Users\USER\Downloads"; | |
Cartoonify(); | |
void Cartoonify() | |
{ | |
FilterImages(image => | |
{ | |
using (var processedImage = new PngImage(image)) | |
{ | |
image.Resize(image.Width * 2, image.Height, ResizeType.LeftTopToLeftTop); | |
processedImage.Cartoonify(); | |
var gr = new Graphics(image); | |
gr.DrawImage(processedImage, processedImage.Width, 0); | |
gr.DrawLine(new Pen(Color.DarkRed, 3), processedImage.Width, 0, processedImage.Width, image.Height); | |
} | |
}, "cartoonify"); | |
} | |
string RasterizeVectorImage(string formatExt, string inputFile) | |
{ | |
string outputFile = Path.Combine(templatesFolder, $"rasterized.{formatExt}.png"); | |
using (var image = Image.Load(inputFile)) | |
{ | |
image.Save(outputFile, new PngOptions()); | |
} | |
return outputFile; | |
} | |
void FilterImages(Action<RasterImage> doFilter, string filterName) | |
{ | |
List<string> rasterFormats = new List<string>() { "jpg", "png", "bmp", "apng", "dicom", | |
"jp2", "j2k", "tga", "webp", "tif", "gif", "ico" }; | |
List<string> vectorFormats = new List<string>() { "svg", "otg", "odg", "eps", "wmf", "emf", "wmz", "emz", "cmx", "cdr" }; | |
List<string> allFormats = new List<string>(rasterFormats); | |
allFormats.AddRange(vectorFormats); | |
allFormats.ForEach( | |
formatExt => | |
{ | |
var inputFile = Path.Combine(templatesFolder, $"template.{formatExt}"); | |
bool isVectorFormat = vectorFormats.IndexOf(formatExt) > -1; | |
//Need to rasterize vector formats before background remove | |
if (isVectorFormat) | |
{ | |
inputFile = RasterizeVectorImage(formatExt, inputFile); | |
} | |
var outputFile = Path.Combine(templatesFolder, $"{filterName}_{formatExt}.png"); | |
Console.WriteLine($"Processing {formatExt}"); | |
using (var image = (RasterImage)Image.Load(inputFile)) | |
{ | |
doFilter(image); | |
//If image is multipage save each page to png to demonstrate results | |
if (image is IMultipageImage multiPage && multiPage.PageCount > 1) | |
{ | |
for (var pageIndex = 0; pageIndex < multiPage.PageCount; pageIndex++) | |
{ | |
string fileName = $"{filterName}_page{pageIndex}_{formatExt}.png"; | |
multiPage.Pages[pageIndex].Save(templatesFolder + fileName, new PngOptions()); | |
File.Delete(templatesFolder + fileName); | |
} | |
} | |
else | |
{ | |
image.Save(outputFile, new PngOptions()); | |
File.Delete(outputFile); | |
} | |
} | |
//Remove rasterized vector image | |
if (isVectorFormat) | |
{ | |
File.Delete(inputFile); | |
} | |
} | |
); | |
} | |
static class ImageFilterExtensions | |
{ | |
public static void Cartoonify(this RasterImage image) | |
{ | |
using var outlines = image.DetectOutlines(Color.Black); | |
image.AdjustBrightness(30); | |
image.Filter(image.Bounds, new MedianFilterOptions(7)); | |
var gr = new Graphics(image); | |
gr.DrawImage(outlines, Point.Empty); | |
} | |
public static RasterImage DetectOutlines(this RasterImage image, Color outlineColor) | |
{ | |
var outlines = new PngImage(image); | |
outlines | |
.GetDataContext() | |
.ApplyConvolutionFilter(ConvolutionFilterOptions.Blur) | |
.ApplyConvolutionFilter(ConvolutionFilterOptions.Outline) | |
.ApplyData(); | |
outlines.BinarizeFixed(30); | |
ImageMasking.ApplyMask(outlines, outlines, new MaskingOptions() { BackgroundReplacementColor = Color.Transparent }); | |
outlines.ReplaceColor(Color.FromArgb(255, 255, 255), 0, outlineColor); | |
outlines.ApplyConvolutionFilter(ConvolutionFilterOptions.Blur); | |
return outlines; | |
} | |
public static RasterImage ApplyOperationToRasterImage(this RasterImage image, Action<RasterImage> operation) | |
{ | |
if (image is IMultipageImage multipage) | |
{ | |
foreach (var page in multipage.Pages) | |
{ | |
operation.Invoke((RasterImage)page); | |
} | |
} | |
else | |
{ | |
operation.Invoke(image); | |
} | |
return image; | |
} | |
public static RasterImage ApplyFilter(this RasterImage image, FilterOptionsBase filterOptions) | |
{ | |
return image.ApplyOperationToRasterImage(img => | |
{ | |
img.Filter(img.Bounds, filterOptions); | |
}); | |
} | |
public static RasterImage ApplyConvolutionFilter(this RasterImage image, ConvolutionFilterOptions filterOptions) | |
{ | |
return image.ApplyOperationToRasterImage(img => | |
{ | |
var pixelsLoader = new ImagePixelsLoader(img.Bounds); | |
img.LoadPartialArgb32Pixels(img.Bounds, pixelsLoader); | |
var outBuffer = new PixelBuffer(img.Bounds, new int[img.Width * img.Height]); | |
ConvolutionFilter.DoFiltering(pixelsLoader.PixelsBuffer, outBuffer, filterOptions); | |
img.SaveArgb32Pixels(outBuffer.Rectangle, outBuffer.Pixels); | |
}); | |
} | |
public static IImageDataContext GetDataContext(this RasterImage image) | |
{ | |
IPixelBuffer GetImageBuffer(RasterImage img) | |
{ | |
var pixelsLoader = new ImagePixelsLoader(img.Bounds); | |
img.LoadPartialArgb32Pixels(img.Bounds, pixelsLoader); | |
return pixelsLoader.PixelsBuffer; | |
} | |
if (image is IMultipageImage multipage) | |
{ | |
return new MultipageDataContext( | |
multipage.Pages.Select(page => new ImageDataContext((RasterImage)page) | |
{ | |
Buffer = GetImageBuffer((RasterImage)page) | |
})); | |
} | |
return new ImageDataContext(image) | |
{ | |
Buffer = GetImageBuffer(image) | |
}; | |
} | |
public static IImageDataContext ApplyToDataContext(this IImageDataContext dataContext, | |
Func<IPixelBuffer, IPixelBuffer> processor) | |
{ | |
if (dataContext is MultipageDataContext multipage) | |
{ | |
foreach (var context in multipage) | |
{ | |
context.Buffer = processor.Invoke(context.Buffer); | |
} | |
} | |
if (dataContext is ImageDataContext imageDataContext) | |
{ | |
imageDataContext.Buffer = processor.Invoke(imageDataContext.Buffer); | |
} | |
return dataContext; | |
} | |
public static IImageDataContext ApplyConvolutionFilter(this IImageDataContext dataContext, | |
ConvolutionFilterOptions filterOptions) | |
{ | |
return dataContext.ApplyToDataContext(buffer => | |
{ | |
var outBuffer = new PixelBuffer(buffer.Rectangle, new int[buffer.Rectangle.Width * buffer.Rectangle.Height]); | |
ConvolutionFilter.DoFiltering(buffer, outBuffer, filterOptions); | |
return outBuffer; | |
}); | |
} | |
} | |
class ConvolutionFilter | |
{ | |
public static void DoFiltering( | |
IPixelBuffer inputBuffer, | |
IPixelBuffer outputBuffer, | |
ConvolutionFilterOptions options) | |
{ | |
var factor = options.Factor; | |
var bias = options.Bias; | |
var kernel = options.Kernel; | |
var filterWidth = kernel.GetLength(1); | |
var filterCenter = (filterWidth - 1) / 2; | |
int x, y; | |
int filterX, filterY, filterPx, filterPy, filterYPos, pixel; | |
double r, g, b, kernelValue; | |
int top = inputBuffer.Rectangle.Top; | |
int bottom = inputBuffer.Rectangle.Bottom; | |
int left = inputBuffer.Rectangle.Left; | |
int right = inputBuffer.Rectangle.Right; | |
for (y = top; y < bottom; y++) | |
{ | |
for (x = left; x < right; x++) | |
{ | |
r = 0; | |
g = 0; | |
b = 0; | |
for (filterY = -filterCenter; filterY <= filterCenter; filterY++) | |
{ | |
filterYPos = filterY + filterCenter; | |
filterPy = filterY + y; | |
if (filterPy >= top && filterPy < bottom) | |
{ | |
for (filterX = -filterCenter; filterX <= filterCenter; filterX++) | |
{ | |
filterPx = filterX + x; | |
if (filterPx >= left && filterPx < right) | |
{ | |
kernelValue = kernel[filterYPos, filterX + filterCenter]; | |
pixel = inputBuffer[filterPx, filterPy]; | |
r += ((pixel >> 16) & 0xFF) * kernelValue; | |
g += ((pixel >> 8) & 0xFF) * kernelValue; | |
b += (pixel & 0xFF) * kernelValue; | |
} | |
} | |
} | |
} | |
r = (factor * r) + bias; | |
g = (factor * g) + bias; | |
b = (factor * b) + bias; | |
r = r > 255 ? 255 : (r < 0 ? 0 : r); | |
g = g > 255 ? 255 : (g < 0 ? 0 : g); | |
b = b > 255 ? 255 : (b < 0 ? 0 : b); | |
outputBuffer[x, y] = ((inputBuffer[x, y] >> 24) << 24) | ((byte)r << 16) | ((byte)g << 8) | (byte)b; | |
} | |
} | |
} | |
} | |
class ConvolutionFilterOptions | |
{ | |
public double Factor { get; set; } = 1.0; | |
public int Bias { get; set; } = 0; | |
public double[,] Kernel { get; set; } | |
public static ConvolutionFilterOptions Blur | |
{ | |
get | |
{ | |
return new ConvolutionFilterOptions | |
{ | |
Kernel = new double[,] { { 1, 2, 1 }, { 2, 4, 2 }, { 1, 2, 1 } }, | |
Factor = 0.25 * 0.25 | |
}; | |
} | |
} | |
public static ConvolutionFilterOptions Sharpen | |
{ | |
get | |
{ | |
return new ConvolutionFilterOptions | |
{ | |
Kernel = new double[,] { { 0, -1, 0 }, { -1, 5, -1 }, { 0, -1, 0 } } | |
}; | |
} | |
} | |
public static ConvolutionFilterOptions Emboss | |
{ | |
get | |
{ | |
return new ConvolutionFilterOptions | |
{ | |
Kernel = new double[,] { { -2, -1, 0 }, { -1, 1, 1 }, { 0, 1, 2 } } | |
}; | |
} | |
} | |
public static ConvolutionFilterOptions Outline | |
{ | |
get | |
{ | |
return new ConvolutionFilterOptions | |
{ | |
Kernel = new double[,] { { -1, -1, -1 }, { -1, 8, -1 }, { -1, -1, -1 } } | |
}; | |
} | |
} | |
public static ConvolutionFilterOptions BottomSobel | |
{ | |
get | |
{ | |
return new ConvolutionFilterOptions | |
{ | |
Kernel = new double[,] { { -1, -2, -1 }, { 0, 0, 0 }, { 1, 2, 1 } } | |
}; | |
} | |
} | |
public static ConvolutionFilterOptions TopSobel | |
{ | |
get | |
{ | |
return new ConvolutionFilterOptions | |
{ | |
Kernel = new double[,] { { 1, 2, 1 }, { 0, 0, 0 }, { -1, -2, -1 } } | |
}; | |
} | |
} | |
public static ConvolutionFilterOptions LeftSobel | |
{ | |
get | |
{ | |
return new ConvolutionFilterOptions | |
{ | |
Kernel = new double[,] { { 1, 0, -1 }, { 2, 0, -2 }, { 1, 0, -1 } } | |
}; | |
} | |
} | |
public static ConvolutionFilterOptions RightSobel | |
{ | |
get | |
{ | |
return new ConvolutionFilterOptions | |
{ | |
Kernel = new double[,] { { -1, 0, 1 }, { -2, 0, 2 }, { -1, 0, 1 } } | |
}; | |
} | |
} | |
} | |
interface IImageDataContext | |
{ | |
void ApplyData(); | |
} | |
class ImageDataContext : IImageDataContext | |
{ | |
public ImageDataContext(RasterImage image) | |
{ | |
this.Image = image; | |
} | |
public RasterImage Image { get; } | |
public IPixelBuffer Buffer { get; set; } | |
public void ApplyData() | |
{ | |
this.Buffer.SaveToImage(this.Image); | |
} | |
} | |
class MultipageDataContext : List<ImageDataContext>, IImageDataContext | |
{ | |
public MultipageDataContext(IEnumerable<ImageDataContext> enumerable) : base(enumerable) | |
{ | |
} | |
public void ApplyData() | |
{ | |
foreach (var context in this) | |
{ | |
context.ApplyData(); | |
} | |
} | |
} | |
class ImagePixelsLoader : IPartialArgb32PixelLoader | |
{ | |
public ImagePixelsLoader(Aspose.Imaging.Rectangle rectangle) | |
{ | |
this.PixelsBuffer = new CompositePixelBuffer(rectangle); | |
} | |
public CompositePixelBuffer PixelsBuffer { get; } | |
public void Process(Aspose.Imaging.Rectangle pixelsRectangle, int[] pixels, Point start, Point end) | |
{ | |
this.PixelsBuffer.AddPixels(pixelsRectangle, pixels); | |
} | |
} | |
interface IPixelBuffer | |
{ | |
Aspose.Imaging.Rectangle Rectangle { get; } | |
int this[int x, int y] | |
{ | |
get; | |
set; | |
} | |
void SaveToImage(RasterImage image); | |
} | |
class PixelBuffer : IPixelBuffer | |
{ | |
public PixelBuffer(Aspose.Imaging.Rectangle rectangle, int[] pixels) | |
{ | |
this.Rectangle = rectangle; | |
this.Pixels = pixels; | |
} | |
public Aspose.Imaging.Rectangle Rectangle { get; } | |
public int[] Pixels { get; } | |
public int this[int x, int y] | |
{ | |
get => this.Pixels[this.GetIndex(x, y)]; | |
set => this.Pixels[this.GetIndex(x, y)] = value; | |
} | |
public void SaveToImage(RasterImage image) | |
{ | |
image.SaveArgb32Pixels(this.Rectangle, this.Pixels); | |
} | |
public bool Contains(int x, int y) | |
{ | |
return this.Rectangle.Contains(x, y); | |
} | |
private int GetIndex(int x, int y) | |
{ | |
x -= this.Rectangle.Left; | |
y -= this.Rectangle.Top; | |
return x + y * this.Rectangle.Width; | |
} | |
} | |
class CompositePixelBuffer : IPixelBuffer | |
{ | |
private readonly List<PixelBuffer> _buffers = new List<PixelBuffer>(); | |
public CompositePixelBuffer(Aspose.Imaging.Rectangle rectangle) | |
{ | |
this.Rectangle = rectangle; | |
} | |
public Aspose.Imaging.Rectangle Rectangle { get; } | |
public int this[int x, int y] | |
{ | |
get => this.GetBuffer(x, y)[x, y]; | |
set => this.GetBuffer(x, y)[x, y] = value; | |
} | |
public void SaveToImage(RasterImage image) | |
{ | |
foreach (var pixelBuffer in this._buffers) | |
{ | |
pixelBuffer.SaveToImage(image); | |
} | |
} | |
public IEnumerable<PixelBuffer> Buffers => this._buffers; | |
public void AddPixels(Aspose.Imaging.Rectangle rectangle, int[] pixels) | |
{ | |
if (this.Rectangle.IntersectsWith(rectangle)) | |
{ | |
this._buffers.Add(new PixelBuffer(rectangle, pixels)); | |
} | |
} | |
private PixelBuffer GetBuffer(int x, int y) | |
{ | |
return this._buffers.First(b => b.Contains(x, y)); | |
} | |
} |
À propos de l'API Aspose.Imaging pour Python
Aspose.Imaging API est une solution de traitement d’images pour créer, modifier, dessiner ou convertir des images (photos) au sein d’applications. Il offre : le traitement d’image multiplateforme, y compris, mais sans s’y limiter, les conversions entre différents formats d’image (y compris le traitement d’image multipage ou multicadre uniforme), les modifications telles que le dessin, l’utilisation de primitives graphiques, les transformations (redimensionner, recadrer, retourner et faire pivoter , binarisation, niveaux de gris, ajustement), fonctionnalités avancées de manipulation d’images (filtrage, tramage, masquage, redressement) et stratégies d’optimisation de la mémoire. C’est une bibliothèque autonome et ne dépend d’aucun logiciel pour les opérations d’image. On peut facilement ajouter des fonctionnalités de conversion d’image hautes performances avec des API natives dans les projets. Ce sont des API sur site 100 % privées et les images sont traitées sur vos serveurs.Cartoonify WMFs via l’application en ligne
Dessinez des documents WMF en visitant notre site Web de démonstrations en direct . La démo en direct présente les avantages suivants
WMF Qu'est-ce que WMF Format de fichier
Les fichiers avec l'extension WMF représentent le métafichier Microsoft Windows (WMF) pour stocker des données d'images vectorielles et bitmap. Pour être plus précis, WMF appartient à la catégorie des formats de fichiers vectoriels des formats de fichiers graphiques indépendants de l'appareil. L'interface de périphérique graphique Windows (GDI) utilise les fonctions stockées dans un fichier WMF pour afficher une image à l'écran. Une version plus améliorée de WMF, connue sous le nom de Enhanced Meta Files (EMF), a été publiée plus tard, ce qui rend le format plus riche en fonctionnalités. Pratiquement, WMF est similaire à SVG.
Lire la suiteAutres formats Cartoonify pris en charge
En utilisant Python, on peut facilement Cartoonifier différents formats, y compris.