Cartoonify JP2 via Java
Build your own Java apps to Cartoonify JP2 files using server-side APIs.
How to Cartoonify JP2 Files Using Java
Cartoon effects have an inherent appeal, often evoking nostalgic childhood memories. Nearly every graphic design article integrates cartoon images as an essential element. Cartoonifying portraits, fine-tuning lighting, converting to black and white, experimenting with colors, mixinging various editing techniques, and crafting sophisticated image effects are all achievable through image filters like AdjustBrightness, BinarizeFixed, Filter, ReplaceColor, and ApplyMask. These filters can be applied to the original loaded photos. Regardless of your webpage’s subject, Cartoon-style images prove suitable for illustration purposes. A scientific article gains vibrancy, while diverse content becomes more enticing to users, thereby boosting website traffic. In order to Cartoonify JP2 files, we’ll use Aspose.Imaging for Java API which is a feature-rich, powerful and easy to use image manipulation and conversion API for Java platform. You can download its latest version directly from Maven and install it within your Maven-based project by adding the following configurations to the pom.xml.
Repository
<repository>
<id>AsposeJavaAPI</id>
<name>Aspose Java API</name>
<url>https://repository.aspose.com/repo/</url>
</repository>
Dependency
<dependency>
<groupId>com.aspose</groupId>
<artifactId>aspose-imaging</artifactId>
<version>version of aspose-imaging API</version>
<classifier>jdk16</classifier>
</dependency>
Steps to Cartoonify JP2 via Java
You need the aspose-imaging-version-jdk16.jar to try the following workflow in your own environment.
- Load JP2 files with Image.Load method
- Cartoonify images;
- Save compressed image to disc in the supported by Aspose.Imaging format
System Requirements
Aspose.Imaging for Java is supported on all major operating systems. Just make sure that you have the following prerequisites.
- JDK 1.6 or higher is installed.
Cartoonify JP2 images - 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 } }); | |
} | |
} |
About Aspose.Imaging for Java API
Aspose.Imaging API is an image processing solution to create, modify, draw or convert images (photos) within applications. It offers: cross-platform Image processing, including but not limited to conversions between various image formats (including uniform multi-page or multi-frame image processing), modifications such as drawing, working with graphic primitives, transformations (resize, crop, flip&rotate, binarization, grayscale, adjust), advanced image manipulation features (filtering, dithering, masking, deskewing), and memory optimization strategies. It’s a standalone library and does not depend on any software for image operations. One can easily add high-performance image conversion features with native APIs within projects. These are 100% private on-premise APIs and images are processed at your servers.Cartoonify JP2 via Online App
Cartoonify JP2 documents by visiting our Live Demos website . The live demo has the following benefits
JP2 What is JP2 File Format
JPEG 2000 (JP2) is an image coding system and state-of-the-art image compression standard. Designed, using wavelet technology JPEG 2000 can code lossless content in any quality at once. Moreover, without any substantial penalty in coding efficiency, JPEG 2000 have the capability to access and decode the same content efficaciously into a variety of other resolutions and qualities. The code streams in JPEG 2000 is significantly scalable having regions of interest that provide the facility for spatial random access. Possessing Up to 16384 diverse components with the dimensions in terapixels, and precision that can be high as 38 bits/sample.
Read MoreOther Supported Cartoonify Formats
Using Java, one can easily Cartoonify different formats including.