Använd Python för WMF tecknade bilder
Skapa Python-appar för att teckna WMF-bilder och foton via server-API:er
Hur man tecknar bilder och foton i WMF med Python
Vi svarar automatiskt på tecknade bilder på grund av deras förmåga att framkalla en känsla av nostalgi. Inom sfären av grafisk design fungerar bilder i tecknad stil som centrala element som ofta ses i marknadsföringsartiklar. Denna Cartoonify-effekt innebär att konvertera fotoporträtt till handritade återgivningar, justera ljusstyrkan, konvertera till svartvitt, leka med färgpaletter och slå samman olika redigeringstekniker för att skapa intrikata visuella effekter. En uppsättning bildfilter, inklusive ‘AdjustBrightness’, ‘BinarizeFixed’, ‘Filter’, ‘ReplaceColor’ och ‘ApplyMask’, ger användare möjlighet att uppnå dessa transformationer. Dessa filter kan användas på bilder och foton i originalformat som har laddats ner. Bilder i tecknad stil är lämpliga för illustrationsändamål på olika webbsidor, för att tillföra vitalitet i vetenskapliga artiklar och göra innehåll mer tilltalande för användarna, vilket leder till ökad trafik till webbplatsen. För att skapa tecknade effekter med hjälp av WMF-bilder kommer vi att använda Aspose.Imaging for Python via .NET API som är ett funktionsrikt, kraftfullt och lättanvänt API för bildmanipulation och konvertering för Python-plattformen. Du kan installera det med följande kommando från ditt systemkommando.
Systemets kommandorad
>> pip install aspose-imaging-python-net
Steg för att Cartoonify WMFs via Python
Du behöver aspose-imaging-python-net för att prova följande arbetsflöde i din egen miljö.
- Ladda WMF-filer med Image.Load-metoden
- Cartoonify bilder;
- Spara komprimerad bild på skiva i det format som stöds av Aspose.Imaging
Systemkrav
Aspose.Imaging för Python stöds på alla större operativsystem. Se bara till att du har följande förutsättningar.
- Microsoft Windows / Linux med .NET Core Runtime.
- Python och PyPi pakethanterare.
Cartoonify WMF-bilder - 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)); | |
} | |
} |
Om Aspose.Imaging för Python API
Aspose.Imaging API är en bildbehandlingslösning för att skapa, modifiera, rita eller konvertera bilder (foton) i applikationer. Det erbjuder: plattformsoberoende bildbehandling, inklusive men inte begränsat till konverteringar mellan olika bildformat (inklusive enhetlig bildbehandling med flera sidor eller flera ramar), modifieringar som ritning, arbete med grafiska primitiver, transformationer (ändra storlek, beskära, vänd och rotera , binarisering, gråskala, justera), avancerade bildmanipuleringsfunktioner (filtrering, vibrering, maskering, avskedning) och minnesoptimeringsstrategier. Det är ett fristående bibliotek och är inte beroende av någon programvara för bildoperationer. Man kan enkelt lägga till högpresterande bildkonverteringsfunktioner med inbyggda API:er inom projekt. Dessa är 100 % privata API:er på plats och bilder bearbetas på dina servrar.Cartoonify WMFs via onlineappen
Cartoonify WMF-dokument genom att besöka vår webbplats för Live Demos . Livedemon har följande fördelar
WMF Vad är WMF Filformat
Filer med WMF-tillägg representerar Microsoft Windows Metafile (WMF) för lagring av vektor- och bitmappsformat bilddata. För att vara mer exakt tillhör WMF kategorin vektorfilformat för grafikfilformat som är enhetsoberoende. Windows Graphical Device Interface (GDI) använder funktionerna som är lagrade i en WMF-fil för att visa en bild på skärmen. En mer förbättrad version av WMF, känd som Enhanced Meta Files (EMF), publicerades senare som gör formatet mer funktionsrikt. Praktiskt taget liknar WMF SVG.
Läs merAndra Cartoonify-format som stöds
Med hjälp av Python kan man enkelt Cartoonify olika format inklusive.