Use Python para desenho animado de imagens WMF
Crie aplicativos Python para Cartoonify WMF imagens e fotos por meio de APIs de servidor
Como cartunizar imagens e fotos WMF com Python
Respondemos automaticamente às imagens de desenhos animados devido à sua capacidade de evocar uma sensação de nostalgia. No domínio do design gráfico, as imagens em estilo cartoon servem como elementos essenciais frequentemente vistos em artigos de marketing. Este efeito Cartoonify envolve a conversão de retratos fotográficos em representações desenhadas à mão, ajuste de brilho, conversão para preto e branco, brincar com paletas de cores e mesclar várias técnicas de edição para criar efeitos visuais complexos. Um conjunto de filtros de imagem, incluindo ‘AdjustBrightness’, ‘BinarizeFixed’, ‘Filter’, ‘ReplaceColor’ e ‘ApplyMask’, capacita os usuários a realizar essas transformações. Esses filtros podem ser utilizados em imagens e fotos de formato original que foram baixadas. As imagens em estilo cartoon são adequadas para fins ilustrativos em diversas páginas da web, injetando vitalidade em artigos científicos e tornando o conteúdo mais atraente para os usuários, aumentando subsequentemente o tráfego para o site. Para gerar efeitos de desenho animado usando imagens WMF, empregaremos Aspose.Imaging para Python via .NET API que é uma API de manipulação e conversão de imagens rica em recursos, poderosa e fácil de usar para a plataforma Python. Você pode instalá-lo usando o seguinte comando do comando do sistema.
A linha de comando do sistema
>> pip install aspose-imaging-python-net
Etapas para caricaturar WMFs via Python
Você precisa do aspose-imaging-python-net para tentar o seguinte fluxo de trabalho em seu próprio ambiente.
- Carregar arquivos WMF com o método Image.Load
- Cartoonify imagens;
- Salve a imagem compactada no disco no formato suportado pelo Aspose.Imaging
Requisitos de sistema
Aspose.Imaging para Python é compatível com todos os principais sistemas operacionais. Apenas certifique-se de ter os seguintes pré-requisitos.
-Microsoft Windows/Linux com .NET Core Runtime.
- Gerenciador de pacotes Python e PyPi.
Imagens do Cartoonify 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)); | |
} | |
} |
Sobre o Aspose.Imaging para a API Python
Aspose.Imaging API é uma solução de processamento de imagens para criar, modificar, desenhar ou converter imagens (fotos) dentro de aplicativos. Oferece: Processamento de imagem multiplataforma, incluindo, mas não limitado a, conversões entre vários formatos de imagem (incluindo processamento de imagem uniforme de várias páginas ou vários quadros), modificações como desenho, trabalho com primitivos gráficos, transformações (redimensionar, cortar, virar e girar , binarização, escala de cinza, ajuste), recursos avançados de manipulação de imagem (filtragem, pontilhamento, mascaramento, alinhamento) e estratégias de otimização de memória. É uma biblioteca autônoma e não depende de nenhum software para operações de imagem. Pode-se adicionar facilmente recursos de conversão de imagem de alto desempenho com APIs nativas nos projetos. Essas são APIs locais 100% privadas e as imagens são processadas em seus servidores.Cartoonify WMFs via aplicativo on-line
Cartoonify documentos WMF visitando nosso site de demonstrações ao vivo . A demonstração ao vivo tem os seguintes benefícios
WMF O que é WMF Formato de arquivo
Arquivos com extensão WMF representam o Microsoft Windows Metafile (WMF) para armazenar dados de imagens vetoriais e em formato de bitmap. Para ser mais preciso, o WMF pertence à categoria de formato de arquivo vetorial dos formatos de arquivo gráfico independente de dispositivo. A interface de dispositivo gráfico do Windows (GDI) usa as funções armazenadas em um arquivo WMF para exibir uma imagem na tela. Uma versão mais aprimorada do WMF, conhecida como Enhanced Meta Files (EMF), foi publicada posteriormente, tornando o formato mais rico em recursos. Praticamente, o WMF é semelhante ao SVG.
consulte Mais informaçãoOutros formatos de Cartoonify suportados
Usando Python, pode-se facilmente Cartoonify diferentes formatos, incluindo.