问题
I'm trying to pixelate a JPEG with Java 6 and not having much luck. It needs to be with Java - not an image manipulation program like Photoshop, and it needs to come out looking old school - like this:
Can anybody help me?
回答1:
Using the java.awt.image
(javadoc) and javax.imageio
(javadoc) APIs, you can easily loop through the image's pixels and perform the pixelation yourself.
Example code follows. You will need at least these imports: javax.imageio.ImageIO
, java.awt.image.BufferedImage
, java.awt.image.Raster
, java.awt.image.WritableRaster
, and java.io.File
.
Example:
// How big should the pixelations be?
final int PIX_SIZE = 10;
// Read the file as an Image
img = ImageIO.read(new File("image.jpg"));
// Get the raster data (array of pixels)
Raster src = img.getData();
// Create an identically-sized output raster
WritableRaster dest = src.createCompatibleWritableRaster();
// Loop through every PIX_SIZE pixels, in both x and y directions
for(int y = 0; y < src.getHeight(); y += PIX_SIZE) {
for(int x = 0; x < src.getWidth(); x += PIX_SIZE) {
// Copy the pixel
double[] pixel = new double[3];
pixel = src.getPixel(x, y, pixel);
// "Paste" the pixel onto the surrounding PIX_SIZE by PIX_SIZE neighbors
// Also make sure that our loop never goes outside the bounds of the image
for(int yd = y; (yd < y + PIX_SIZE) && (yd < dest.getHeight()); yd++) {
for(int xd = x; (xd < x + PIX_SIZE) && (xd < dest.getWidth()); xd++) {
dest.setPixel(xd, yd, pixel);
}
}
}
}
// Save the raster back to the Image
img.setData(dest);
// Write the new file
ImageIO.write(img, "jpg", new File("image-pixelated.jpg"));
Edit: I thought I should mention -- the double[] pixel
is, as far as I can tell, just the RGB color values. For example, when I dumped a single pixel, it looked like {204.0, 197.0, 189.0}
, a light tan color.
回答2:
To complete the answer of @bchociej
I use in the pixel the dominant color of the zone defined by PIX_SIZE
. It's not a perfect solution but it's a little better.
Here an example:
Original:
Old Algorithm:
New Algorithm:
Code sample
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.*;
import java.util.List;
public final class ImageUtil {
public static BufferedImage pixelate(BufferedImage imageToPixelate, int pixelSize) {
BufferedImage pixelateImage = new BufferedImage(
imageToPixelate.getWidth(),
imageToPixelate.getHeight(),
imageToPixelate.getType());
for (int y = 0; y < imageToPixelate.getHeight(); y += pixelSize) {
for (int x = 0; x < imageToPixelate.getWidth(); x += pixelSize) {
BufferedImage croppedImage = getCroppedImage(imageToPixelate, x, y, pixelSize, pixelSize);
Color dominantColor = getDominantColor(croppedImage);
for (int yd = y; (yd < y + pixelSize) && (yd < pixelateImage.getHeight()); yd++) {
for (int xd = x; (xd < x + pixelSize) && (xd < pixelateImage.getWidth()); xd++) {
pixelateImage.setRGB(xd, yd, dominantColor.getRGB());
}
}
}
}
return pixelateImage;
}
public static BufferedImage getCroppedImage(BufferedImage image, int startx, int starty, int width, int height) {
if (startx < 0) startx = 0;
if (starty < 0) starty = 0;
if (startx > image.getWidth()) startx = image.getWidth();
if (starty > image.getHeight()) starty = image.getHeight();
if (startx + width > image.getWidth()) width = image.getWidth() - startx;
if (starty + height > image.getHeight()) height = image.getHeight() - starty;
return image.getSubimage(startx, starty, width, height);
}
public static Color getDominantColor(BufferedImage image) {
Map<Integer, Integer> colorCounter = new HashMap<>(100);
for (int x = 0; x < image.getWidth(); x++) {
for (int y = 0; y < image.getHeight(); y++) {
int currentRGB = image.getRGB(x, y);
int count = colorCounter.getOrDefault(currentRGB, 0);
colorCounter.put(currentRGB, count + 1);
}
}
return getDominantColor(colorCounter);
}
private static Color getDominantColor(Map<Integer, Integer> colorCounter) {
int dominantRGB = colorCounter.entrySet().stream()
.max((entry1, entry2) -> entry1.getValue() > entry2.getValue() ? 1 : -1)
.get()
.getKey();
return new Color(dominantRGB);
}
}
How to use it
img = ImageIO.read(new File("image.jpg"));
BufferedImage imagePixelated = ImageUtil.pixelate(img, PIX_SIZE);
ImageIO.write(imagePixelated, "jpg", new File("image-pixelated.jpg"));
回答3:
I don't have the code off hand, but if you can resize the image to 1/4 the original size then resample that back to the original size it should do the trick. Most image libraries can do that.
回答4:
Search "Close Pixelate" Project, probably this is what you need.
回答5:
Improving @thibaut-mottet answer, as I've got a compiler error on 'getDominantColor' method ('entry1' and 'entry2' not defined).
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
public class ImageUtil {
public static BufferedImage pixelate(BufferedImage imageToPixelate, int pixelSize) {
BufferedImage pixelateImage = new BufferedImage(
imageToPixelate.getWidth(),
imageToPixelate.getHeight(),
imageToPixelate.getType());
for (int y = 0; y < imageToPixelate.getHeight(); y += pixelSize) {
for (int x = 0; x < imageToPixelate.getWidth(); x += pixelSize) {
BufferedImage croppedImage = getCroppedImage(imageToPixelate, x, y, pixelSize, pixelSize);
Color dominantColor = getDominantColor(croppedImage);
for (int yd = y; (yd < y + pixelSize) && (yd < pixelateImage.getHeight()); yd++) {
for (int xd = x; (xd < x + pixelSize) && (xd < pixelateImage.getWidth()); xd++) {
pixelateImage.setRGB(xd, yd, dominantColor.getRGB());
}
}
}
}
return pixelateImage;
}
public static BufferedImage getCroppedImage(BufferedImage image, int startx, int starty, int width, int height) {
if (startx < 0) startx = 0;
if (starty < 0) starty = 0;
if (startx > image.getWidth()) startx = image.getWidth();
if (starty > image.getHeight()) starty = image.getHeight();
if (startx + width > image.getWidth()) width = image.getWidth() - startx;
if (starty + height > image.getHeight()) height = image.getHeight() - starty;
return image.getSubimage(startx, starty, width, height);
}
public static Color getDominantColor(BufferedImage image) {
Map<Integer, Integer> colorCounter = new HashMap<>(100);
for (int x = 0; x < image.getWidth(); x++) {
for (int y = 0; y < image.getHeight(); y++) {
int currentRGB = image.getRGB(x, y);
int count = colorCounter.getOrDefault(currentRGB, 0);
colorCounter.put(currentRGB, count + 1);
}
}
return getDominantColor(colorCounter);
}
@SuppressWarnings("unchecked")
private static Color getDominantColor(Map<Integer, Integer> colorCounter) {
int dominantRGB = colorCounter.entrySet().stream()
.max(new EntryComparator())
.get()
.getKey();
return new Color(dominantRGB);
}
}
@SuppressWarnings("rawtypes")
class EntryComparator implements Comparator {
@SuppressWarnings("unchecked")
@Override
public int compare(Object o1, Object o2) {
Entry<Integer, Integer> entry1 = (Map.Entry<Integer, Integer>) o1;
Entry<Integer, Integer> entry2 = (Map.Entry<Integer, Integer>) o2;
return (entry1.getValue() > entry2.getValue() ? 1 : -1);
}
}
Use it exactly the same way:
img = ImageIO.read(new File("image.jpg"));
BufferedImage imagePixelated = ImageUtil.pixelate(img, PIX_SIZE);
ImageIO.write(imagePixelated, "jpg", new File("image-pixelated.jpg"));
来源:https://stackoverflow.com/questions/15777821/how-can-i-pixelate-a-jpg-with-java