Cartoonify APNG a través de Java
Cree sus propias aplicaciones Java para archivos Cartoonify APNG utilizando las API del lado del servidor.
Cómo caricaturizar archivos APNG usando Java
Los efectos de dibujos animados tienen un atractivo inherente y a menudo evocan recuerdos nostálgicos de la infancia. Casi todos los artículos de diseño gráfico integran imágenes de dibujos animados como elemento esencial. Caricaturizar retratos, ajustar la iluminación, convertir a blanco y negro, experimentar con colores, mezclar varias técnicas de edición y crear efectos de imagen sofisticados se pueden lograr a través de filtros de imagen como AjustarBrillo, BinarizeFixed, Filtro, ReemplazarColor y AplicarMask. Estos filtros se pueden aplicar a las fotos cargadas originales. Independientemente del tema de su página web, las imágenes de estilo caricatura resultan adecuadas para fines ilustrativos. Un artículo científico gana vitalidad, mientras que el contenido diverso se vuelve más atractivo para los usuarios, aumentando así el tráfico del sitio web. Para caricaturizar archivos APNG, usaremos Aspose.Imaging para Java API que es una API de conversióny manipulación de imágenes rica en funciones, potente y fácil de usar para la plataforma Java. Puedes descargar su última versión directamente desde Maven e instálelo dentro de su proyecto basado en Maven agregando las siguientes configuraciones al pom.xml.
Repositorio
<repository>
<id>AsposeJavaAPI</id>
<name>Aspose Java API</name>
<url>https://repository.aspose.com/repo/</url>
</repository>
Dependencia
<dependency>
<groupId>com.aspose</groupId>
<artifactId>aspose-imaging</artifactId>
<version>version of aspose-imaging API</version>
<classifier>jdk16</classifier>
</dependency>
Pasos para caricaturizar APNG a través de Java
necesitas el aspose-imaging-version-jdk16.jar para probar el siguiente flujo de trabajo en su propio entorno.
- Cargue archivos APNG con el método Image.Load
- Imágenes de dibujos animados;
- Guarde la imagen comprimida en el disco en el formato compatible con Aspose.Imaging
Requisitos del sistema
Aspose.Imaging para Java es compatible con todos los principales sistemas operativos. Solo asegúrese de tener los siguientes requisitos previos.
- Está instalado JDK 1.6 o superior.
Imágenes de Cartoonify APNG - Java
import com.aspose.imaging.*; | |
import com.aspose.imaging.fileformats.png.PngImage; | |
import com.aspose.imaging.imagefilters.filteroptions.FilterOptionsBase; | |
import com.aspose.imaging.imagefilters.filteroptions.MedianFilterOptions; | |
import com.aspose.imaging.imageoptions.PngOptions; | |
import com.aspose.imaging.masking.ImageMasking; | |
import com.aspose.imaging.masking.options.MaskingOptions; | |
import java.io.File; | |
import java.util.*; | |
import java.util.function.Consumer; | |
import java.util.function.Function; | |
import java.util.stream.Collectors; | |
cartoonify(); | |
public static void cartoonify() | |
{ | |
filterImages(image -> | |
{ | |
try (PngImage processedImage = new PngImage(image)) | |
{ | |
image.resize(image.getWidth() * 2, image.getHeight(), ResizeType.LeftTopToLeftTop); | |
ImageFilterExtensions.cartoonify(processedImage); | |
Graphics gr = new Graphics(image); | |
gr.drawImage(processedImage, processedImage.getWidth(), 0); | |
gr.drawLine(new Pen(Color.getDarkRed(), 3), processedImage.getWidth(), 0, processedImage.getWidth(), image.getHeight()); | |
} | |
}, "cartoonify"); | |
} | |
static String templatesFolder = "D:\\TestData\\"; | |
public static void filterImages(Consumer<RasterImage> doFilter, String filterName) | |
{ | |
List<String> rasterFormats = Arrays.asList("jpg", "png", "bmp", "apng", "dicom", | |
"jp2", "j2k", "tga", "webp", "tif", "gif", "ico"); | |
List<String> vectorFormats = Arrays.asList("svg", "otg", "odg", "eps", "wmf", "emf", "wmz", "emz", "cmx", "cdr"); | |
List<String> allFormats = new LinkedList<>(rasterFormats); | |
allFormats.addAll(vectorFormats); | |
allFormats.forEach( | |
formatExt -> | |
{ | |
String inputFile = templatesFolder + "template." + formatExt; | |
boolean isVectorFormat = vectorFormats.contains(formatExt); | |
//Need to rasterize vector formats before background remove | |
if (isVectorFormat) | |
{ | |
inputFile = rasterizeVectorImage(formatExt, inputFile); | |
} | |
String outputFile = templatesFolder + String.format("%s_%s.png", filterName, formatExt); | |
System.out.println("Processing " + formatExt); | |
try (RasterImage image = (RasterImage) Image.load(inputFile)) | |
{ | |
doFilter.accept(image); | |
//If image is multipage save each page to png to demonstrate results | |
if (image instanceof IMultipageImage && ((IMultipageImage) image).getPageCount() > 1) | |
{ | |
IMultipageImage multiPage = (IMultipageImage) image; | |
final int pageCount = multiPage.getPageCount(); | |
final Image[] pages = multiPage.getPages(); | |
for (int pageIndex = 0; pageIndex < pageCount; pageIndex++) | |
{ | |
String fileName = String.format("%s_page%d_%s.png", filterName, pageIndex, formatExt); | |
pages[pageIndex].save(fileName, new PngOptions()); | |
} | |
} | |
else | |
{ | |
image.save(outputFile, new PngOptions()); | |
} | |
} | |
//Remove rasterized vector image | |
if (isVectorFormat) | |
{ | |
new File(inputFile).delete(); | |
} | |
} | |
); | |
} | |
private static String rasterizeVectorImage(String formatExt, String inputFile) | |
{ | |
String outputFile = templatesFolder + "rasterized." + formatExt + ".png"; | |
try (Image image = Image.load(inputFile)) | |
{ | |
image.save(outputFile, new PngOptions()); | |
} | |
return outputFile; | |
} | |
interface IImageDataContext | |
{ | |
void applyData(); | |
} | |
class ImageFilterExtensions | |
{ | |
public static void cartoonify(RasterImage image) | |
{ | |
try (RasterImage outlines = detectOutlines(image, Color.getBlack())) | |
{ | |
image.adjustBrightness(30); | |
image.filter(image.getBounds(), new MedianFilterOptions(7)); | |
Graphics gr = new Graphics(image); | |
gr.drawImage(outlines, Point.getEmpty()); | |
} | |
} | |
public static RasterImage detectOutlines(RasterImage image, Color outlineColor) | |
{ | |
PngImage outlines = new PngImage(image); | |
IImageDataContext ctx = getDataContext(outlines); | |
applyConvolutionFilter(ctx, ConvolutionFilterOptions.getBlur()); | |
applyConvolutionFilter(ctx, ConvolutionFilterOptions.getOutline()); | |
ctx.applyData(); | |
outlines.binarizeFixed((byte)30); | |
ImageMasking.applyMask(outlines, outlines, new MaskingOptions() | |
{{ | |
setBackgroundReplacementColor(Color.getTransparent()); | |
}}); | |
outlines.replaceColor(Color.fromArgb(255, 255, 255), (byte)0, outlineColor); | |
applyConvolutionFilter(outlines, ConvolutionFilterOptions.getBlur()); | |
return outlines; | |
} | |
public static RasterImage applyOperationToRasterImage(RasterImage image, Consumer<RasterImage> operation) | |
{ | |
if (image instanceof IMultipageImage) | |
{ | |
IMultipageImage multipage = (IMultipageImage) image; | |
for (Image page : multipage.getPages()) | |
{ | |
operation.accept((RasterImage) page); | |
} | |
} | |
else | |
{ | |
operation.accept(image); | |
} | |
return image; | |
} | |
public static RasterImage applyFilter(RasterImage image, FilterOptionsBase filterOptions) | |
{ | |
return applyOperationToRasterImage(image, img -> | |
img.filter(img.getBounds(), filterOptions)); | |
} | |
public static RasterImage applyConvolutionFilter(RasterImage image, ConvolutionFilterOptions filterOptions) | |
{ | |
return applyOperationToRasterImage(image, img -> | |
{ | |
ImagePixelsLoader pixelsLoader = new ImagePixelsLoader(img.getBounds()); | |
img.loadPartialArgb32Pixels(img.getBounds(), pixelsLoader); | |
PixelBuffer outBuffer = new PixelBuffer(img.getBounds(), new int[img.getWidth() * img.getHeight()]); | |
ConvolutionFilter.doFiltering(pixelsLoader.getPixelsBuffer(), outBuffer, filterOptions); | |
img.saveArgb32Pixels(outBuffer.getRectangle(), outBuffer.getPixels()); | |
}); | |
} | |
public static IImageDataContext getDataContext(RasterImage image) | |
{ | |
if (image instanceof IMultipageImage) | |
{ | |
return new MultipageDataContext( | |
Arrays.stream(((IMultipageImage)image).getPages()).map(page -> { | |
ImageDataContext buf = new ImageDataContext((RasterImage) page); | |
buf.setBuffer(getImageBuffer((RasterImage)page)); | |
return buf; | |
}).collect(Collectors.toList())); | |
} | |
ImageDataContext buf = new ImageDataContext(image); | |
buf.setBuffer(getImageBuffer(image)); | |
return buf; | |
} | |
static IPixelBuffer getImageBuffer(RasterImage img) | |
{ | |
ImagePixelsLoader pixelsLoader = new ImagePixelsLoader(img.getBounds()); | |
img.loadPartialArgb32Pixels(img.getBounds(), pixelsLoader); | |
return pixelsLoader.getPixelsBuffer(); | |
} | |
public static IImageDataContext applyToDataContext(IImageDataContext dataContext, | |
Function<IPixelBuffer, IPixelBuffer> processor) | |
{ | |
if (dataContext instanceof MultipageDataContext) | |
{ | |
for (ImageDataContext context : (MultipageDataContext) dataContext) | |
{ | |
context.setBuffer(processor.apply(context.getBuffer())); | |
} | |
} | |
if (dataContext instanceof ImageDataContext) | |
{ | |
ImageDataContext ctx = (ImageDataContext)dataContext; | |
ctx.setBuffer(processor.apply(ctx.getBuffer())); | |
} | |
return dataContext; | |
} | |
public static IImageDataContext applyConvolutionFilter(IImageDataContext dataContext, | |
ConvolutionFilterOptions filterOptions) | |
{ | |
return applyToDataContext(dataContext, buffer -> | |
{ | |
PixelBuffer outBuffer = new PixelBuffer(buffer.getRectangle(), new int[buffer.getRectangle().getWidth() * buffer.getRectangle().getHeight()]); | |
ConvolutionFilter.doFiltering(buffer, outBuffer, filterOptions); | |
return outBuffer; | |
}); | |
} | |
} | |
class ImageDataContext implements IImageDataContext | |
{ | |
private final RasterImage image; | |
private IPixelBuffer buffer; | |
public ImageDataContext(RasterImage image) | |
{ | |
this.image = image; | |
} | |
public RasterImage getImage() | |
{ | |
return image; | |
} | |
public IPixelBuffer getBuffer() | |
{ | |
return buffer; | |
} | |
public void setBuffer(IPixelBuffer buffer) | |
{ | |
this.buffer = buffer; | |
} | |
public void applyData() | |
{ | |
this.buffer.saveToImage(this.image); | |
} | |
} | |
class MultipageDataContext extends LinkedList<ImageDataContext> implements IImageDataContext | |
{ | |
public MultipageDataContext(Collection<ImageDataContext> enumerable) | |
{ | |
addAll(enumerable); | |
} | |
public void applyData() | |
{ | |
for (ImageDataContext context : this) | |
{ | |
context.applyData(); | |
} | |
} | |
} | |
class ImagePixelsLoader implements IPartialArgb32PixelLoader | |
{ | |
private final CompositePixelBuffer pixelsBuffer; | |
public ImagePixelsLoader(Rectangle rectangle) | |
{ | |
this.pixelsBuffer = new CompositePixelBuffer(rectangle); | |
} | |
public CompositePixelBuffer getPixelsBuffer() | |
{ | |
return pixelsBuffer; | |
} | |
@Override | |
public void process(Rectangle pixelsRectangle, int[] pixels, Point start, Point end) | |
{ | |
this.pixelsBuffer.addPixels(pixelsRectangle,pixels); | |
} | |
} | |
interface IPixelBuffer | |
{ | |
Rectangle getRectangle(); | |
int get(int x, int y); | |
void set(int x, int y, int value); | |
void saveToImage(RasterImage image); | |
} | |
class PixelBuffer implements IPixelBuffer | |
{ | |
private final Rectangle rectangle; | |
private final int[] pixels; | |
public PixelBuffer(Rectangle rectangle,int[] pixels) | |
{ | |
this.rectangle = rectangle; | |
this.pixels = pixels; | |
} | |
@Override | |
public com.aspose.imaging.Rectangle getRectangle() | |
{ | |
return rectangle; | |
} | |
public int[] getPixels() | |
{ | |
return pixels; | |
} | |
@Override | |
public int get(int x, int y) | |
{ | |
return pixels[getIndex(x,y)]; | |
} | |
@Override | |
public void set(int x, int y, int value) | |
{ | |
pixels[getIndex(x,y)] = value; | |
} | |
public void saveToImage(RasterImage image) | |
{ | |
image.saveArgb32Pixels(this.rectangle, this.pixels); | |
} | |
public boolean contains(int x,int y) | |
{ | |
return this.rectangle.contains(x,y); | |
} | |
private int getIndex(int x,int y) | |
{ | |
x -= this.rectangle.getLeft(); | |
y -= this.rectangle.getTop(); | |
return x + y * this.rectangle.getWidth(); | |
} | |
} | |
class CompositePixelBuffer implements IPixelBuffer | |
{ | |
private final List<PixelBuffer> _buffers = new ArrayList<>(); | |
private final Rectangle rectangle; | |
public CompositePixelBuffer(Rectangle rectangle) | |
{ | |
this.rectangle = rectangle; | |
} | |
@Override | |
public com.aspose.imaging.Rectangle getRectangle() | |
{ | |
return rectangle; | |
} | |
@Override | |
public int get(int x, int y) | |
{ | |
return getBuffer(x,y).get(x, y); | |
} | |
@Override | |
public void set(int x, int y, int value) | |
{ | |
getBuffer(x, y).set(x, y, value); | |
} | |
@Override | |
public void saveToImage(RasterImage image) | |
{ | |
for (PixelBuffer buffer : this._buffers) | |
{ | |
buffer.saveToImage(image); | |
} | |
} | |
public void addPixels(Rectangle rectangle,int[] pixels) | |
{ | |
if(rectangle.intersectsWith(rectangle)) | |
{ | |
this._buffers.add(new PixelBuffer(rectangle,pixels)); | |
} | |
} | |
private PixelBuffer getBuffer(int x,int y) | |
{ | |
return this._buffers.stream().filter(b -> b.contains(x,y)).findFirst().get(); | |
} | |
} | |
class ConvolutionFilter | |
{ | |
public static void doFiltering( | |
IPixelBuffer inputBuffer, | |
IPixelBuffer outputBuffer, | |
ConvolutionFilterOptions options) | |
{ | |
double factor = options.getFactor(); | |
int bias = options.getBias(); | |
double[][] kernel = options.getKernel(); | |
int filterWidth = kernel[0].length; | |
int filterCenter = (filterWidth - 1) / 2; | |
int x, y; | |
int filterX, filterY, filterPx, filterPy, filterYPos, pixel; | |
double r, g, b, kernelValue; | |
int top = inputBuffer.getRectangle().getTop(); | |
int bottom = inputBuffer.getRectangle().getBottom(); | |
int left = inputBuffer.getRectangle().getLeft(); | |
int right = inputBuffer.getRectangle().getRight(); | |
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.get(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.set(x, y, (inputBuffer.get(x, y) & 0xFF000000) | ((int)r << 16) | ((int)g << 8) | (int)b); | |
} | |
} | |
} | |
} | |
class ConvolutionFilterOptions | |
{ | |
private double factor = 1.0; | |
public double getFactor() | |
{ | |
return factor; | |
} | |
public void setFactor(double factor) | |
{ | |
this.factor = factor; | |
} | |
private int bias = 0; | |
public int getBias() | |
{ | |
return bias; | |
} | |
public void setBias(int bias) | |
{ | |
this.bias = bias; | |
} | |
private double[][] kernel; | |
public double[][] getKernel() | |
{ | |
return kernel; | |
} | |
public void setKernel(double[][] kernel) | |
{ | |
this.kernel = kernel; | |
} | |
public ConvolutionFilterOptions() | |
{ | |
} | |
public ConvolutionFilterOptions(double[][] kernel) | |
{ | |
this.kernel = kernel; | |
} | |
public static ConvolutionFilterOptions getBlur() | |
{ | |
ConvolutionFilterOptions filterOptions = new ConvolutionFilterOptions(); | |
filterOptions.setKernel(new double[][] { { 1, 2, 1 }, { 2, 4, 2 }, { 1, 2, 1 } }); | |
filterOptions.setFactor(0.25 * 0.25); | |
return filterOptions; | |
} | |
public static ConvolutionFilterOptions getSharpen() | |
{ | |
return new ConvolutionFilterOptions(new double[][] { { 0, -1, 0 }, { -1, 5, -1 }, { 0, -1, 0 } }); | |
} | |
public static ConvolutionFilterOptions getEmboss() | |
{ | |
return new ConvolutionFilterOptions(new double[][] { { -2, -1, 0 }, { -1, 1, 1 }, { 0, 1, 2 } }); | |
} | |
public static ConvolutionFilterOptions getOutline() | |
{ | |
return new ConvolutionFilterOptions(new double[][] { { -1, -1, -1 }, { -1, 8, -1 }, { -1, -1, -1 } }); | |
} | |
public static ConvolutionFilterOptions getBottomSobel() | |
{ | |
return new ConvolutionFilterOptions(new double[][] { { -1, -2, -1 }, { 0, 0, 0 }, { 1, 2, 1 } }); | |
} | |
public static ConvolutionFilterOptions getTopSobel() | |
{ | |
return new ConvolutionFilterOptions(new double[][] { { 1, 2, 1 }, { 0, 0, 0 }, { -1, -2, -1 } }); | |
} | |
public static ConvolutionFilterOptions getLeftSobel() | |
{ | |
return new ConvolutionFilterOptions(new double[][] { { 1, 0, -1 }, { 2, 0, -2 }, { 1, 0, -1 } }); | |
} | |
public static ConvolutionFilterOptions getRightSobel() | |
{ | |
return new ConvolutionFilterOptions(new double[][] { { -1, 0, 1 }, { -2, 0, 2 }, { -1, 0, 1 } }); | |
} | |
} |
Acerca de Aspose.Imaging para Java API
Aspose.Imaging API es una solución de procesamiento de imágenes para crear, modificar, dibujar o convertir imágenes (fotos) dentro de las aplicaciones. Ofrece: procesamiento de imágenes multiplataforma, que incluye, entre otros, conversiones entre varios formatos de imagen (incluido el procesamiento uniforme de imágenes de varias páginas o varios cuadros), modificaciones como dibujar, trabajar con primitivas gráficas, transformaciones (redimensionar, recortar, voltear y rotar , binarización, escala de grises, ajuste), funciones avanzadas de manipulación de imágenes (filtrado, difuminado, enmascaramiento, corrección del sesgo) y estrategias de optimización de la memoria. Es una biblioteca independiente y no depende de ningún software para las operaciones de imagen. Uno puede agregar fácilmente funciones de conversión de imágenes de alto rendimiento con API nativas dentro de los proyectos. Estas son API locales 100 % privadas y las imágenes se procesan en sus servidores.Cartoonify APNGs a través de la aplicación en línea
Cartoonify APNG documentos visitando nuestro [sitio web de demostraciones en vivo] ( https://products.aspose.app/imaging/image-Cartoonify) . La demostración en vivo tiene los siguientes beneficios
APNG Qué es APNG Formato de archivo
Un archivo con la extensión .apng (Animated Portable Network Graphics) es un formato gráfico de trama y es una extensión no oficial de Portable Network Graphic (PNG). Se compone de múltiples fotogramas (cada uno de imagen PNG) que representa una secuencia de animación. Esto proporciona una visualización similar a la de un archivo GIF. Los archivos APNG admiten imágenes de 24 bits y transparencia de 8 bits. APNG es compatible con versiones anteriores de archivos GIF no animados. Los archivos APNG usan la misma extensión .png y pueden abrirse con aplicaciones como Mozilla Firefox, Chrome con compatibilidad con APNG, aplicaciones de iMessage para iOS 10.
Leer másOtros formatos de Cartoonify compatibles
Usando Java, uno puede caricaturizar fácilmente diferentes formatos, incluidos.