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);