Эффект мультипликации изображений WEBP с помощью C#
Создавайте собственные приложения .NET с эффектом мультипликации файлов WEBP с помощью серверных API.
Как создать эффект мультипликации для файлов WEBP с помощью C#
Все мы неосознанно реагируем на эффекты мультипликации (Cartoonify) — возможно, они переносят нас в мир детства. Почти каждая статья о графическом дизайне включает в себя мультипликационные изображения в качестве важного элемента. Мультипликация портретов, точная настройка освещения, преобразование в черно-белое изображение, экспериментирование с цветами, смешивание различных методов редактирования и создание сложных графических эффектов — все это возможно с помощью таких фильтров изображений, как «AdjustBrightness», «BinarizeFixed», «Filter», «ReplaceColor» и «ApplyMask». Эти фильтры можно применять к исходным загруженным фотографиям. Независимо от тематики вашей веб-страницы изображения в мультипликационном стиле подойдут для иллюстративных целей. Научная статья приобретет большую живость, материалы иного характера станут более привлекательны для пользователей, тем самым увеличив посещаемость сайта. Чтобы добиться эффекта мультипликации файлов WEBP, мы будем использовать Aspose.Imaging для .NET API, который представляет собой многофункциональный, мощный и простой в использовании API для обработки и преобразования изображений для платформы C#. Откройте менеджер пакетов NuGet , найдите через поиск пакет Aspose.Imaging и установите его. Вы также можете использовать следующую команду из консоли диспетчера пакетов.
Команда консоли диспетчера пакетов
PM> Install-Package Aspose.Imaging
Шаги по мультипликации WEBP с помощью C#
Вам потребуется библиотека aspose.imaging.dll , чтобы попробовать следующий рабочий процесс в вашей собственной среде:
- Загрузить файлы WEBP с помощью метода Image.Load
- Применить эффект мультипликации для изображений и фотографий;
- Сохранение сжатого изображения на диск в поддерживаемом Aspose.Imaging формате.
Системные Требования
Aspose.Imaging для .NET поддерживается во всех основных операционных системах. Просто убедитесь, что у вас есть следующие предпосылки:
- Microsoft Windows или совместимая ОС с .NET Framework, .NET Core, приложением Windows, веб-приложением ASP.NET;
- среда разработки, такая как Microsoft Visual Studio;
- Aspose.Imaging для .NET упоминается в вашем проекте.
Эффект мультипликации изображений WEBP - .NET
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)); | |
} | |
} |
Об Aspose.Imaging for .NET API
Aspose.Imaging API — это решение для обработки изображений, позволяющее создавать, изменять, рисовать или конвертировать изображения и фотографии в приложениях. Он предлагает: кросс-платформенную обработку изображений, включая, помимо прочего, преобразования между различными форматами изображений (включая единую многостраничную или многокадровую обработку изображений), модификации, такие как рисование, работа с графическими примитивами, преобразования (изменение размера, обрезка, отражение и поворот), бинаризация, оттенки серого, настройка яркости, контрастности, расширенные функции обработки изображений (фильтрация, дизеринг, маскирование, устранение перекоса) и стратегии оптимизации памяти. Это автономная библиотека, которая не зависит от какого-либо программного обеспечения для операций с изображениями. Можно легко добавить в проекты высокопроизводительные функции преобразования изображений с помощью собственных API. Это 100% частные локальные API, а изображения обрабатываются на ваших серверах.Cartoonify WEBP через онлайн-приложение
Оживите документы WEBP эффектом мультипликации, посетив наш веб-сайт Live Demos . Online примеры имеют следующие преимущества
WEBP Чем является WEBP формат
WebP, представленный Google, представляет собой современный формат файлов растровых веб-изображений, основанный на сжатии без потерь и с потерями. Он обеспечивает такое же качество изображения при значительном уменьшении размера изображения. Поскольку большинство веб-страниц используют изображения в качестве эффективного представления данных, использование изображений WebP на веб-страницах приводит к более быстрой загрузке веб-страниц. По данным Google, изображения WebP без потерь на 26% меньше по размеру по сравнению с PNG, а изображения WebP с потерями на 25-34% меньше, чем сопоставимые изображения JPEG. Изображения сравниваются на основе индекса структурного сходства (SSIM) между WebP и другими форматами файлов изображений. WebP — это родственный проект формата мультимедийных контейнеров WebM.
Читать далееДругие поддерживаемые форматы для эффекта мультипликации (Cartoonify)
Используя C#, можно легко применять эффект мультипликации для различных форматов, в том числе: