How to convert an image into a transparent image in java - java

How to convert a white background of an image into a transparent background? Can anyone tel me how to do this?

The first result from Google is this:
Make a color transparent
http://www.rgagnon.com/javadetails/java-0265.html
It makes the Blue part of an image transparent, but I'm sure you can adapt that to use White intstead
(hint: Pass Color.WHITE to the makeColorTransparent function, instead of Color.BLUE)
Found a more complete and modern answer here: How to make a color transparent in a BufferedImage and save as PNG

This method will make background transparent. You need to pass the image you want to modify, colour, and tolerance.
final int color = ret.getRGB(0, 0);
final Image imageWithTransparency = makeColorTransparent(ret, new Color(color), 10);
final BufferedImage transparentImage = imageToBufferedImage(imageWithTransparency);
private static BufferedImage imageToBufferedImage(final Image image) {
final BufferedImage bufferedImage =
new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
final Graphics2D g2 = bufferedImage.createGraphics();
g2.drawImage(image, 0, 0, null);
g2.dispose();
return bufferedImage;
}
private static Image makeColorTransparent(final BufferedImage im, final Color color, int tolerance) {
int temp = 0;
if (tolerance < 0 || tolerance > 100) {
System.err.println("The tolerance is a percentage, so the value has to be between 0 and 100.");
temp = 0;
} else {
temp = tolerance * (0xFF000000 | 0xFF000000) / 100;
}
final int toleranceRGB = Math.abs(temp);
final ImageFilter filter = new RGBImageFilter() {
// The color we are looking for (white)... Alpha bits are set to opaque
public int markerRGBFrom = (color.getRGB() | 0xFF000000) - toleranceRGB;
public int markerRGBTo = (color.getRGB() | 0xFF000000) + toleranceRGB;
public final int filterRGB(final int x, final int y, final int rgb) {
if ((rgb | 0xFF000000) >= markerRGBFrom && (rgb | 0xFF000000) <= markerRGBTo) {
// Mark the alpha bits as zero - transparent
return 0x00FFFFFF & rgb;
} else {
// Nothing to do
return rgb;
}
}
};
final ImageProducer ip = new FilteredImageSource(im.getSource(), filter);
return Toolkit.getDefaultToolkit().createImage(ip);
}

Here is my solution. This filter will remove the background from any image as long as the background image color is in the top left corner.
private static class BackgroundFilter extends RGBImageFilter{
boolean setUp = false;
int bgColor;
#Override
public int filterRGB(int x, int y, int rgb) {
int colorWOAlpha = rgb & 0xFFFFFF;
if( ! setUp && x == 0 && y == 0 ){
bgColor = colorWOAlpha;
setUp = true;
}
else if( colorWOAlpha == bgColor )
return colorWOAlpha;
return rgb;
}
}
Elsewhere...
ImageFilter bgFilter = new BackgroundFilter();
ImageProducer ip = new FilteredImageSource(image.getSource(), bgFilter);
image = Toolkit.getDefaultToolkit().createImage(ip);

I am aware that this question is over a decade old and that some answers have already been given. However, none of them is satisfactory if the pixels inside the image are the same color as the background. Let's take a practical example. Given these images:
both have a white background, but the white color is also inside the image to be cutout. In other words, the white pixels on the outside of the two pennants must become transparent, the ones on the inside must remain as they are. Add to this the complication that the white of the background is not perfectly white (due to jpeg compression), so a tolerance is needed. The issue can be made more complex by figures that are not only convex, but also concave.
I created an algorithm in Java that solves the problem very well, I tested it with the two figures shown here. The following code refers to the Java API of Codename One (https://www.codenameone.com/javadoc/), but can be repurposed to the Java SE API or implemented in other languages. The important thing is to understand the rationale.
/**
* Given an image with no transparency, it makes the white background
* transparent, provided that the entire image outline has a different color
* from the background; the internal pixels of the image, even if they have
* the same color as the background, are not changed.
*
* #param source image with a white background; the image must have an
* outline of a different color from background.
* #return a new image with a transparent background
*/
public static Image makeBackgroundTransparent(Image source) {
/*
* Algorithm
*
* Pixels must be iterated in the four possible directions: (1) left to
* right, for each row (top to bottom); (2) from right to left, for each
* row (from top to bottom); (3) from top to bottom, for each column
* (from left to right); (4) from bottom to top, for each column (from
* left to right).
*
* In each iteration, each white pixel is replaced with a transparent
* one. Each iteration ends when a pixel of color other than white (or
* a transparent pixel) is encountered.
*/
if (source == null) {
throw new IllegalArgumentException("ImageUtilities.makeBackgroundTransparent -> null source image");
}
if (source instanceof FontImage) {
source = ((FontImage) source).toImage();
}
int[] pixels = source.getRGB(); // array instance containing the ARGB data within this image
int width = source.getWidth();
int height = source.getHeight();
int tolerance = 1000000; // value chosen through several attempts
// check if the first pixel is transparent
if ((pixels[0] >> 24) == 0x00) {
return source; // nothing to do, the image already has a transparent background
}
Log.p("Converting white background to transparent...", Log.DEBUG);
// 1. Left to right, for each row (top to bottom)
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int color = pixels[y * width + x];
if ((color >> 24) != 0x00 && color >= ColorUtil.WHITE - tolerance && color <= ColorUtil.WHITE + tolerance) { // means white with tolerance and no transparency
pixels[y * width + x] = 0x00; // means full transparency
} else {
break;
}
}
}
// 2. Right to left, for each row (top to bottom)
for (int y = 0; y < height; y++) {
for (int x = width - 1; x >= 0; x--) {
int color = pixels[y * width + x];
if ((color >> 24) != 0x00 && color >= ColorUtil.WHITE - tolerance && color <= ColorUtil.WHITE + tolerance) { // means white with tolerance and no transparency
pixels[y * width + x] = 0x00; // means full transparency
} else {
break;
}
}
}
// 3. Top to bottom, for each column (from left to right)
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int color = pixels[y * width + x];
if ((color >> 24) != 0x00 && color >= ColorUtil.WHITE - tolerance && color <= ColorUtil.WHITE + tolerance) { // means white with tolerance and no transparency
pixels[y * width + x] = 0x00; // means full transparency
} else {
break;
}
}
}
// 4. Bottom to top, for each column (from left to right)
for (int x = 0; x < width; x++) {
for (int y = height - 1; y >= 0; y--) {
int color = pixels[y * width + x];
if ((color >> 24) != 0x00 && color >= ColorUtil.WHITE - tolerance && color <= ColorUtil.WHITE + tolerance) { // means white with tolerance and no transparency
pixels[y * width + x] = 0x00; // means full transparency
} else {
break;
}
}
}
return EncodedImage.createFromRGB(pixels, width, height, false);
}

Related

Draw Image using CYMK values

I want to convert a buffered image from RGBA format to CYMK format without using auto conversion tools or libraries,so i tried to extract the RGBA values from individual pixels that i got using BufferedImage.getRGB() and here what I've done so far :
BufferedImage img = new BufferedImage("image path")
int R,G,B,pixel,A;
float Rc,Gc,Bc,K,C,M,Y;
int height = img.getHeight();
int width = img.getWidth();
for(int y = 0 ; y < height ; y++){
for(int x = 0 ; x < width ; x++){
pixel = img.getRGB(x, y);
//I shifted the int bytes to get RGBA values
A = (pixel>>24)&0xff;
R = (pixel>>16)&0xff;
G = (pixel>>8)&0xff;
B = (pixel)&0xff;
Rc = (float) ((float)R/255.0);
Gc = (float) ((float)G/255.0);
Bc = (float) ((float)B/255.0);
// Equations i found on the internet to get CYMK values
K = 1 - Math.max(Bc, Math.max(Rc, Gc));
C = (1- Rc - K)/(1-K);
Y = (1- Bc - K)/(1-K);
M = (1- Gc - K)/(1-K);
}
}
Now after I've extracted it ,i want to draw or construct an image using theses values ,can you tell me of a method or a way to do this because i don't thinkBufferedImage.setRGB() would work ,and also when i printed the values of C,Y,M some of them hadNaN value can someone tell me what that means and how to deal with it ?
While it is possible, converting RGB to CMYK without a proper color profile will not produce the best results. For better performance and higher color fidelity, I really recommend using an ICC color profile (see ICC_Profile and ICC_ColorSpace classes) and ColorConvertOp. :-)
Anyway, here's how to do it using your own conversion. The important part is creating a CMYK color space, and a ColorModel and BufferedImage using that color space (you could also load a CMYK color space from an ICC profile as mentioned above, but the colors would probably look more off, as it uses different calculations than you do).
public static void main(String[] args) throws IOException {
BufferedImage img = ImageIO.read(new File(args[0]));
int height = img.getHeight();
int width = img.getWidth();
// Create a color model and image in CMYK color space (see custom class below)
ComponentColorModel cmykModel = new ComponentColorModel(CMYKColorSpace.INSTANCE, false, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
BufferedImage cmykImg = new BufferedImage(cmykModel, cmykModel.createCompatibleWritableRaster(width, height), cmykModel.isAlphaPremultiplied(), null);
WritableRaster cmykRaster = cmykImg.getRaster();
int R,G,B,pixel;
float Rc,Gc,Bc,K,C,M,Y;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
pixel = img.getRGB(x, y);
// Now, as cmykImg already is in CMYK color space, you could actually just invoke
//cmykImg.setRGB(x, y, pixel);
// and the method would perform automatic conversion to the dest color space (CMYK)
// But, here you go... (I just cleaned up your code a little bit):
R = (pixel >> 16) & 0xff;
G = (pixel >> 8) & 0xff;
B = (pixel) & 0xff;
Rc = R / 255f;
Gc = G / 255f;
Bc = B / 255f;
// Equations I found on the internet to get CMYK values
K = 1 - Math.max(Bc, Math.max(Rc, Gc));
if (K == 1f) {
// All black (this is where you would get NaN values I think)
C = M = Y = 0;
}
else {
C = (1- Rc - K)/(1-K);
M = (1- Gc - K)/(1-K);
Y = (1- Bc - K)/(1-K);
}
// ...and store the CMYK values (as bytes in 0..255 range) in the raster
cmykRaster.setDataElements(x, y, new byte[] {(byte) (C * 255), (byte) (M * 255), (byte) (Y * 255), (byte) (K * 255)});
}
}
// You should now have a CMYK buffered image
System.out.println("cmykImg: " + cmykImg);
}
// A simple and not very accurate CMYK color space
// Full source at https://github.com/haraldk/TwelveMonkeys/blob/master/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/color/CMYKColorSpace.java
final static class CMYKColorSpace extends ColorSpace {
static final ColorSpace INSTANCE = new CMYKColorSpace();
final ColorSpace sRGB = getInstance(CS_sRGB);
private CMYKColorSpace() {
super(ColorSpace.TYPE_CMYK, 4);
}
public static ColorSpace getInstance() {
return INSTANCE;
}
public float[] toRGB(float[] colorvalue) {
return new float[]{
(1 - colorvalue[0]) * (1 - colorvalue[3]),
(1 - colorvalue[1]) * (1 - colorvalue[3]),
(1 - colorvalue[2]) * (1 - colorvalue[3])
};
}
public float[] fromRGB(float[] rgbvalue) {
// NOTE: This is essentially the same equation you use, except
// this is slightly optimized, and values are already in range [0..1]
// Compute CMY
float c = 1 - rgbvalue[0];
float m = 1 - rgbvalue[1];
float y = 1 - rgbvalue[2];
// Find K
float k = Math.min(c, Math.min(m, y));
// Convert to CMYK values
return new float[]{(c - k), (m - k), (y - k), k};
}
public float[] toCIEXYZ(float[] colorvalue) {
return sRGB.toCIEXYZ(toRGB(colorvalue));
}
public float[] fromCIEXYZ(float[] colorvalue) {
return sRGB.fromCIEXYZ(fromRGB(colorvalue));
}
}
PS: Your question talks about RGBA and CMYK, but your code just ignores the alpha value, so I did the same. If you really wanted to, you could just keep the alpha value as-is and have a CMYK+A image, to allow alpha-compositing in CMYK color space. I'll leave that as an exercise. ;-)

How to threshold any image (for black and white color seperation) in java?

SOLVED : Issue was "jpeg compression". Saving as ".png" worked.
I had detected edges of an image using a canny filter program in java.
After applying filter ...
This is my image
If zoomed in ...
Zoomed
All have different shades of black and white.
I want all my edge pixels as pure white(#FFFFFF) and the remaining portion black.
Note: Different pixels may have different shades apart from the one above(#F7F7F7). The zoomed image above is just an example.
Edit:
I had written this code to take effect on image ...
public void convert(){
try{
BufferedImage img = ImageIO.read(new File("input.jpg"));
int rgb;
int height = img.getHeight();
int width = img.getWidth();
File f = new File("newThreshold.jpg");
Color white = new Color(255,255,255);
int wh = white.getRGB();
for (int h = 0; h<height; h++){
for (int w = 0; w<width; w++){
rgb = img.getRGB(w, h);
red = (rgb & 0x00ff0000) >> 16;
green = (rgb & 0x0000ff00) >> 8;
blue = rgb & 0x000000ff;
if(red >= 200 || blue >= 200 || green >= 200){
img.setRGB(w,h,wh);
}
}
}
ImageIO.write(img,"jpg",f);
}
catch(Exception e){
}
}
Even after running the code, there is no change in my image.
Even if the red, green and blue values are above 200, my image is not changing.
UPDATE: Saving the image as ".png" rather than ".jpg" worked!
You can go through each pixel in the image and determine if it is above a certain threshold, if it is set its value to pure white. You can also do the same for the darker areas if needed.
Example:
public Image thresholdWhite(Image in, int threshold)
{
Pixel[][] pixels = in.getPixels();
for(int i = 0; i < pixels.length; ++i)
{
for(int j = 0; j < pixels[i].length; ++j)
{
byte red = pixels[i][j].getRed();
byte green = pixels[i][j].getGreen();
byte blue = pixels[i][j].getBlue();
/* In case it isn't a grayscale image, if it is grayscale this if can be removed (the block is still needed though) */
if(Math.abs(red - green) >= 16 && Math.abs(red - blue) >= 16 && Math.abs(blue- green) >= 16)
{
if(red >= threshold || blue >= threshold || green >= threshold)
{
pixels[i][j] = new Pixel(Colors.WHITE);
}
}
}
}
return new Image(pixels);
}

How to remove a border with an unknown width from an image

I'm trying to build a program which can remove an single-colored border form an image.
The border is always white but the width of the border on the left and right side might differ from the width of the border at the top and bottom. So the image I want to extract is centered within the source image.
So from the following image I want to extract the green rectangle.
At the moment I don't know how to start solving this problem.
UPDATE
So finally calsign's code snippet and some improvements on it, solves my problem. I realized that the border around the inner image may not be completely single colored but can vary slightly. This leads to the behavior that for some images were left with a small border.
I solved this problem by improving the comparison of the color of two pixels by comparing the color distance of the two colors with a threshold. When the distance is below the threshold then the colors are handled as equally.
public Bitmap cropBorderFromBitmap(Bitmap bmp) {
//Convenience variables
int width = bmp.getWidth();
int height = bmp.getHeight();
int[] pixels = new int[height * width];
//Load the pixel data into the pixels array
bmp.getPixels(pixels, 0, width, 0, 0, width, height);
int length = pixels.length;
int borderColor = pixels[0];
//Locate the start of the border
int borderStart = 0;
for(int i = 0; i < length; i ++) {
// 1. Compare the color of two pixels whether they differ
// 2. Check whether the difference is significant
if(pixels[i] != borderColor && !sameColor(borderColor, pixels[i])) {
Log.i(TAG,"Current Color: " + pixels[i]);
borderStart = i;
break;
}
}
//Locate the end of the border
int borderEnd = 0;
for(int i = length - 1; i >= 0; i --) {
if(pixels[i] != borderColor && !sameColor(borderColor, pixels[i])) {
Log.i(TAG,"Current Color: " + pixels[i]);
borderEnd = length - i;
break;
}
}
//Calculate the margins
int leftMargin = borderStart % width;
int rightMargin = borderEnd % width;
int topMargin = borderStart / width;
int bottomMargin = borderEnd / width;
//Create the new, cropped version of the Bitmap
bmp = Bitmap.createBitmap(bmp, leftMargin, topMargin, width - leftMargin - rightMargin, height - topMargin - bottomMargin);
return bmp;
}
private boolean sameColor(int color1, int color2){
// Split colors into RGB values
long r1 = (color1)&0xFF;
long g1 = (color1 >>8)&0xFF;
long b1 = (color1 >>16)&0xFF;
long r2 = (color2)&0xFF;
long g2 = (color2 >>8)&0xFF;
long b2 = (color2 >>16)&0xFF;
long dist = (r2 - r1) * (r2 - r1) + (g2 - g1) * (g2 - g1) + (b2 - b1) *(b2 - b1);
// Check vs. threshold
return dist < 200;
}
Perhaps not the best use of the APIs to find a solution, but the one that came to mind: directly modify the image's pixels.
You can get a Bitmap's pixels with getPixels() and then create a new, cropped Bitmap with createBitmap(). Then, it's just a matter of finding the dimensions of the border.
You can find the color of the border by accessing the pixel located at position 0, and then compare that value (an int) to the value of each proceeding pixel until your reach the border (the pixel that isn't that color). With a little bit of math, it can be done.
Here is some simple code that demonstrates the point:
private void cropBorderFromBitmap(Bitmap bmp) {
int[] pixels;
//Load the pixel data into the pixels array
bmp.getPixels(pixels, 0, width, 0, 0, width, height);
//Convenience variables
int width = bmp.getWidth();
int height = bmp.getHeight();
int length = pixels.length;
int borderColor = pixels[0];
//Locate the start of the border
int borderStart;
for(int i = 0; i < length; i ++) {
if(pixels[i] != borderColor) {
borderStart = i;
break;
}
}
//Locate the end of the border
int borderEnd;
for(int i = length - 1; i >= 0; i --) {
if(pixels[i] != borderColor) {
borderEnd = length - i;
break;
}
}
//Calculate the margins
int leftMargin = borderStart % width;
int rightMargin = borderEnd % width;
int topMargin = borderStart / width;
int bottomMargin = borderEnd / width;
//Create the new, cropped version of the Bitmap
bmp = createBitmap(bmp, leftMargin, topMargin, width - leftMargin - rightMargin, height - topMargin - bottomMargin);
}
This is untested and lacks error checking (e.g., what if the width is 0?), but it should serve as a proof-of-concept.
EDIT: I just realized that I failed to complete the getPixels() method. The wonders of testing your code... it's fixed now.
If the frame around your picture is uniform then all you need to do is investigate when the pixels in the image change.
But first thing's first - you need to have a BufferedImage object to work with. It's a class that allows you to traverse the bitmap of an image (http://docs.oracle.com/javase/6/docs/api/java/awt/image/BufferedImage.html).
If you have the image saved as a file you need to call this method:
BufferedImage bimage = ImageIO.read(new File(file));
Now you can fetch the bitmap array from the bimage:
bimage.getRGB(int startX, int startY, int w, int h, int[] rgbArray, int offset, int scansize)
like this:
int[] rgb = bimage.getRGB(0, 0, bimage.getWidth(), bimage.getHeight(), null, 0, bimage.getWidth());
There could be some issues here with ColorModel so be sure to read up on your documentation of how to fetch the appropriate rgb from different file types.
Now that you have the rgb array you should start searching how far the frame stretches out from the middle of the picture. Keep in mind that this a single dimensional array - all the lines are written here sequentially one after another - as if you sliced the picture into lines 1pixel heigh and glued them together to form one long line.
This actually works to our advantage because the first different pixel we encounter in this table will work as a great reference point.
So now we just do something like this:
int pixel1=0,pixel2=0, i=0;
while(pixel1==pixel2 && i<bimage.getWidth()*bimage.getHeight()){
pixel1=pixel2;
pixel2=rgb[i++];
}
So now if the frame of your image is uniform, the top offset is the same as the bottom offset and the left offset is the same as the right offset then the number in the variable i is very likely to be the first pixel in the green rectangle.
In order to know which row and which column it is you need the following code:
int row= i%bimage.getWidth();
int column= i - row*bimage.getWidth();
Now the problem is that you may have an image embedded in the frame that in it's left upper corner is of the same color as the frame - so for example an image of a green rectangle with white corners in a white frame. Is this the case?
You can use the public int getPixel (int x, int y) function which return for every pixel its color
It should be easy to run through the border lines and verify that the color is still the same
This is my solution:
private Bitmap cropBorderFromBitmap(Bitmap bmp) {
final int borderWidth = 10; //preserved border width
final int borderColor = -1; //WHITE
int width = bmp.getWidth();
int height = bmp.getHeight();
int[] pixels = new int[width * height];
bmp.getPixels(pixels, 0, width, 0, 0, width, height);
int minX = -1;
int minY = -1;
int maxX = -1;
int maxY = -1;
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
if(bmp.getPixel(x,y) != borderColor) {
minX = (minX == -1) ? x : Math.min(x, minX);
minY = (minY == -1) ? y : Math.min(y, minY);
maxX = (maxX == -1) ? x : Math.max(x, maxX);
maxY = (maxY == -1) ? y : Math.max(y, maxY);
}
}
}
minX = Math.max(0, minX - borderWidth);
maxX = Math.min(width, maxX + borderWidth);
minY = Math.max(0, minY - borderWidth);
maxY = Math.min(height, maxY + borderWidth);
//Create the new, cropped version of the Bitmap
return Bitmap.createBitmap(bmp, minX, minY, maxX - minX, maxY-minY);
}

java image color formulas

I know the java code for grayscale is this( 0.2126 * red + 0.7152 * green + 0.0722 * blue(
I was wondering if anyone knows how I can find more variety of coloring formulas, like if i want to make the picture old fashion way, more orange, make it brighter, or darker ... sharper and so on
int pixel = image.getRGB(j, i);
int red = (pixel) & 0xff;
int green = (pixel >> 8) & 0xff;
int blue = (pixel >> 16) & 0xff;
int newPixel = (int) (0.2126 * red + 0.7152 * green + 0.0722 * blue);
image1.setRGB(j, i, newPixel);
The old fashion way you mention is called "sepia" effect. Take a look at this question particularly this answer which points out to the following code snippet (note that I did not write this code, just helping out in finding answers to your question)
/**
*
* #param img Image to modify
* #param sepiaIntensity From 0-255, 30 produces nice results
* #throws Exception
*/
public static void applySepiaFilter(BufferedImage img, int
sepiaIntensity) throws Exception
{
// Play around with this. 20 works well and was recommended
// by another developer. 0 produces a grey image
int sepiaDepth = 20;
int w = img.getWidth();
int h = img.getHeight();
WritableRaster raster = img.getRaster();
// We need 3 integers (for R,G,B color values) per pixel.
int[] pixels = new int[w*h*3];
raster.getPixels(0, 0, w, h, pixels);
// Process 3 ints at a time for each pixel. Each pixel has 3 RGB
colors in array
for (int i=0;i<pixels.length; i+=3)
{
int r = pixels[i];
int g = pixels[i+1];
int b = pixels[i+2];
int gry = (r + g + b) / 3;
r = g = b = gry;
r = r + (sepiaDepth * 2);
g = g + sepiaDepth;
if (r>255) r=255;
if (g>255) g=255;
if (b>255) b=255;
// Darken blue color to increase sepia effect
b-= sepiaIntensity;
// normalize if out of bounds
if (b<0) b=0;
if (b>255) b=255;
pixels[i] = r;
pixels[i+1]= g;
pixels[i+2] = b;
}
raster.setPixels(0, 0, w, h, pixels);
}
I would just play with the numbers.
more orange,
more red and a little more green (red + green = yellow)
brighter
increase all the factors
darker
decrease all the factors
sharper
This is specific filter which compare surrounding pixels to find edges. It not just a matter of playing with the colours.
BTW: You should add capping of the values. i.e. Math.min(255, Math.max(0, value))
You can manipulate the proportion between the color channels in order to change the scene "atmosphere". The images below were created using the ColorChannel plug-in.
The algorithm source code is presented below. The method getAttribute() gets the parameters (red,gree,blue) passed by the user. The methods getIntComponent0, getIntComponent1 and getIntComponent2 get each color channel (red, gree and blue). The method setIntColor sets back the value of each channel.
#Override
public void process
(
MarvinImage imageIn,
MarvinImage imageOut,
MarvinAttributes attrOut,
MarvinImageMask mask,
boolean preview
) {
int vr = (Integer)getAttribute("red");
int vg = (Integer)getAttribute("green");
int vb = (Integer)getAttribute("blue");
double mr = 1+Math.abs((vr/100.0)*2.5);
double mg = 1+Math.abs((vg/100.0)*2.5);
double mb = 1+Math.abs((vb/100.0)*2.5);
mr = (vr > 0? mr : 1.0/mr);
mg = (vg > 0? mg : 1.0/mg);
mb = (vb > 0? mb : 1.0/mb);
int red,green,blue;
for(int y=0; y<imageIn.getHeight(); y++){
for(int x=0; x<imageIn.getWidth(); x++){
red = imageIn.getIntComponent0(x, y);
green = imageIn.getIntComponent1(x, y);
blue = imageIn.getIntComponent2(x, y);
red = (int)Math.min(red * mr, 255);
green = (int)Math.min(green * mg, 255);
blue = (int)Math.min(blue * mb, 255);
imageOut.setIntColor(x, y, 255, red, green, blue);
}
}
}

Finding border pixels of a image with transparent surrounding (for collision detection)

I'm programming a game and I want to collide images with transparent borders (sprites) against a circle.
It's easy to know if the circle is overlapping the image by checking collision with the pixels which are not transparent.
The problem I have is to know the normal angle in order to make a bounce.
I would need a library (Java) or algorithm that given an image it would return an array with the pixels that are at the border of the image so i can find the slope between two points of the surface.
Is there any library/algorithm/code snippet i can learn from?
Here's a simple approach:
Create a mask from the original image where all transparent pixels are 0 and all non-transparent pixels are 1
Then, perform a simple edge detection on your mask by subtracting each pixel (x,y), which will be 0 or 1, from pixel (x+1,y+1) and taking the absolute value.
This will give you a 1 for pixels on the edge of the image and a 0 everywhere else.
Note: this method is essentially equivalent to treating the image as a 2d function and calculating its gradient. The edges are steep parts of the intensity surface (which correspond to large gradient values). Here's some more information on gradient-based edge detection.
Here's an example image:
First mask all the non-transparent pixels:
Then shift the image down and over one pixel and subtract it from itself.
This creates the image below. Now simply read out the matrix indices with value 1.
That's your array of edge pixels.
Note: if your images contain interior transparent pixels, this technique will also find interior edges, which may or may not be a problem for you...
This is what I'v implemented over the time: (detectionStrength is best 10)
public static List<Pixel> getEdges(Image image, int detectionStrength) {
boolean[][] opaque = new boolean[image.getWidth(null)][image
.getHeight(null)];
LinkedList<Pixel> edges = new LinkedList<Pixel>();
int rgb;
/*
* convert to BufferedImage to get individual pixel colors
*/
BufferedImage bufferedImage;
if (image instanceof BufferedImage)
bufferedImage = (BufferedImage) image;
else {
bufferedImage = new BufferedImage(image.getWidth(null),
image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
bufferedImage.createGraphics().drawImage(image, 0, 0, null);
}
for (int i = 0; i < opaque.length; i++) {
for (int j = 0; j < opaque[i].length; j++) {
rgb = bufferedImage.getRGB(i, j);
opaque[i][j] = (rgb >> 24 & 0xFF) > detectionStrength; // transparency
}
}
/*
* If a pixel is opaque, but is surrounded, with at least one
* transparent pixel, it is considered an edge.
*/
for (int x = 0; x < opaque.length; x++) {
for (int y = 0; y < opaque[x].length; y++) {
if ((x == 0) || (x == opaque.length - 1) || (y == 0)
|| (y == opaque[x].length - 1)) { // border pixel
if (opaque[x][y]) // if opaque, it is automatically an edge,
// no matter its surrounding...
edges.add(new Pixel(x, y, new Color(bufferedImage
.getRGB(x, y))));
} else { // not a border pixel
if (opaque[x][y]
&& (!opaque[x - 1][y - 1] || !opaque[x][y - 1]
|| !opaque[x + 1][y - 1]
|| !opaque[x - 1][y] || !opaque[x + 1][y]
|| !opaque[x - 1][y + 1]
|| !opaque[x][y + 1] || !opaque[x + 1][y + 1]))
edges.add(new Pixel(x, y, new Color(bufferedImage
.getRGB(x, y))));
}
}
}
return edges;
}
And the Pixel class (just a very simple extension of Point) :
public class Pixel extends Point implements Cloneable {
private static final long serialVersionUID = -9053911985748552077L;
public Color color;
public Pixel(int x, int y, Color c) {
super(x, y);
color = c;
}
public Pixel(Pixel other) {
super(other.x, other.y);
color = other.color;
}
public Color getColor() {
return color;
}
public void setColor(Color newColor) {
color = newColor;
}
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((color == null) ? 0 : color.hashCode());
return result;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (!(obj instanceof Pixel))
return false;
Pixel other = (Pixel) obj;
if (color == null) {
if (other.color != null)
return false;
} else if (!color.equals(other.color))
return false;
return true;
}
public Object clone() {
return new Pixel(x, y, color);
}
public String toString() {
return "Pixel [color=" + color + ", x=" + x + ", y=" + y + "]";
}
}
The image created with the algorithm, will be:

Categories

Resources