Create polygon of non-transparent parts of image - java

What I am trying to achieve is getting a mask of an image, converting it into an Array with the dimensions (image.getWidth(), image.getHeight()), then getting all the pixels and seeing if they have an alpha value of 0.
IF they do, then:
add value 1 for the x, y co-ordinate that I am examining at the moment.
ELSE:
add value 0 for the x, y co-ordinate that I am examining at the moment.
Up to this point, I know how to program this. If you are interested, this is the code I am using:
private int[] createMask(BufferedImage image) {
final int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
final int width = image.getWidth();
final int height = image.getHeight();
final boolean hasAlphaChannel = image.getAlphaRaster() != null;
int[][] result = new int[height][width];
if (hasAlphaChannel) {
for (int pixel = 0, row = 0, col = 0; pixel < pixels.length; pixel += 4) {
int alpha = pixels[pixel];
if(alpha != 0) {
result[row][col] = 1;
} else {
result[row][col] = 0;
}
if (col == width) {
col = 0;
row++;
}
}
}
return result;
}
After I get this mask, I am attempting to use this Array to draw a Polygon via this code, or an alternation of it (obtained from http://docs.oracle.com/javase/tutorial/2d/geometry/arbitrary.html) :
int x1Points[] = {0, 100, 0, 100};
int y1Points[] = {0, 50, 50, 0};
GeneralPath polygon =
new GeneralPath(GeneralPath.WIND_EVEN_ODD,
x1Points.length);
polygon.moveTo(x1Points[0], y1Points[0]);
for (int index = 1; index < x1Points.length; index++) {
polygon.lineTo(x1Points[index], y1Points[index]);
};
polygon.closePath();
g2.draw(polygon);
However, I need to create a method which gives me all the co-ordinates in an Array consisting of Point objects to go around the image to essentially create a "mask".
public Point[] getCords(int[] mask) {
ArrayList<Point> points = new ArrayList<Point>(); //you can change this to whatever you want to use
//get coords to surround the mask
// >> involving `0` (for transparent) and `1` (non transparent)
// >> these are contained in the `mask` Array...
return points.toArray(new Points[0]);
So, to conclude:
I require to obtain a polygon that outlines an image's visible pixels from an int[] array which contains the values 1's and 0's, the former for non-transparent pixel and latter for transparent pixel, respectively.

(link to related java code below)
To create the mask border, do the following: for each pair of coordinates (x,y) check if any one of 8 its neighboring points is outside the mask. However, keep in mind that the resulting mask isn't necessarily 1 pixel wide and vectorizing it might be non-trivial, as in this example (white is the masked area, red is the mask border inside the mask, black is unmasked area):
Luckily, even if you get a wider-than-1-pixel border in some places in your mask, you can workaround that by rejecting some pixels of the mask from that mask building a submask that is polygonizable. The following image shows the submask's border in blue:
I implemented such algorithm a while ago. There is the code you can use, but it is quite tightly coupled with my solution, however you could find some insights in it: Thick mask border resolution. Its idea is that from the initial mask you build a submask by flood-filling original mask with a predicate that checks that a cell of submask's border has at most 2 cardinal direction neighbors (ordinal direction neighbors don't matter here).
Once you got the blue submask's border, build a graph where vertices are points of submask's border, and edges are between cardinally neighboring points. Then traverse each component of that graph, and for each component you get a list of points that form your polygons.

Related

Turning a Double into color ints in Android Studio

I'm working with an image in android studio. To make it simple let's say it only has 9 pixels. The values of the pixel are held in a double matrix in, lets say its something I like this:
double[][] matrix = {{-0.5, -0.4, -0.3},
{-0.1, -0.9, -0.4},
{-0.5, -0.6, -0.9}}
I want to turn each of those doubles into a color int so I can put them into an image pixel by pixel. (In Matlab you'd use something like colormap('jet')).
Bitmap bitmap = Bitmap.createBitmap(length, height, Bitmap.Config.RGB_565);
for(int i = 0; i< length ; i++ ){
for (int j = 0; j < height; j++){
//Convert number in matrix to color from blue to red
int color;
//Make correlating pixel that color
bitmap.setPixel(i,j, color);
}
}
I have tried using the Scichart SDK but the license is too expensive.
I also tried the Mines package but it depends on Java AWT which is not available in android studio
I can't find anything on the documentation.
A quick way to map a value to a color by using one of the methods in Color class.
For example, using Color.HSVToColor and passing your value in as the "hue" will give you a rainbow color palette. The appropriate mapping will depend on what range the values in your matrix can take. If they are in the range -1 to +1, this will work for you:
float hue = (float) (matrix[i][j] + 1) * 180;
float[] hsv = { hue, 1f, 1f };
int color = Color.HSVToColor(hsv);

Java - Convert Image to black and white - fails with bright colors

I'm attempting to convert an image to black and white only (not grey scale).
I've used this:
BufferedImage blackAndWhiteImage = new BufferedImage(
dWidth.intValue(),
dHeight.intValue(),
BufferedImage.TYPE_BYTE_BINARY);
Graphics2D graphics = blackAndWhiteImage.createGraphics();
graphics.drawImage(colourImage, 0, 0, null);
return blackAndWhiteImage;
Everything fine, until I decided to try out brighter colors, like the Google logo for example:
and it came out with this:
Then I tried first to pass trough grey scale first using:
BufferedImage blackAndWhiteImage2 = new BufferedImage(
dWidth.intValue(),
dHeight.intValue(),
BufferedImage.TYPE_USHORT_GRAY);
And it seemed to have saved the Blue color, but not the brightest (in this case yellow), and as you may see it decreased in quality:
Any suggestions are much appreciated; I believe what I am seeking for is to convert every colour to Black except White (which would be the background color), this is already done when applying TYPE_BYTE_BINARY removing the alpha channel.
EDIT:
Maybe I have not explained very clear:
the final image has to have White background **1
every other color has to be converted to Black
**1 - there are some cases where the image is actually White on Black..which is annoying (whiteOnBlackExample) as it complicates a lot this process, and I will leave this later on, as priority now is to convert "normal" images.
What I did was, first strip out the alpha channel if it exists -> therefore convert the alpha channel to White; then convert every other color to Black
If you use JavaFX you can use the ColorAdjust effect with brightness of -1 (minimum), which makes all the (non-white) colors black:
public class Main extends Application {
Image image = new Image("https://i.stack.imgur.com/UPmqE.png");
#Override
public void start(Stage primaryStage) {
ImageView colorView = new ImageView(image);
ImageView bhView = new ImageView(image);
ColorAdjust colorAdjust = new ColorAdjust();
colorAdjust.setBrightness(-1);
bhView.setEffect(colorAdjust);
primaryStage.setScene(new Scene(new VBox(colorView, bhView)));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
These Effects are optimized so they are probably faster than what you would achieve by applying them manually.
Edit
Since your requirements are that
any pixel which is not opaque should be transformed to white, and
any pixel which is not white should be transformed to black,
the predesigned effects won't suit you as far as I can tell - they are too specific. You can do pixel by pixel manipulation:
WritableImage writableImage = new WritableImage(image.getPixelReader(), (int) image.getWidth(), (int) image.getHeight());
PixelWriter pixelWriter = writableImage.getPixelWriter();
PixelReader pixelReader = writableImage.getPixelReader();
for (int i = 0; i < writableImage.getHeight(); i++) {
for (int j = 0; j < writableImage.getWidth(); j++) {
Color c = pixelReader.getColor(j, i);
if (c.getOpacity() < 1) {
pixelWriter.setColor(j, i, Color.WHITE);
}
if (c.getRed() > 0 || c.getGreen() > 0 || c.getBlue() > 0) {
pixelWriter.setColor(j, i, Color.BLACK);
}
}
}
ImageView imageView = new ImageView(writableImage);
Note that the order in which you apply the rules matter. A transparent non-white pixel will turn white if you apply 1 and then 2, but if you apply 2 and then 1 it will end up black. This is because the predefined WHITE and BLACK colors are opaque. You can manually set the red, green and blue values while not changing the alpha value instead. It all depends on your exact requirements.
Remember that due to lossy compression of some file formats you might not find true white in them at all, but a value which is close to true white and your eye won't be able to tell the difference.
Here is the example from my comment. At first open the input image and create a new one for output.
BufferedImage myColorImage = ImageIO.read(fileInput);
BufferedImage myBWImage = new BufferedImage(myColorImage.getWidth(), myColorImage.getHeight(), BufferedImage.TYPE_BYTE_BINARY);
Then iterate through all the pixels and compare rgb values with a threshold:
for (int x = 0; x < myColorImage.getWidth(); x++)
for (int y = 0; y < myColorImage.getHeight(); y++)
if (rgbToGray(myColorImage.getRGB(x, y), MODE.AVERAGE) > threshold)
myBWImage.setRGB(x, y, 0);
else
myBWImage.setRGB(x, y, 0xffffff);
Here is rgbToGray method implementation to compare with threshold:
private static int rgbToGray(int rgb, MODE mode) {
// split rgb integer into R, G and B components
int r = (rgb >> 16) & 0xff;
int g = (rgb >> 8) & 0xff;
int b = rgb & 0xff;
int gray;
// Select mode
switch (mode) {
case LIGHTNESS:
gray = Math.round((Math.max(r, Math.max(g, b)) + Math.min(r, Math.min(g, b))) / 2);
break;
case LUMINOSITY:
gray = Math.round(0.21f * r + 0.72f * g + 0.07f * b);
break;
case AVERAGE:
default:
gray = Math.round((r + g + b) / 3);
break;
}
return gray;
}
An utility enum:
private enum MODE {
LIGHTNESS, AVERAGE, LUMINOSITY
}
I got the following result:
Note: for your google image even threshold = 1 is suitable, for other images you should pick another values from range [0..255]. For photos, most likely, more appropriate values are about 100-150. MODE also will affect the final result.

Java Convolution

Hi I am in need of some help. I need to write a convolution method from scratch that takes in the following inputs: int[][] and BufferedImage inputImage. I can assume that the kernel has size 3x3.
My approach is to do the follow:
convolve inner pixels
convolve corner pixels
convolve outer pixels
In the program that I will post below I believe I convolve the inner pixels but I am a bit lost at how to convolve the corner and outer pixels. I am aware that corner pixels are at (0,0), (width-1,0), (0, height-1) and (width-1,height-1). I think I know to how approach the problem but not sure how to execute that in writing though. Please to aware that I am very new to programming :/ Any assistance will be very helpful to me.
import java.awt.*;
import java.awt.image.BufferedImage;
import com.programwithjava.basic.DrawingKit;
import java.util.Scanner;
public class Problem28 {
// maximum value of a sample
private static final int MAX_VALUE = 255;
//minimum value of a sample
private static final int MIN_VALUE = 0;
public BufferedImage convolve(int[][] kernel, BufferedImage inputImage) {
}
public BufferedImage convolveInner(double center, BufferedImage inputImage) {
int width = inputImage.getWidth();
int height = inputImage.getHeight();
BufferedImage inputImage1 = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
//inner pixels
for (int x = 1; x < width - 1; x++) {
for (int y = 1; y < height - 1; y ++) {
//get pixels at x, y
int colorValue = inputImage.getRGB(x, y);
Color pixelColor = new Color(colorValue);
int red = pixelColor.getRed() ;
int green = pixelColor.getGreen() ;
int blue = pixelColor.getBlue();
int innerred = (int) center*red;
int innergreen = (int) center*green;
int innerblue = (int) center*blue;
Color newPixelColor = new Color(innerred, innergreen, innerblue);
int newRgbvalue = newPixelColor.getRGB();
inputImage1.setRGB(x, y, newRgbvalue);
}
}
return inputImage1;
}
public BufferedImage convolveEdge(double edge, BufferedImage inputImage) {
int width = inputImage.getWidth();
int height = inputImage.getHeight();
BufferedImage inputImage2 = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
//inner pixels
for (int x = 0; x < width - 1; x++) {
for (int y = 0; y < height - 1; y ++) {
//get pixels at x, y
int colorValue = inputImage.getRGB(x, y);
Color pixelColor = new Color(colorValue);
int red = pixelColor.getRed() ;
int green = pixelColor.getGreen() ;
int blue = pixelColor.getBlue();
int innerred = (int) edge*red;
int innergreen = (int) edge*green;
int innerblue = (int) edge*blue;
Color newPixelColor = new Color(innerred, innergreen, innerblue);
int newRgbvalue = newPixelColor.getRGB();
inputImage2.setRGB(x, y, newRgbvalue);
}
}
return inputImage2;
}
public BufferedImage convolveCorner(double corner, BufferedImage inputImage) {
int width = inputImage.getWidth();
int height = inputImage.getHeight();
BufferedImage inputImage3 = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
//inner pixels
for (int x = 0; x < width - 1; x++) {
for (int y = 0; y < height - 1; y ++) {
//get pixels at x, y
int colorValue = inputImage.getRGB(x, y);
Color pixelColor = new Color(colorValue);
int red = pixelColor.getRed() ;
int green = pixelColor.getGreen() ;
int blue = pixelColor.getBlue();
int innerred = (int) corner*red;
int innergreen = (int) corner*green;
int innerblue = (int) corner*blue;
Color newPixelColor = new Color(innerred, innergreen, innerblue);
int newRgbvalue = newPixelColor.getRGB();
inputImage3.setRGB(x, y, newRgbvalue);
}
}
return inputImage3;
}
public static void main(String[] args) {
DrawingKit dk = new DrawingKit("Compositor", 1000, 1000);
BufferedImage p1 = dk.loadPicture("image/pattern1.jpg");
Problem28 c = new Problem28();
BufferedImage p5 = c.convolve();
dk.drawPicture(p5, 0, 100);
}
}
I changed the code a bit but the output comes out as black. What did I do wrong:
import java.awt.*;
import java.awt.image.BufferedImage;
import com.programwithjava.basic.DrawingKit;
import java.util.Scanner;
public class Problem28 {
// maximum value of a sample
private static final int MAX_VALUE = 255;
//minimum value of a sample
private static final int MIN_VALUE = 0;
public BufferedImage convolve(int[][] kernel, BufferedImage inputImage) {
int width = inputImage.getWidth();
int height = inputImage.getHeight();
BufferedImage inputImage1 = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
//for every pixel
for (int x = 0; x < width; x ++) {
for (int y = 0; y < height; y ++) {
int colorValue = inputImage.getRGB(x,y);
Color pixelColor = new Color(colorValue);
int red = pixelColor.getRed();
int green = pixelColor.getGreen();
int blue = pixelColor.getBlue();
double gray = 0;
//multiply every value of kernel with corresponding image pixel
for (int i = 0; i < 3; i ++) {
for (int j = 0; j < 3; j ++) {
int imageX = (x - 3/2 + i + width) % width;
int imageY = (x -3/2 + j + height) % height;
int RGB = inputImage.getRGB(imageX, imageY);
int GRAY = (RGB) & 0xff;
gray += (GRAY*kernel[i][j]);
}
}
int out;
out = (int) Math.min(Math.max(gray * 1, 0), 255);
inputImage1.setRGB(x, y, new Color(out,out,out).getRGB());
}
}
return inputImage1;
}
public static void main(String[] args) {
int[][] newArray = {{1/9, 1/9, 1/9}, {1/9, 1/9, 1/9}, {1/9, 1/9, 1/9}};
DrawingKit dk = new DrawingKit("Problem28", 1000, 1000);
BufferedImage p1 = dk.loadPicture("image/pattern1.jpg");
Problem28 c = new Problem28();
BufferedImage p2 = c.convolve(newArray, p1);
dk.drawPicture(p2, 0, 100);
}
}
Welcome ewuzz! I wrote a convolution using CUDA about a week ago, and the majority of my experience is with Java, so I feel qualified to provide advice for this problem.
Rather than writing all of the code for you, the best way to solve this large program is to discuss individual elements. You mentioned you are very new to programming. As the programs you write become more complex, it's essential to write small working snippets before combining them into a large successful program (or iteratively add snippets). With this being said, it's already apparent you're trying to debug a ~100 line program, and this approach will cost you time in most cases.
The first point to discuss is the general approach you mentioned. If you think about the program, what is the simplest and most repeated step? Obviously this is the kernel/mask step, so we can start from here. When you convolute each pixel, you are performing a similar option, regardless of the position (corner, edge, inside). While there are special steps necessary for these edge cases, they share similar underlying steps. If you try to write code for each of these cases separately, you will have to update the code in multiple (three) places with each adjustment and it will make the whole program more difficult to grasp.
To support my point above, here's what happened when I pasted your code into IntelliJ. This illustrates the (yellow) red flag of using the same code in multiple places:
The concrete way to fix this problem is to combine the three convolve methods into a single one and use if statements for edge-cases as necessary.
Our pseudocode with this change:
convolve(kernel, inputImage)
for each pixel in the image
convolve the single pixel and check edge cases
endfor
end
That seems pretty basic right? If we are able to successfully check edge cases, then this extremely simple logic will work. The reason I left it so general above to show how convolve the single pixel and check edge cases is logically grouped. This means it's a good candidate for extracting a method, which could look like:
private void convolvePixel(int x, int y, int[][] kernel, BufferedImage input, BufferedImage output)
Now to implement our method above, we will need to break it into a few steps, which we may then break into more steps if necessary. We'll need to look at the input image, if possible for each pixel accumulate the values using the kernel, and then set this in the output image. For brevity I will only write pseudocode from here.
convolvePixel(x, y, kernel, input, output)
accumulation = 0
for each row of kernel applicable pixels
for each column of kernel applicable pixels
if this neighboring pixel location is within the image boundaries then
input color = get the color at this neighboring pixel
adjusted value = input color * relative kernel mask value
accumulation += adjusted value
else
//handle this somehow, mentioned below
endif
endfor
endfor
set output pixel as accumulation, assuming this convolution method does not require normalization
end
The pseudocode above is already relatively long. When implementing you could write methods for the if and the else cases, but it you should be fine with this structure.
There are a few ways to handle the edge case of the else above. Your assignment probably specifies a requirement, but the fancy way is to tile around, and pretend like there's another instance of the same image next to this input image. Wikipedia explains three possibilities:
Extend - The nearest border pixels are conceptually extended as far as necessary to provide values for the convolution. Corner pixels are extended in 90° wedges. Other edge pixels are extended in lines.
Wrap - (The method I mentioned) The image is conceptually wrapped (or tiled) and values are taken from the opposite edge or corner.
Crop - Any pixel in the output image which would require values from beyond the edge is skipped. This method can result in the output image being slightly smaller, with the edges having been cropped.
A huge part of becoming a successful programmer is researching on your own. If you read about these methods, work through them on paper, run your convolvePixel method on single pixels, and compare the output to your results by hand, you will find success.
Summary:
Start by cleaning-up your code before anything.
Group the same code into one place.
Hammer out a small chunk (convolving a single pixel). Print out the result and the input values and verify they are correct.
Draw out edge/corner cases.
Read about ways to solve edge cases and decide what fits your needs.
Try implementing the else case through the same form of testing.
Call your convolveImage method with the loop, using the convolvePixel method you know works. Done!
You can look up pseudocode and even specific code to solve the exact problem, so I focused on providing general insight and strategies I have developed through my degree and personal experience. Good luck and please let me know if you want to discuss anything else in the comments below.
Java code for multiple blurs via convolution.

Fill not-transparent part of image by color

I have one task in Libgdx:
Change color of image for example triangle,star, heart and others shapes.
All shapes are given in png with transparent background.
I'm doing this with Pixmap, checking every pixel if it is not-transparent fill pixel with needed color.
Here is the code:
for (int y = 0; y < pixmap.getHeight(); y++) {
for (int x = 0; x < pixmap.getWidth(); x++) {
Color color = new Color();
Color.rgba8888ToColor(color, pixmap.getPixel(x, y));
if(color.r != 1 || color.b != 1 && color.g != 1){
pixmap.setColor(setColor);
pixmap.fillRectangle(x, y, 1, 1);
}
}
}
Is there any other way to do this?
Because method below works too long.
You can certainly speed up the way you're doing it, because right now for every pixel in the image you are instantiating a new Color object and converting the pixel components into separate floats. And then the GC will have to take time to clear up all those Color objects you are generating. Those extra intermediate steps are unnecessary.
Also, you only need to call pixmap.setColor one time (although that is fairly trivial). And you can use drawPixel instead of fillRectangle to more efficiently draw a single pixel.
static final int R = 0xFF000000;
static final int G = 0x00FF0000;
static final int B = 0x0000FF00;
pixmap.setColor(setColor);
for (int y = 0; y < pixmap.getHeight(); y++) {
for (int x = 0; x < pixmap.getWidth(); x++) {
int pixel = pixmap.getPixel(x, y);
if((pixel & R) != R || (pixel & B) != B && (pixel & G) != G){
pixmap.drawPixel(x, y);
}
}
}
(By the way, did you mean to check red or blue and green? Seems like odd criteria unless you only want to change the color if the original color is pure yellow, cyan, or white.)
If you are merely drawing the images as Textures, then there is no need to be operating on the Pixmaps like this. You could make your source image white and tint the image when drawing it with SpriteBatch, for example, and this would have no impact on performance.
Support library provides utilities to tint drawable.
// create a drawable from the bitmap
BitmapDrawable tintedDrawable = DrawableCompat.wrap(new BitmapDrawable(getResources(), pixmap));
// Apply a Tint, it will color all non-transparent pixel
DrawableCompat.setTint(setColor);
// Draw it back on a bitmap
Bitmap b = Bitmap.createBitmap(pixmap.getWidth(), pixmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
tintedDrawable.setBounds(0, 0, pixmap.getWidth(), pixmap.getHeight());
tintedDrawable.draw(c);
If you just need to show these pictures with a specific color in your application you can simply do it with setColorFilter
ImageView ivEx = (ImageView) findViewById(R.id.ivEx);
int color = Color.parseColor("your color's code");
ivEx.setColorFilter(color);

Determine touch/click of 4 Polygons from a Rectangle using libgdx

I'm currently working with libgdx, and am trying to get 4 equal Polygons from a Rectangle:
Rectangle myRect = Rectangle(0, 0, 171, 171);
I am looking to determine the 4 Polygons that represent each side of the Rectangle
This is my first day working with this engine, and I am a bit rusty on my geometry, so I'm looking for any help I can get. Essentialy, I'm going to use these Polygons to determine whether a specified X,Y pair is within them.
Thanks for the help.
You could find the mid point of the rectangle fairly easily, just average the height and width. From there you could manually construct a polygon, jumping from corner to corner to midpoint. You would lose some precision due to rounding, but you can use getX() and getWidth() if you need double precision.
public Polygon[] findTris(Rectangle rectangle){
//Creating a list of the x points of the rectangle, ordered clockwise.
new int[] xpoints = new int[5];
xpoints[0] = rectangle.x;
xpoints[1] = rectangle.x+rectangle.width;
xpoints[2] = rectangle.x+rectangle.width;
xpoints[3] = rectangle.x;
xpoints[4] = rectangle.x;
//Doing the same for y points.
int[] ypoints = new int[5];
ypoints[0] = rectangle.y;
ypoints[1] = rectangle.y;
ypoints[2] = rectangle.y+rectangle.height;
ypoints[3] = rectangle.y+rectangle.height;
ypoints[4] = rectangle.y;
//Finding the midpoint.
int midx = (rectangle.x+rectangle.width)/2;
int midy = (rectangle.y+rectangle.height)/2;
//Creating an array to hold the polygons.
Polygon[] polys = new Polygon[4];
//Creating the polygons.
for(int i = 0; i < 4; i++){
int[] triXPoints = {xpoints[i], xpoints[i+1], midx};
int[] triYPoints = {ypoints[i], ypoints[i+1], midy};
polys[i] = Polygon(xpoints,ypoints,3);
}
return polys;
}
Now that will work fine, but if all you are trying to do is find the mouse position in a square, you can use mouse maps. A mouse map is an image with distinctly different colors in each region that you want to be able to recognize the mouse in. You would store the map as a BufferedImage and whenever you needed to find the region the mouse was in, you can get the color of the buffered image at the appropriate position on the BufferedImage.
Here is the idea:
http://i.stack.imgur.com/iFPsl.png

Categories