I have problems with extracting a pictogram into some further processable format, since now I have got like this:
Part of the current solution is taken from the BoofCV ImageTresholding example. My code for this solution the following:
import georegression.metric.UtilAngle;
import java.awt.Color;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.io.IOException;
import boofcv.alg.color.ColorHsv;
import boofcv.alg.filter.binary.BinaryImageOps;
import boofcv.alg.filter.binary.GThresholdImageOps;
import boofcv.alg.filter.binary.ThresholdImageOps;
import boofcv.gui.ListDisplayPanel;
import boofcv.gui.binary.VisualizeBinaryData;
import boofcv.gui.image.ImagePanel;
import boofcv.gui.image.ShowImages;
import boofcv.io.image.ConvertBufferedImage;
import boofcv.io.image.UtilImageIO;
import boofcv.struct.image.ImageFloat32;
import boofcv.struct.image.ImageUInt8;
import boofcv.struct.image.MultiSpectral;
public class Binaryzation {
static double splitFraction = 0.05;
static double minimumSideFraction = 0.1;
static ListDisplayPanel gui = new ListDisplayPanel();
public static void printClickedColor(final BufferedImage image) {
ImagePanel gui = new ImagePanel(image);
gui.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
float[] color = new float[3];
int rgb = image.getRGB(e.getX(), e.getY());
ColorHsv.rgbToHsv((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF,
rgb & 0xFF, color);
System.out.println("H = " + color[0] + " S = " + color[1]
+ " V = " + color[2]);
try {
showSelectedColor("Selected", image, color[0], color[1]);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});
ShowImages.showWindow(gui, "Color Selector");
}
public static void showSelectedColor(String name, BufferedImage image,
float hue, float saturation) throws IOException {
ImageUInt8 binary = binaryTreshold(name, image, hue, saturation);
// MAGIC HAPPENDS -removing small objects
ImageUInt8 filtered = BinaryImageOps.erode4(binary, 1, null);
filtered = BinaryImageOps.dilate8(filtered, 1, null);
filtered = BinaryImageOps.removePointNoise(filtered, filtered);
ShowImages.showWindow(filtered, "Binary " + name);
BufferedImage visualFiltered1 = VisualizeBinaryData.renderBinary(
filtered, true, null);
ShowImages.showWindow(visualFiltered1, "Mask");
BufferedImage visualFiltered12 = Fill.fill(visualFiltered1);
ShowImages.showWindow(visualFiltered12, "Filled Mask");
ImageUInt8 mask = ConvertBufferedImage.convertFromSingle(
visualFiltered12, null, ImageUInt8.class);
ImageUInt8 wynik = new ImageUInt8(mask.width, mask.height);
//subtraction of images: wynik=mask-filtered;
int min = 0;
int max = 1;
for (int i = 0; i < mask.height; i++) {
// System.out.println("i=" + i);
for (int j = 0; j < mask.width; j++) {
// System.out.println("j=" + j);
if (filtered.get(j, i) < min)
min = filtered.get(j, i);
if (filtered.get(j, i) > max)
max = filtered.get(j, i);
int filtInt = filtered.get(j, i);
if (filtInt >= 1)
filtInt = 1;
else if (filtInt < 1)
filtInt = 0;
int maskInt = mask.get(j, i);
if (maskInt >= 1)
maskInt = 0;
else if (maskInt < 1)
maskInt = 1;
int diff = maskInt - filtInt;
if (diff == 1) {
diff = 255;
wynik.set(j, i, diff);
} else if (diff == 0) {
diff = 0;
wynik.set(j, i, diff);
} else {
diff = 255;
wynik.set(j, i, diff);
}
}
}
ShowImages.showWindow(wynik, "Wynik=Mask-Filtered");
wynik = BinaryImageOps.erode4(wynik, 1, null);
wynik = BinaryImageOps.dilate8(wynik, 1, null);
wynik = BinaryImageOps.removePointNoise(wynik, wynik);
UtilImageIO.saveImage(wynik, "C:/dev/zdjeciaTestowe/wynik.jpg");
ShowImages.showWindow(wynik, "Wynik=Mask-Filtered After noise remove");
}
private static ImageUInt8 binaryTreshold(String name, BufferedImage image,
float hue, float saturation) throws IOException {
MultiSpectral<ImageFloat32> input = ConvertBufferedImage
.convertFromMulti(image, null, true, ImageFloat32.class);
MultiSpectral<ImageFloat32> hsv = input.createSameShape();
// Convert into HSV
ColorHsv.rgbToHsv_F32(input, hsv);
// Euclidean distance squared threshold for deciding which pixels are
// members of the selected set
float maxDist2 = 0.4f * 0.4f;
// Extract hue and saturation bands which are independent of intensity
ImageFloat32 H = hsv.getBand(0);
ImageFloat32 S = hsv.getBand(1);
// Adjust the relative importance of Hue and Saturation.
// Hue has a range of 0 to 2*PI and Saturation from 0 to 1.
float adjustUnits = (float) (Math.PI / 2.0);
// step through each pixel and mark how close it is to the selected
// color
BufferedImage output = new BufferedImage(input.width, input.height,
BufferedImage.TYPE_INT_RGB);
for (int y = 0; y < hsv.height; y++) {
for (int x = 0; x < hsv.width; x++) {
// Hue is an angle in radians, so simple subtraction doesn't
// work
float dh = UtilAngle.dist(H.unsafe_get(x, y), hue);
float ds = (S.unsafe_get(x, y) - saturation) * adjustUnits;
// this distance measure is a bit naive, but good enough for to
// demonstrate the concept
float dist2 = dh * dh + ds * ds;
if (dist2 <= maxDist2) {
System.out.println(image.getRGB(x, y));
output.setRGB(x, y, image.getRGB(x, y));
}
}
}
ImageFloat32 output1 = ConvertBufferedImage.convertFromSingle(output,
null, ImageFloat32.class);
ImageUInt8 binary = new ImageUInt8(input.width, input.height);
double threshold = GThresholdImageOps.computeOtsu(output1, 0, 255);
// Apply the threshold to create a binary image
ThresholdImageOps.threshold(output1, binary, (float) threshold, true);
return binary;
}
public static void main(String args[]) throws IOException {
BufferedImage image = UtilImageIO
.loadImage("C:/dev/zdjeciaTestowe/images.jpg");
// Let the user select a color
printClickedColor(image);
// Display pre-selected colors
showSelectedColor("Yellow", image, 1f, 1f);
}
}
import java.awt.image.BufferedImage;
import boofcv.struct.image.ImageUInt8;
public class Fill {
private static final int BLACK = -16777216;
private static final int WHITE = -1;
/**
* #param input Buffered image
* #return image with filled holes
*/
public static BufferedImage fill(BufferedImage input) {
int width = input.getWidth();
int height = input.getHeight();
BufferedImage output=new BufferedImage(width, height, input.getType());
for (int i = 0; i < height; i++) {
// System.out.println("i=" + i);
for (int j = 0; j < width; j++) {
// System.out.println("j=" + j);
if (input.getRGB(j, i) == WHITE) {
output.setRGB(j, i, WHITE);
} else if (isPreviusWhite(j, i, input)
&& isAnotherWhiteInLine(j, i, input)) {
output.setRGB(j, i, WHITE);
}
}
}
return output;
}
private static boolean isPreviusWhite(int i, int i2, BufferedImage input) {
boolean condition = false;
while (1 < i2) {
if (input.getRGB(i, i2) == WHITE)
return true;
i2--;
}
return condition;
}
private static boolean isAnotherWhiteInLine(int j, int i,
BufferedImage input) {
boolean condition = false;
while (j < input.getWidth()) {
if (input.getRGB(j, i) == WHITE)
return true;
j++;
}
return condition;
}
}
I know how to extract a pictogram on a sign, and i have done it by subtracting the Mask from Filled Mask but have problem to obtain some processable result,
because int the end I have an image in grayscale not a binary image (or as it is in boofCV ImageUInt8).
How do I properly do subtraction of two images in ImageUInt8 format so the result would be also ImageUInt8?
Today i have wrote futher part of that algorithm and now the problem which i want to ask about is more clarified. Here is added code (part from //subtraction of images: wynik=mask-filtered;) and 2 additional pictures as product of processing.
The problem is that last image after noise remove is solid black and without any information. How to correctly convert image to obtain processable content??
What i'm doing wrong?
I have found solution to my problem on the last picture "Wynik=Mask-Filtered After noise Remove" there is a pictogram but diffirence beetwen piksels in grayscale is so low that it's hard to see so problemsolver is adding:
GrayImageOps.stretch(wynik, 125, 125, 255, wynik);
Related
Im calculting skintone of an image in java.
convert the pixel of Image in yCbCR.
check if image pixel is in specific range, then its a skin color.
calculate percentage by dividing it by total pixel.
Its working fine in CPU code, but when i convert it to GPU code, The pixel percentage is not coming right.
The confusing part for me was send the pixel data to GPU and get its r, g, b value in GPU.
So i follow JCuda Pixel Invert Example example to send pixel data. The difference is the example send pixel data in int[] array and I'm sending it in byte[] array.
Here the code.
import static jcuda.driver.JCudaDriver.cuCtxCreate;
import static jcuda.driver.JCudaDriver.cuCtxSynchronize;
import static jcuda.driver.JCudaDriver.cuDeviceGet;
import static jcuda.driver.JCudaDriver.cuInit;
import static jcuda.driver.JCudaDriver.cuLaunchKernel;
import static jcuda.driver.JCudaDriver.cuMemAlloc;
import static jcuda.driver.JCudaDriver.cuMemFree;
import static jcuda.driver.JCudaDriver.cuMemcpyDtoH;
import static jcuda.driver.JCudaDriver.cuMemcpyHtoD;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.Raster;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import ij.IJ;
import jcuda.Pointer;
import jcuda.Sizeof;
import jcuda.driver.CUcontext;
import jcuda.driver.CUdevice;
import jcuda.driver.CUdeviceptr;
import jcuda.driver.CUfunction;
import jcuda.driver.JCudaDriver;
import jcuda.nvrtc.JNvrtc;
public class SkinTone {
public static void CalculateSKintoneGPU(File file) throws IOException {
BufferedImage bufferedImage = ImageIO.read(file);
if (bufferedImage == null || bufferedImage.getData() == null)
return;
Raster raster = bufferedImage.getData();
DataBuffer dataBuffer = raster.getDataBuffer();
DataBufferByte dataBufferInt = (DataBufferByte)dataBuffer;
byte[] pixels = dataBufferInt.getData();
int totalPixels = raster.getHeight() * raster.getWidth();
CUfunction kernelFunction = initlize();
int output[] = execute(kernelFunction, pixels, raster.getWidth(), raster.getHeight());
// Flushing memory
raster = null;
bufferedImage.flush();
bufferedImage = null;
long skintoneThreshold = Math.round(output[0] / (double) totalPixels * 100.0);
System.err.println("Skintone Using GPU: " + output[0]);
System.err.println("Total Pixel Of GPU: " + totalPixels);
System.err.println("SKinTone Percentage Using GPU: " + skintoneThreshold + "%");
}
static int[] execute(CUfunction kernelFunction, byte[] pixels, int w, int h) {
// Allocate memory on the device, and copy the host data to the device
int size = w * h * Sizeof.BYTE;
CUdeviceptr pointer = new CUdeviceptr();
cuMemAlloc(pointer, size);
cuMemcpyHtoD(pointer, Pointer.to(pixels), size);
int numElements = 9;
int s = 0;
// Allocate device output memory
CUdeviceptr deviceOutput = new CUdeviceptr();
cuMemAlloc(deviceOutput, numElements * Sizeof.INT);
// Set up the kernel parameters: A pointer to an array
// of pointers which point to the actual values.
Pointer kernelParameters = Pointer.to(Pointer.to(pointer), Pointer.to(new int[] { w }),
Pointer.to(new int[] { h }), Pointer.to(deviceOutput));
// Call the kernel function
int blockSize = 16;
int gridSize = (Math.max(w, h) + blockSize - 1) / blockSize;
cuLaunchKernel(kernelFunction, gridSize, gridSize, 1, // Grid dimension
blockSize, blockSize, 1, // Block dimension
0, null, // Shared memory size and stream
kernelParameters, null // Kernel- and extra parameters
);
cuCtxSynchronize();
// Allocate host output memory and copy the device output
// to the host.
int hostOutput[] = new int[numElements];
cuMemcpyDtoH(Pointer.to(hostOutput), deviceOutput, numElements * Sizeof.INT);
// Clean up.
cuMemFree(deviceOutput);
cuMemFree(pointer);
return hostOutput;
}
public static CUfunction initlize() {
// Enable exceptions and omit all subsequent error checks
JCudaDriver.setExceptionsEnabled(true);
JNvrtc.setExceptionsEnabled(true);
// Initialize the driver and create a context for the first device.
cuInit(0);
CUdevice device = new CUdevice();
cuDeviceGet(device, 0);
CUcontext context = new CUcontext();
cuCtxCreate(context, 0, device);
// Obtain the CUDA source code from the CUDA file
String cuFileName = "Skintone.cu";
String sourceCode = CudaUtils.readResourceAsString(cuFileName);
if (sourceCode == null) {
IJ.showMessage("Error", "Could not read the kernel source code");
}
// Create the kernel function
return CudaUtils.createFunction(sourceCode, "skintone");
}
public static void CalculateSKintoneCPU(File file) throws IOException {
BufferedImage bufferedImage = ImageIO.read(file);
if (bufferedImage == null || bufferedImage.getData() == null)
return;
Raster raster = bufferedImage.getData();
float[] rgb = new float[4];
int totalPixels = raster.getHeight() * raster.getWidth();
int skinTonePixels = 0;
for (int x = 0; x < raster.getWidth(); x++) {
for (int y = 0; y < raster.getHeight(); y++) {
raster.getPixel(x, y, rgb);
if (skintone(rgb)) {
skinTonePixels++;
}
}
}
// Flushing memory
raster = null;
rgb = null;
bufferedImage.flush();
bufferedImage = null;
long skintoneThreshold = Math.round(skinTonePixels / (double) totalPixels * 100.0);
System.err.println("Skintone Using CPU: " + skinTonePixels);
System.err.println("Total Pixel Of CPU: " + totalPixels);
System.err.println("SKinTone Percentage Using CPU: " + skintoneThreshold + "%");
}
private static boolean skintone(float[] rgb) {
float yCbCr[] = (float[]) convertRGBtoYUV(rgb);
if ((yCbCr[1] >= 80 && yCbCr[1] <= 120) && (yCbCr[2] >= 133 && yCbCr[2] <= 173)) {
return true;
}
return false;
}
private static float[] convertRGBtoYUV(float[] rgb) {
final float[] yCbCr = new float[3];
float r = rgb[0];
float g = rgb[1];
float b = rgb[2];
yCbCr[0] = 16 + (0.299f * r) + (0.587f * g) + (0.144f * b);
yCbCr[1] = 128 + (-0.169f * r) - (0.331f * g) + (0.5f * b);
yCbCr[2] = 128 + (0.5f * r) - (0.419f * g) - (0.081f * b);
return yCbCr;
}
public static void main(String[] args) throws IOException {
File file = new File("C:\\Users\\Aqeel\\git\\jcuda-imagej-example\\src\\test\\resources\\lena512color.png");
CalculateSKintoneCPU(file);
CalculateSKintoneGPU(file);
}
}
Kernal File
extern "C"
__global__ void skintone(uchar4* data, int w, int h, int* output)
{
int x = threadIdx.x+blockIdx.x*blockDim.x;
int y = threadIdx.y+blockIdx.y*blockDim.y;
if (x < w && y < h)
{
float r, g, b;
float cb, cr;
int index = y*w+x;
uchar4 pixel = data[index];
r = pixel.x;
g = pixel.y;
b = pixel.z;
cb = 128 + (-0.169f * r) - (0.331f * g) + (0.5f * b);
cr = 128 + (0.5f * r) - (0.419f * g) - (0.081f * b);
if((cb >= 80 && cb <= 120) && (cr >= 133 && cr <= 173)) {
atomicAdd(&output[0], 1);
}
}
}
Complete Example src, Machine Need Nvida Card, Cuda Toolkit V9 and Graphics Drivers
I solve the problem by hit and trial method. In the kernel i change the position of r with b, and the problem resolved, also instead of byte i have to send the code in int array in java.
extern "C"
__global__ void skintone(uchar4* data, int w, int h, int* output)
{
int x = threadIdx.x+blockIdx.x*blockDim.x;
int y = threadIdx.y+blockIdx.y*blockDim.y;
if (x < w && y < h)
{
float b, g, r;
float cb, cr;
int index = y*w+x;
uchar4 pixel = data[index];
b = (float)pixel.x;
g = (float)pixel.y;
r = (float)pixel.z;
cb = 128 + (-0.169f * r) - (0.331f * g) + (0.5f * b);
cr = 128 + (0.5f * r) - (0.419f * g) - (0.081f * b);
if((cb >= 80 && cb <= 120) && (cr >= 133 && cr <= 173)) {
atomicAdd(&output[0], 1);
}
}
}
Java Code Changes.
public static void calculateSkintoneGPU() throws IOException {
BufferedImage img = ImageIO.read(SkinTone.class.getClassLoader().getResource("images.jpg"));
if (img == null || img.getData() == null)
return;
int width = img.getWidth(null);
int height = img.getHeight(null);
int[] pixels = new int[width * height];
PixelGrabber pg = new PixelGrabber(img, 0, 0, width, height, pixels, 0, width);
try {
pg.grabPixels();
} catch (InterruptedException e){};
int totalPixels = width * height;
CUfunction kernelFunction = initlize();
int output[] = execute(kernelFunction, pixels, width, height);
// Flushing memory
img.flush();
img = null;
long skintoneThreshold = Math.round(output[0] / (double) totalPixels * 100.0);
System.err.println("Skintone Using GPU: " + output[0]);
System.err.println("Total Pixel Of GPU: " + totalPixels);
System.err.println("SKinTone Percentage Using GPU: " + skintoneThreshold + "%");
}
Hy Guys,
I am having problems when I want to play a movie using processing because when I grab a frame to apply a filter of a convolution mask I lose the sound. Everything works fine, but I can´t understand while I can hear only second of the sound and then the rest is off.
Here is my code:
import processing.video.*;
//DECLARATION OF OBJECT MOVIE
Movie myMovie;
//DECLARATION OF IMAGE SOURCE AND IMAGE FILTERED
PImage imgOriginal, imgFilterContornos;
// CONVOLUTION MASK TO DETECT EDGES
float[][] matrixContorno = {
{ -1, -1, -1 },
{ -1, 8, -1 },
{ -1, -1, -1 } };
// DIMENSION OF CONVOLUTION MASK
int matrixsizeContorno = 3;
// OFFSET
int offsetContorno = 128;
final int RED=0, GREEN=1, BLUE=2;
void setup() {
//LOAD THE VIDEO
myMovie = new Movie(this, "cartell_cine.mov");
myMovie.noLoop();
size(1280, 720);
myMovie.play();
frameRate(30);
}
void draw() {
}
// Called every time a new frame is available to read
void movieEvent(Movie m) {
m.read();
//GET THE FRAME
imgOriginal=m;
// I CREATE A NEW IMAGE WITH SAME DIMENSIONS AS ORIGINAL
imgFilterContornos = createImage(imgOriginal.width, imgOriginal.height, RGB);
// LOADING PIXELES TO ALLOW ACCESS TO ARRAY pixels[]
imgOriginal.loadPixels();
imgFilterContornos.loadPixels();
for (int x = 0; x < m.width; x++) {
for (int y = 0; y < m.height; y++) {
// CALCULATING CONVOLUTION
int r = convolution(x, y, matrixContorno, matrixsizeContorno, offsetContorno, imgOriginal,RED);
int g = convolution(x, y, matrixContorno, matrixsizeContorno, offsetContorno, imgOriginal,GREEN);
int b = convolution(x, y, matrixContorno, matrixsizeContorno, offsetContorno, imgOriginal,BLUE);
// I GET THE NEW PIXEL
int loc = x + y * imgOriginal.width;
imgFilterContornos.pixels[loc] = color(r,g,b);
}
}
imgFilterContornos.updatePixels();
set(0,0,imgFilterContornos);
}
// METHOD TO CALCULATE DE SPACIAL CONVOLUTION
int convolution(int x, int y, float[][] matrix, int matrixsize, int offset, PImage img, int canal) {
float result = 0.0;
int half = matrixsize / 2;
// Recorremos la matriz de convolución
for (int i = 0; i < matrixsize; i++) {
for (int j = 0; j < matrixsize; j++) {
// Cálculo del píxel sobre el que estamos trabajando
int xloc = x + i - half;
int yloc = y + j - half;
int loc = xloc + img.width * yloc;
// We make sure that we take a pixel within the valid range. In this case we are
// applying replication of nearby pixel values to pixel locations
// coming out of the image
loc = constrain(loc, 0, img.pixels.length-1);
// Calculation of the convolution operation
// Check the value of the corresponding channel
if (canal==RED)
result += ((imgOriginal.pixels[loc] >> 16 & 0xFF) * matrix[i][j]);
else if (canal==GREEN)
result += ((imgOriginal.pixels[loc] >> 8 & 0xFF) * matrix[i][j]);
else if (canal==BLUE)
result += ((imgOriginal.pixels[loc] & 0xFF) * matrix[i][j]);
}
}
// OFFSET
result += offset;
result = constrain(result, 0, 255);
return (int)result;
}
Thank you in advance!
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?
motorcycle.jpg
motorcycle-03.png
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");
}
}
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();
}
}
}
Here's something faster but less accurate, useful for collision checking or 2D physics.
Point[] MakePoly(BufferedImage spr,int d,int angle){
//creates an outline of a transparent image, points are stored in an array
//arg0 - BufferedImage source image
//arg1 - Int detail (lower = better)
//arg2 - Int angle threshold in degrees (will remove points with angle differences below this level; 15 is a good value)
// making this larger will make the body faster but less accurate;
int w= spr.getWidth(null); int h= spr.getHeight(null);
// increase array size from 255 if needed
int[] vertex_x=new int[255], vertex_y=new int[255], vertex_k=new int[255];
int numPoints=0, tx=0,ty=0,fy=-1,lx=0,ly=0; vertex_x[0]=0; vertex_y[0]=0; vertex_k[0]=1;
for (tx=0;tx<w;tx+=d) for (ty=0;ty<h;ty+=1) if((spr.getRGB(tx,ty)>>24) != 0x00 )
{vertex_x[numPoints]=tx; vertex_y[numPoints]=h-ty; vertex_k[numPoints]=1; numPoints++; if (fy<0) fy=ty; lx=tx; ly=ty; break; }
for (ty=0;ty<h;ty+=d) for (tx=w-1;tx>=0;tx-=1) if((spr.getRGB(tx,ty)>>24) != 0x00 && ty > ly)
{vertex_x[numPoints]=tx; vertex_y[numPoints]=h-ty; vertex_k[numPoints]=1; numPoints++; lx=tx; ly=ty; break; }
for (tx=w-1;tx>=0;tx-=d) for (ty=h-1;ty>=0;ty-=1) if((spr.getRGB(tx,ty)>>24) != 0x00 && tx < lx)
{vertex_x[numPoints]=tx; vertex_y[numPoints]=h-ty; vertex_k[numPoints]=1; numPoints ++; lx=tx; ly=ty; break; }
for (ty=h-1;ty>=0;ty-=d) for (tx=0;tx<w;tx+=1) if((spr.getRGB(tx,ty)>>24) != 0x00 && ty < ly && ty > fy)
{vertex_x[numPoints]=tx; vertex_y[numPoints]=h-ty; vertex_k[numPoints]=1; numPoints ++; lx=tx; ly=ty; break; }
double ang1,ang2; for (int i=0;i<numPoints-2;i++) {
ang1 = PointDirection(vertex_x[i],vertex_y[i], vertex_x[i+1],vertex_y[i+1]);
ang2 = PointDirection(vertex_x[i+1],vertex_y[i+1], vertex_x[i+2],vertex_y[i+2]);
if (Math.abs(ang1-ang2) <= angle) vertex_k[i+1] = 0; }
ang1 = PointDirection(vertex_x[numPoints-2],vertex_y[numPoints-2], vertex_x[numPoints-1],vertex_y[numPoints-1]);
ang2 = PointDirection(vertex_x[numPoints-1],vertex_y[numPoints-1], vertex_x[0],vertex_y[0]);
if (Math.abs(ang1-ang2) <= angle) vertex_k[numPoints-1] = 0;
ang1 = PointDirection(vertex_x[numPoints-1],vertex_y[numPoints-1], vertex_x[0],vertex_y[0]);
ang2 = PointDirection(vertex_x[0],vertex_y[0], vertex_x[1],vertex_y[1]);
if (Math.abs(ang1-ang2) <= angle) vertex_k[0] = 0;
int n=0;for (int i=0;i<numPoints;i++)if(vertex_k[i]==1)n++;
Point[] poly= new Point[n]; n=0; for (int i=0;i<numPoints;i++) if (vertex_k[i]==1)
{ poly[n]=new Point(); poly[n].x=vertex_x[i]; poly[n].y=h-vertex_y[i];n++;} return poly;
}
double PointDirection(double xfrom,double yfrom,double xto,double yto){
return Math.atan2(yto-yfrom,xto-xfrom)*180/Math.PI ;
}
I am trying to perform ocr on numbers using tesseract and found that when I used the threshold filter in photoshop to preprocess the images I get some really good results. I am trying to replicate this programatically and have found the following useful tutorial: http://developer.bostjan-cigan.com/java-image-binarization/ When I run the code though I am just getting an entirely black image. Does any one know how I can fix this?
package otsubinarize;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class OtsuBinarize {
private static BufferedImage original, grayscale, binarized;
public static void main(String[] args) throws IOException {
File original_f = new File("/Users/unknown1/Desktop/t1.png");
String output_f = "/Users/unknown1/Desktop/t1";
original = ImageIO.read(original_f);
grayscale = toGray(original);
binarized = binarize(grayscale);
writeImage(output_f);
}
private static void writeImage(String output) throws IOException {
File file = new File(output+".jpg");
ImageIO.write(binarized, "jpg", file);
}
// Return histogram of grayscale image
public static int[] imageHistogram(BufferedImage input) {
int[] histogram = new int[100];
for(int i=0; i<histogram.length; i++) histogram[i] = 0;
for(int i=0; i<input.getWidth(); i++) {
for(int j=0; j<input.getHeight(); j++) {
int red = new Color(input.getRGB (i, j)).getRed();
histogram[red]++;
}
}
return histogram;
}
// The luminance method
private static BufferedImage toGray(BufferedImage original) {
int alpha, red, green, blue;
int newPixel;
BufferedImage lum = new BufferedImage(original.getWidth(), original.getHeight(), original.getType());
for(int i=0; i<original.getWidth(); i++) {
for(int j=0; j<original.getHeight(); j++) {
// Get pixels by R, G, B
alpha = new Color(original.getRGB(i, j)).getAlpha();
red = new Color(original.getRGB(i, j)).getRed();
green = new Color(original.getRGB(i, j)).getGreen();
blue = new Color(original.getRGB(i, j)).getBlue();
red = (int) (0.21 * red + 0.71 * green + 0.07 * blue);
// Return back to original format
newPixel = colorToRGB(alpha, red, red, red);
// Write pixels into image
lum.setRGB(i, j, newPixel);
}
}
return lum;
}
// Get binary treshold using Otsu's method
private static int otsuTreshold(BufferedImage original) {
int[] histogram = imageHistogram(original);
int total = original.getHeight() * original.getWidth();
float sum = 0;
for(int i=0; i<100; i++) sum += i * histogram[i];
float sumB = 0;
int wB = 0;
int wF = 0;
float varMax = 0;
int threshold = 0;
for(int i=0 ; i<100 ; i++) {
wB += histogram[i];
if(wB == 0) continue;
wF = total - wB;
if(wF == 0) break;
sumB += (float) (i * histogram[i]);
float mB = sumB / wB;
float mF = (sum - sumB) / wF;
float varBetween = (float) wB * (float) wF * (mB - mF) * (mB - mF);
if(varBetween > varMax) {
varMax = varBetween;
threshold = i;
}
}
return threshold;
}
private static BufferedImage binarize(BufferedImage original) {
int red;
int newPixel;
int threshold = otsuTreshold(original);
BufferedImage binarized = new BufferedImage(original.getWidth(), original.getHeight(), original.getType());
for(int i=0; i<original.getWidth(); i++) {
for(int j=0; j<original.getHeight(); j++) {
// Get pixels
red = new Color(original.getRGB(i, j)).getRed();
int alpha = new Color(original.getRGB(i, j)).getAlpha();
if(red > threshold) {
newPixel = 100;
}
else {
newPixel = 0;
}
newPixel = colorToRGB(alpha, newPixel, newPixel, newPixel);
binarized.setRGB(i, j, newPixel);
}
}
return binarized;
}
// Convert R, G, B, Alpha to standard 8 bit
private static int colorToRGB(int alpha, int red, int green, int blue) {
int newPixel = 0;
newPixel += alpha;
newPixel = newPixel << 8;
newPixel += red; newPixel = newPixel << 8;
newPixel += green; newPixel = newPixel << 8;
newPixel += blue;
return newPixel;
}
}
They are different issues:
If you sum the weight coefficients in your method toGray, you will see that the sum is not 1. Take a look to the coefficients on the CIE page (better precision and the sum is 1).
In the same method, you create a new gray level image, but you use the same encoding as the original image, which is a color image. The type should be TYPE_BYTE_GRAY.
In your method histogram, you instantiate it for 100 elements. You work with gray level images normally encoded on 256 gray levels, but it can also be 65536.
I don't have the same way to compute the Otsu threshold. Take a look to the ImageJ source code, you will have exactly the good way to do.
Overall remark: you use getRGB to access to the pixel values, and it's a terrible method. Use getRaster().getSample instead (faster and easier), or the fastest way is to access the DataBuffer.
Hi guys i'm trying to create a draughts game in Java and am using JPanels to represent the squares, if I were to change the size of the panels how would I do so ? if I use a layout manager the squares are not big enough. At the moment i'm not using a layout manager to try and change the size, but the size doesnt seem to change - just stays at 1,1 pixel.
private void createSquares(){
for(int i = 0; i < 65; i++){
squares[i] = new JPanel();
squares[i].setLayout(null);
squares[i].setSize(20,20);
board.add(squares[i]);
}
}
You could always "borrow" an image or two online, and put that into your program. For example this code:
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.*;
import javax.imageio.ImageIO;
import javax.swing.*;
public class GetChessSquareImages {
public static final String PATH_TO_SQUARES = "http://www.colourbox.com/preview/" +
"4578561-622234-seamless-oak-square-chess-like-parquet-texture.jpg";
private static final int IMG_SIDE_COUNT = 4;
private static final double SCALE = 0.8;
private Map<SquareColor, List<Icon>> squareColorMap = new HashMap<SquareColor, List<Icon>>();
private Random random = new Random();
public void downloadImages() throws IOException {
URL lrgImgUrl = new URL(PATH_TO_SQUARES);
BufferedImage largeImg = ImageIO.read(lrgImgUrl);
int w = largeImg.getWidth() / IMG_SIDE_COUNT;
int h = largeImg.getHeight() / IMG_SIDE_COUNT;
for (int i = 0; i < IMG_SIDE_COUNT; i++) {
int x = (i * largeImg.getWidth()) / IMG_SIDE_COUNT;
for (int j = 0; j < IMG_SIDE_COUNT; j++) {
if (j != 1 && j != 2) {
int y = (j * largeImg.getHeight()) / IMG_SIDE_COUNT;
extractSubImg(largeImg, i, j, x, y, w, h);
}
}
}
}
private void extractSubImg(BufferedImage largeImg,
int i, int j, int x, int y, int w, int h) {
Image subImg = largeImg.getSubimage(x, y, w, h);
int width = (int) (w * SCALE);
int height = (int) (h * SCALE);
subImg = subImg.getScaledInstance(width, height, Image.SCALE_SMOOTH);
List<Icon> iconList = null;
if (i % 2 == j % 2) {
iconList = squareColorMap.get(SquareColor.LIGHT);
if (iconList == null) {
iconList = new ArrayList<Icon>();
squareColorMap.put(SquareColor.LIGHT, iconList);
}
} else {
iconList = squareColorMap.get(SquareColor.DARK);
if (iconList == null) {
iconList = new ArrayList<Icon>();
squareColorMap.put(SquareColor.DARK, iconList);
}
}
iconList.add(new ImageIcon(subImg));
}
public Icon getRandomIcon(SquareColor sqrColor) {
List<Icon> iconList = squareColorMap.get(sqrColor);
if (iconList == null) {
return null;
} else {
return iconList.get(random.nextInt(iconList.size()));
}
}
public static void main(String[] args) {
GetChessSquareImages getImages = new GetChessSquareImages();
try {
getImages.downloadImages();
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
int side = 8;;
JPanel panel = new JPanel(new GridLayout(side , side));
for (int i = 0; i < side; i++) {
for (int j = 0; j < side; j++) {
SquareColor sqrColor = (i % 2 == j % 2) ? SquareColor.LIGHT : SquareColor.DARK;
Icon icon = getImages.getRandomIcon(sqrColor);
panel.add(new JLabel(icon));
}
}
JOptionPane.showMessageDialog(null, panel);
}
}
enum SquareColor {
DARK, LIGHT
}
returns this JPanel:
Then your square size will be based on the sizes of your ImageIcons. For example, I have scaled my squares back with a scale factor of 0.8 (the SCALE constant above) to make the grid a more reasonable size.