I am using the following code currently to loop through pixels in an image and return the coordinates of the pixel with RGB values the same as defined in the if statement:
outerloop:
for (int y = 0; y < image.getHeight(); y = y + 1) {
for (int x = 0; x < image.getWidth(); x = x + 1) {
Color mycolor = new Color(image.getRGB(x, y));
int red = mycolor.getRed();
int green = mycolor.getGreen();
int blue = mycolor.getBlue();
if (red == 183 & green == 86 & blue == 182){
System.out.println(x,y);
break outerloop;
}
}
}
The problem now is that the RGB values vary very slightly every time in the application so I am trying to add a sort of "tolerance" to the currently constant RGB values. For example, in one case Red could be 185, green could be 89 and blue for example could be the same (182).
I understand I could just define all the conditions using the OR (||) function within the if statement but as this would require a lot of code is there any simpler solution? For example defining a positive tolerance as a constant and looping through all combinations of the RGB values within this tolerance?
It will be very slow to loop through all permutations of the colors within tolerance: let's say that you have a tolerance of +/- 5, that would require checking 1331 different colors (11 reds * 11 greens * 11 blues).
It is much faster just to change your condition red == 183 to something like Math.abs(red - 183) < tolerance or (red >= 183 - tolerance || red <= 183 + tolerance) (similarly for other channels).
Rather than check that your values are explicitly equal to a list of numbers, you'll be happier to check that they are within a certain range. You could do this with something like (180<x & x<185), but it's a little cleaner to use absolute value:
int TOLERANCE = 3;
boolean in_range(int value, int setpt) {
return abs(value-setpt) <= TOLERANCE;
}
And then in your loop, your conditional would look like:
int R_SETPT = 183;
int G_SETPT = 86;
int B_SETPT = 182;
if (in_range(red, R_SETPT) &
in_range(green, G_SETPT) &
in_range(blue, B_SETPT)) {
// etc.
Related
I have been working on a nonogram solver in Java, and all of my algorithm works fine, but I have been struggling with the visualization a little bit.
During the execution of the algorithm, I have access to two "solution-arrays". One is of type int[][], and contains values -1 for "certainly white", 0 for "uncertain" and 1 for "certainly black". Another array is of type float[][] which contains values between 0 and 1, here 0 is for certainly white, 1 is for certainly black and a value of .2 indicates it is more likely for the cell to be white than it to be black.
Since I've recently switched from PHP to Java programming (without proper introduction), I don't know much about visualizing this array properly. Of course I have first tried to simply print the first type of array to the console with characters like X, . and ?, but this is far from nice. I then found something about BufferedImage's, and I created the following function (for the float[][], the int[][] is similar):
public void toImage(int w, int h, float[][] solution) throws IOException {
int[] data = toImage1(w, h, solution);
BufferedImage img = toImage2(data, w, h);
toImage3(img);
}
public int[] toImage1(int w, int h, float[][] solution) throws IOException {
int[] data = new int[w * h];
int i = 0;
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
int a = y / 100;
int b = x / 100;
int z = (int) (255 * Math.sqrt(1 - solution[a][b]));
if (solution[a][b] == 1 && ((((y % 100 >= 10 && y % 100 <= 15) || (y % 100 >= 85 && y % 100 <= 90)) && x % 100 >= 10 && x % 100 <= 90) || (((x % 100 >= 10 && x % 100 <= 15) || (x % 100 >= 85 && x % 100 <= 90)) && y % 100 >= 10 && y % 100 <= 90))) {
z = 100;
} else if (solution[a][b] == 0 && ((((y % 100 >= 10 && y % 100 <= 15) || (y % 100 >= 85 && y % 100 <= 90)) && x % 100 >= 10 && x % 100 <= 90) || (((x % 100 >= 10 && x % 100 <= 15) || (x % 100 >= 85 && x % 100 <= 90)) && y % 100 >= 10 && y % 100 <= 90))) {
z = 230;
}
data[i++] = z << 16 | z << 8 | z;
}
}
return data;
}
public BufferedImage toImage2(int[] data, int w, int h) {
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY);
img.setRGB(0, 0, w, h, data, 0, w);
return img;
}
public void toImage3(BufferedImage img) throws IOException {
File f = new File("Nonogram.png");
ImageIO.write(img, "PNG", f);
}
Here, w and h are supposed to be the amount of cells in each column resp. row multiplied by 100 (I want each cell to be represented by a block of 100x100 pixels).
I then also want an extra gray box in cells that are certain, that's what the if and else if are for.
This works beautifully, and creates images like this:
However, I run into two problems:
This function is super slow. After profiling the execution, I see 90% of the execution time goes to this function. After breaking down my function into two bits, as suggested in the comments, I get the following profile:
Instead of writing to a .png file, I would like a JFrame to display my image, but (again since I missed my proper introduction to Java), JFrame's don't seem to be my best friends and I can't seem to work out how to use them.
Is there a possibility to fill entire 100x100 px cells at once? Is there a way to not have to create the entire BufferedImage each time over, but just modify the bits that have changed in another method? Should I use something else than BufferedImage? What elements does my code need, could someone code out an example method, or the needed code snippets?
Okay, so it looks like writing to a file is not actually your biggest problem, it looks like your biggest problem is that you're dumping the pixels individually. Here are some things I might do:
Make a smaller image. 100x100 is a lot. Why not 20x20? You can always zoom in with an image editor.
Skip the int[] step entirely, and write to the BufferedImage directly.
Use bufferedImage.setRGB(startX, startY, w, h, rgbArray, offset, scansize) as you've been doing, but only for the section of the image you're drawing in bulk.
Do everything based on the values of a and b (as opposed to w and h, including and especially the loops, see point 4.
Fill the the boxes completely solidly, then add the inner rectangle by overwriting those lines separately. All these complicated if checks are killing your performance. Do it branchless (no if statements), and it will run much faster.
Put the code that's inside the for loops into a separate method that makes it more clear. Call it something like drawSingleBox.
Remember, more methods with good names make it easier to follow what's going on. writeImageToFile is preferred over toImage3. convertArrayToImage is preferred over toImage2.
Also, you asked about how to put an image as the background of a JFrame; once you have your fully drawn BufferedImage object, you can use the information in JFrame background image to do that part of it.
// load pixels into an image
this.image = new BufferedImage(this.width,
this.height,
BufferedImage.TYPE_INT_RGB);
// get actual image data for easier pixel loading
byte[] iData = new byte[this.size - 54];
for(int i = 0; i < this.size - 54; i++) {
iData[i] = this.data[i+54];
}
// start from bottom row
for(int y = this.height-1; y >= 0; y--) {
for(int x = 0; x < this.width; x++) {
int index = (this.width*y + x) * 3;
int b = iData[index];
int g = iData[index+1];
int r = iData[index+2];
//System.out.format("R: %s\nG: %s\nB: %s\n\n", r, g, b);
// merge rgb values to single int
int rgb = ((r&0x0ff)<<16)|((g&0x0ff)<<8)|(b&0x0ff);
// build image from bottom up
this.image.setRGB(x, this.height-1-y, rgb);
}
}
I'm reading RGB values from a Bitmap. My iData byte array is correct, as I've checked it against a hex editor. However, when I run this loop, my output image is warped (see picture). I've been wracking my brain for hours trying to fix this, why is it happening?
Input image is a canadian flag.
output image:
I wasn't accounting for the zero-byte padding on the width, for which the formula is
WidthWithPadding = ceiling((w*colorDepth)/32)*32.
Images usually have width, depth (bits per pixel), and stride. Usually the stride is not just width*depth, but padded by some amount (often used to align each row to a 16-bit boundary for memory access reasons).
Your code does not appear to be accounting for the stride or any padding, which explains the offset as you go through the image. I'm surprised the colors don't get switched (suggesting the padding is the same size as a pixel), but those undefined values are causing the black stripe through the image.
Rather than multiplying (this.width*y + x) * 3 to get your index, you should use (this.stride*y + x) * 3 with the appropriate value for stride. The BufferedImage class doesn't seem to provide that in any obvious fashion, so you need to calculate or fetch that otherwise.
The general issue that's happening is that your code has conflated stride (distance between rows in bytes) with width (number of bytes in a row); in aligned data formats these are not necessarily the same thing. The general result of this is getting the sort of skew you're seeing, as the second row starts out on the last byte(s) of the first row, the third row starts out on the second-to-last byte(s) of the second row, and so on.
The first thing you need to do is calculate the stride of the data, and use that as the multiplication factor for the row. Here is a simplified loop that also is somewhat more efficient than multiplying on a per-pixel basis:
int stride = (this.width + 3) & ~3; // rounds up to the nearest multiple of 4
for (int y = 0; y < this.height; y++) {
int idx = y*stride;
for (int x = 0; x < this.width; x++) {
int b = iData[idx++];
int g = iData[idx++];
int a = iData[idx++];
// etc.
}
}
Note that the rounding trick above ((w + a - 1) & ~(a - 1)) only works for power-of-two values; a more general form of the rounding trick is:
int stride = (width + stride - 1)/stride*stride;
although it's very uncommon to find alignments that aren't a power of 2 in the first place.
How do I calculate the middle gray level (max(z)+min(z)/2 over the points where the structuring element is 1 and sets the output pixel to that value?
I just know a little about how to get the RGB value each pixel by using image.getRGB(x,y). I have no idea how to get gray level value each pixel of the image and what is z in the formula and all that?
Please help me with this. Thanks in advance.
I'm going to assume that z are the pixels within your structuring element. I'm also going to assume that "structuring element" is in the case of morphology. Here are a few pointers before we start:
You can convert a colour pixel to its graylevel intensity by using the Luminance formula. By consulting the SMPTE Rec. 709 standard, the output graylevel intensity, given the RGB components is: Y = 0.2126*R + 0.7152*G + 0.0722*B.
We're going to assume that the structuring element is odd. This will allow for the symmetric analysis of the structuring element for each pixel in your image where it is placed
I'm going to assume that your image is already loaded in as a BufferedImage.
Your structuring element will be a 2D array of int.
I'm not going to process those pixels where the structuring element traverses out of bounds to make things easy.
As such, the basic algorithm is this:
For each pixel in our image, place the centre of the structuring element at this location
For each pixel location where the structuring element is 1 that coincides with this position, find the max and minimum graylevel intensity
Set the output image pixel at this location to be (max(z) + min(z)) / 2).
Without further ado:
public BufferedImage calculateMiddleGray(BufferedImage img, int[][] mask)
{
// Declare output image
BufferedImage outImg = new BufferedImage(img.getWidth(),
img.getHeight(), BufferedImage.TYPE_INT_RGB);
// For each pixel in our image...
for (int i = mask.length/2; i < img.getWidth() - mask.length/2; i++) {
for (int j = mask[0].length/2; j < img.getHeight() - mask[0].length/2; j++) {
int maxPix = -1;
int minPix = 256;
// For each pixel in the mask...
for (int x = -mask.length/2; x <= mask.length/2; x++) {
for (int y = -mask[0].length/2; y <= mask[0].length/2; y++) {
//Obtain structuring element pixel
int structPix = mask[y+mask.length/2][x+mask[0].length/2];
// If not 1, continue
if (structPix != 1)
continue;
// Get RGB pixel
int rgb = img.getRGB(i+x, j+y);
// Get red, green and blue channels individually
int redPixel = (rgb >> 16) & 0xFF;
int greenPixel = (rgb >> 8) & 0xFF;
int bluePixel = rgb & 0xFF;
// Convert to grayscale
// Performs SMPTE Rec. 709 lum. conversion using integer logic
int lum = (77*red + 150*green + 29*blue) >> 8;
// Find max and min appropriately
if (lum > maxPix)
maxPix = lum;
if (lum < minPix)
minPix = lum;
}
}
// Set output pixel
// Grayscale image has all of its RGB pixels equal
int outPixel = (maxPix + minPix) / 2;
// Cap output - Ensure we don't go out of bounds
if (outPixel > 255)
outPixel = 255;
if (outPixel < 0)
outPixel = 0;
int finalOut = (outPixel << 16) | (outPixel << 8) | outPixel;
outImg.setRGB(i, j, finalOut);
}
}
}
To call this method, create an image img using any standard method, then create a structuring element mask that is a 2D integer array. After, place this method in your class, then invoke the method by:
BufferedImage outImg = calculateMiddleGray(img, mask);
Also (and of course), make sure you import the necessary package for the BufferedImage class, or:
import java.awt.image.BufferedImage;
Note: This is untested code. Hope it works!
I have this working code which reads in a 700x700 RGB24 TIF file and places it into display memory. The line which assigns the pixelARGB value appears to be extremely inefficient, this code takes 3-4 seconds to redraw the screen. Is there a way I can avoid the shifting and oring and just place the byte values into the correct position within the 32 bit word?
In other languages I have done this with "overlayed variables" or "variant records" or such. Cannot find this in Java. Thank you.
for (y=0; y<700; y++) { // for each line
i = 0;
for (x=0; x<700; x++) { // for each dot
red = lineBuf[i++] & 0xFF;
green = lineBuf[i++] & 0xFF;
blue = lineBuf[i++]& 0xFF;
pixelARGB = 0xFF000000 | (red << 16)| (green << 8) | blue;
this_g.setPixel(x + BORDER, y + BORDER, pixelARGB);
}
size=is.read(lineBuf,0,2100);
}
There is at least one way to convert your TIFF image data buffer into a Bitmap more efficiently, and there is an optimization that can possibly be made.
1. Use an int[] array instead of pixel copies:
You still have to calculate each pixel individually, but set them in an int[] array.
It is the setPixel() function that is taking all your time.
Example:
final int w = 700;
final int h = 700;
final int n = w * h;
final int [] buf = new int[n];
for (int y = 0; y < h; y++) {
final int yw = y * w;
for (int x = 0; x < w; x++) {
int i = yw + x;
// Calculate 'pixelARGB' here.
buf[i] = pixelARGB;
}
}
Bitmap result = Bitmap.createBitmap(buf, w, h, Bitmap.Config.ARGB_8888);
2. Resize within your Loop:
This is not very likely, but in case your destination ImageView for the result image is known to be smaller than the source image, which is 700x700 in your question, then you can resize within your for loop for an extremely high performance increase.
What you have to do is loop through your destination image pixels, calculate the pixel x, y values you need from your source image, calculate the pixelARGB value for only those pixels, populate a smaller int[] array, and finally generate a smaller Bitmap. Much. Faster.
You can even enhance the resize quality with a homebrew cubic interpolation of the four nearest source pixels for each destination pixel, but I think you will find this unnecessary for display purposes.
I tried to implement a my color editor in Java. It should be a simple software. The user will input color in hexadecimal RGBs, for example: 0xFF00FF. I know how to calculate Hue, Chroma, Saturation and Lightness, but how to add or substract Hue value to this given color? This is the mystery.
Is there any algorithm or formula to use?
Now I use this method, but I think the result is different from what I got with Adobe Photoshop.
public void addHue(float addHue) {
float c = getChroma();
addHue %= 6;
if (addHue < 2) {
float n = 1 - green;
green = green + addHue * n;
} else if (addHue < 4) {
addHue -= 2;
float n = 1 - blue;
blue = blue + addHue * n;
} else if (addHue < 6) {
addHue -=4;
float n = 1 - red;
red = red + addHue * n;
}
if (green > 1) green = 1;
else if (green < 0) green = 0;
if (red > 1) red = 1;
else if (red < 0) red = 0;
if (blue > 1) blue = 1;
else if (blue < 0) blue = 0;
}
The java.awt.Color class can help with this.
I don't really understand what you try to achieve by adding two hues together.
Hue is an angle giving the "tint" of the color you are representing. If you think in degrees, the hue will be in the [0, 360[ range. Also, values of 45 and 405 will be representing the same hue.
You have to take this into account when you want to manipulate hues. That is, is you want to average two values, for instance 355 and 5, the correct result could be 0 (draw a circle and take the smallest half angle between those two values) or 180 (the largest half angle).
How I did it on Android (which does not have the Color manipulation utilities) is to create my own color class that stores both representations of a color (rgb and hsv).
Then I have methods that allow to set RGB or set HSV. Adding in the same color space is trivial. Then each time these methods are called, I respectively call an updateHSV or updateRGB method that computes the color components from the new values.
class Color {
float[] hsv;
float[] rgb;
public void setRgb(float[] rgb) {
System.arraycopy(rgb, 0, this.rgb, 0, 3);
computeHsvFromRgb();
}
public void setHsv(float[] hsv) {
System.arraycopy(hsv, 0, this.hsv, 0, 3);
computeRgbFromHsv();
}
// ...
}
For color convertion sample code:
http://www.koders.com/java/fid698452C6AA108615D4A611B52D27A9F5819B39F5.aspx?s=stopwatch
http://www.f4.fhtw-berlin.de/~barthel/ImageJ/ColorInspector//HTMLHelp/farbraumJava.htm