Image/Graphic into a Shape

18,143

Solution 1

motorcycle.jpg

Original Image

motorcycle-03.png

Processed Image

ImageOutline.java

This code requires some patience (when running).

import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.geom.Area;
import javax.imageio.ImageIO;
import java.io.File;
import java.util.Date;
import javax.swing.*;

/* Motorcycle image courtesy of ShutterStock
http://www.shutterstock.com/pic-13585165/stock-vector-travel-motorcycle-silhouette.html */
class ImageOutline {

    public static Area getOutline(BufferedImage image, Color color, boolean include, int tolerance) {
        Area area = new Area();
        for (int x=0; x<image.getWidth(); x++) {
            for (int y=0; y<image.getHeight(); y++) {
                Color pixel = new Color(image.getRGB(x,y));
                if (include) {
                    if (isIncluded(color, pixel, tolerance)) {
                        Rectangle r = new Rectangle(x,y,1,1);
                        area.add(new Area(r));
                    }
                } else {
                    if (!isIncluded(color, pixel, tolerance)) {
                        Rectangle r = new Rectangle(x,y,1,1);
                        area.add(new Area(r));
                    }
                }
            }
        }
        return area;
    }

    public static boolean isIncluded(Color target, Color pixel, int tolerance) {
        int rT = target.getRed();
        int gT = target.getGreen();
        int bT = target.getBlue();
        int rP = pixel.getRed();
        int gP = pixel.getGreen();
        int bP = pixel.getBlue();
        return(
            (rP-tolerance<=rT) && (rT<=rP+tolerance) &&
            (gP-tolerance<=gT) && (gT<=gP+tolerance) &&
            (bP-tolerance<=bT) && (bT<=bP+tolerance) );
    }

    public static BufferedImage drawOutline(int w, int h, Area area) {
        final BufferedImage result = new BufferedImage(
            w,
            h,
            BufferedImage.TYPE_INT_RGB);
        Graphics2D g = result.createGraphics();

        g.setColor(Color.white);
        g.fillRect(0,0,w,h);

        g.setClip(area);
        g.setColor(Color.red);
        g.fillRect(0,0,w,h);

        g.setClip(null);
        g.setStroke(new BasicStroke(1));
        g.setColor(Color.blue);
        g.draw(area);

        return result;
    }

    public static BufferedImage createAndWrite(
        BufferedImage image,
        Color color,
        boolean include,
        int tolerance,
        String name)
        throws Exception {
        int w = image.getWidth();
        int h = image.getHeight();

        System.out.println("Get Area: " + new Date() + " - " + name);
        Area area = getOutline(image, color, include, tolerance);
        System.out.println("Got Area: " + new Date() + " - " + name);

        final BufferedImage result = drawOutline(w,h,area);
        displayAndWriteImage(result, name);

        return result;
    }

    public static void displayAndWriteImage(BufferedImage image, String fileName) throws Exception {
        ImageIO.write(image, "png", new File(fileName));
        JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(image)));
    }

    public static void main(String[] args) throws Exception {
        final BufferedImage outline = ImageIO.read(new File("motorcycle.jpg"));
        BufferedImage crop = outline.getSubimage(17,35,420,270);
        displayAndWriteImage(crop, "motorcycle-01.png");

        BufferedImage crude = createAndWrite(crop, Color.white, false, 60, "motorcycle-02.png");

        BufferedImage combo = createAndWrite(crude, Color.red, true, 0, "motorcycle-03.png");
    }
}

Solution 2

function getArea_FastHack is build upon Andrew Thompsons work, which was very helpful. Mine should be faster, however: (//Edit: and sloppier, too)

import java.awt.*;
import java.awt.geom.Area;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.io.File;
import java.io.IOException;
/**
 * CustomShape
 * based on a Class from Andrew Thompson * 
 * Source: http://stackoverflow.com/questions/7052422/image-graphic-into-a-shape-in-java/7059497#7059497
 * @author Samuel Schneider, Andrew Thompson
 * 
 *
 */
class CustomShape {

    private BufferedImage image=null;

    /**
     * Creates an Area with PixelPerfect precision
     * @param color The color that is draws the Custom Shape
     * @param tolerance The color tolerance
     * @return Area
     */
    public Area getArea(Color color, int tolerance) {
        if(image==null) return null;
        Area area = new Area();
        for (int x=0; x<image.getWidth(); x++) {
            for (int y=0; y<image.getHeight(); y++) {
                Color pixel = new Color(image.getRGB(x,y));
                if (isIncluded(color, pixel, tolerance)) {
                    Rectangle r = new Rectangle(x,y,1,1);
                    area.add(new Area(r));
                }
            }
        }

        return area;
    }

    public Area getArea_FastHack() {
        //Assumes Black as Shape Color
        if(image==null) return null;

        Area area = new Area();
        Rectangle r;
        int y1,y2;

        for (int x=0; x<image.getWidth(); x++) {
            y1=99;
            y2=-1;
            for (int y=0; y<image.getHeight(); y++) {
                Color pixel = new Color(image.getRGB(x,y));
                //-16777216 entspricht RGB(0,0,0)
                if (pixel.getRGB()==-16777216) {
                    if(y1==99) {y1=y;y2=y;}
                    if(y>(y2+1)) {
                        r = new Rectangle(x,y1,1,y2-y1);
                        area.add(new Area(r)); 
                        y1=y;y2=y;
                    }
                    y2=y;
                }               
            }
            if((y2-y1)>=0) {
                r = new Rectangle(x,y1,1,y2-y1);
                area.add(new Area(r)); 
            }
        }

        return area;
    }

    public static boolean isIncluded(Color target, Color pixel, int tolerance) {
        int rT = target.getRed();
        int gT = target.getGreen();
        int bT = target.getBlue();
        int rP = pixel.getRed();
        int gP = pixel.getGreen();
        int bP = pixel.getBlue();
        return(
            (rP-tolerance<=rT) && (rT<=rP+tolerance) &&
            (gP-tolerance<=gT) && (gT<=gP+tolerance) &&
            (bP-tolerance<=bT) && (bT<=bP+tolerance) );
    }

    public CustomShape(String path) {
        try {
            BufferedImage image = ImageIO.read(new File(path));
            this.image = image;
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
Share:
18,143
Pav
Author by

Pav

Updated on June 05, 2022

Comments

  • Pav
    Pav almost 2 years

    I was wondering whether there is any way to convert an image/graphic into a Shape? For example, can I convert the outline of a motorcycle shape into a Shape so I can then use it in Java? I know you can do it with normal squares or with rounded corners, polygons, etc. But is there a way to do a custom shape?

    • Andrew Thompson
      Andrew Thompson over 12 years
      Can you upload an example image and tell us where it can be seen? Preferably not too big in bytes or pixel size. Like the example shown in Cut out image in shape of text.
    • Jonah
      Jonah over 12 years
      Do you mean converting from a raster bitmap to a vector graphic?
    • Alex
      Alex over 12 years
      If you search Google for 'raster vector conversion algorithms' it will give you some indication of how to do it. It's not easy.
    • toto2
      toto2 over 12 years
      You could create your own shape with java.awt.geom.Path2D by specifying a geometric path, but your motorcycle will probably end up looking like a cat or a kettle.
    • toto2
      toto2 over 12 years
      Actually I found a Shape editor. I think it's equivalent to what I described above, but at least you can see what you are doing. You could probably also hack the program such that the background is some image which you could trace over.
  • Adamski
    Adamski over 12 years
    +1: Cool solution. You mentioned it requires patience when running. Do you know the main cause for the slow runtime? If it's due to the number of Areas created could it be optimised by created fewer rectangles by taking a "scan line" approach (whereby each rectangle is a horizontal line of included pixels)?
  • Andrew Thompson
    Andrew Thompson over 12 years
    @Adamski There is a much improved (faster) version available on Smoothing a jagged path. Try it out & see how you go.