So Im still learning Java. Now Im learning JavaFX.
I have a picture of a tree. And I want to try two different method. the first method I use was using unary operator to turn the image colour to grey.
Now I want to try a second method using the ColourTransformer interface that I made to get a 10 pixel wide gray frame replacing the pixels on the border of an image.
This is what I have done. for the second method, im not quite sure how to specify the pixel. Any suggestions?
this is what I have done
public class ColourFilter extends Application {
//Using Unary Operator to transform image to grayscale - Method 1
public static Image transform(Image in, UnaryOperator<Color> f) {
int width = (int) in.getWidth();
int height = (int) in.getHeight();
WritableImage out = new WritableImage(
width, height);
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++)
out.getPixelWriter().setColor(x, y,
f.apply(in.getPixelReader().getColor(x, y)));
return out;
}
public static <T> UnaryOperator<T> compose(UnaryOperator<T> op1, UnaryOperator<T> op2) {
return t -> op2.apply(op1.apply(t));
}
//Using ColourTransformer interface to get 10 pixel wide gray frame replacing the pixels on the border of an image - Method 2
public static Image transform(Image in, ColourTransformer f) {
int width = (int) in.getWidth();
int height = (int) in.getHeight();
WritableImage out = new WritableImage(
width, height);
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++)
out.getPixelWriter().setColor(x, y, f.apply(x, y, in.getPixelReader().getColor(x, y)));
return out;
}
#FunctionalInterface
interface ColourTransformer {
Color apply(int x, int y, Color colorAtXY);
}
public void start(Stage stage) {
Image image = new Image("amazing-trees.jpg");
Image image2 = transform(image, Color::brighter);
Image image3 = transform(image2, Color::grayscale);
// alternative to two previous image transforms -- composition
//Image image3 = transform(image, compose(Color::brighter, Color::grayscale));
stage.setScene(new Scene(new VBox(
new ImageView(image),
// new ImageView(image2),
new ImageView(image3))));
stage.show();
}
}
As the compile error message says, you are trying to call
f.apply(Color);
where f is a ColourTransformer: however you defined the apply method in ColorTransformer with three parameters: apply(int, int, Color).
You need to replace
f.apply(in.getPixelReader().getColor(x, y))
with
f.apply(x, y, in.getPixelReader().getColor(x, y))
i.e.
public static Image transform(Image in, ColourTransformer f) {
int width = (int) in.getWidth();
int height = (int) in.getHeight();
WritableImage out = new WritableImage(
width, height);
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++)
out.getPixelWriter().setColor(x, y, f.apply(x, y, in.getPixelReader().getColor(x, y)));
return out;
}
You can get the 10-pixel gray border by doing:
Image image = new Image("amazing-trees.jpg");
int width = (int) image.getWidth();
int height = (int) image.getHeight();
Image framedImage = transform(image, (x, y, color) -> {
if (x < 10 || y < 10 || width - x < 10 || height - y < 10) {
return Color.GRAY ;
} else return color ;
});
Related
I want to fill a credit card image with some text(name, surname,...) but i found problems to correctly find the starting position of the text.
I have marked my image with special pixels, I want to draw text at the start of this specific pixels but when I run the text is not aligned with them, I supposed that the specific pixel position that I calculated in the code is calculated starting from the first canvas pixel rather than the first bitmap pixel. How can I solve it? Thank you.
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Bitmap bitmap = ((BitmapDrawable)this.getDrawable()).getBitmap();
final int w = bitmap.getWidth();
final int h = bitmap.getHeight();
int pixel;
int redValue;
int blueValue;
int greenValue;
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
pixel = bitmap.getPixel(x, y);
redValue = Color.red(pixel);
blueValue = Color.blue(pixel);
greenValue = Color.green(pixel);
if (redValue == 255 && greenValue == 254 && blueValue == 0){
Paint paint = new Paint();
paint.setColor(Color.BLACK);
paint.setTextSize(35);
canvas.drawText("Some Text", x, y, paint);
}
}
}
}
I'm new to Java and ImageJ and I'm trying to write a PluginFilter with ImageJ and Java. I want it to crop the right and left side of an image (500 pixel each), turn those parts 90 degrees to the outside and load them in a new image.
I already tried couple of options and now I got totally confused. I don't think there is any mistake in the algorith itself, but in the handling of ImageProcessor etc..
import ij.*;
import ij.process.*;
import ij.gui.*;
import java.awt.*;
import ij.plugin.filter.*;
public class Cropping28_Plugin implements PlugInFilter {
ImagePlus imp;
public int setup(String arg, ImagePlus imp){
imp = IJ.getImage();
this.imp = imp;
return DOES_ALL;
}
public void run(ImageProcessor ip){
// int[] p = null;
// int[] q = null;
int height = imp.getHeight();
int width = imp.getWidth();
// New Image
ImagePlus new_image = IJ.createImage("Nr1", "RGB", height, 500, 1);
// turning the picture for y=0:500 (left side of picture)
for(int x = 0; x < height; x++){
for(int y = 0; y < 500; y++){
int k = 0;
// loading pixel
int q = imp.getProcessor().getPixel(x, y);
int [] convert = {q, q, q};
// pixel in newimage
int a = height - k;
int b = x;
new_image.getProcessor().putPixel(a, b, convert);
k++;
}
}
// turning the picture for y=get.Length:get.Length-500 (right side of picture)
for(int x = 0; x < height; x++){
for(int y = width -500; y < width; y++){
int k = 0;
// loading pixel
int q = imp.getProcessor().getPixel(x, y);
int [] convert = {q, q, q};
// pixel in new image
int a = height - k;
int b = x;
new_image.getProcessor().putPixel(a, b, convert);
k++;
}
}
// draw new picture
new_image.show();
}
}
I expect the output to be a picture containing the pixel of the right and left edge of the original image. The actual output is a write image of the size I expect.
It would be awesome if someone had an idea!
EDIT: I've changed some of my code to reflect what AlamasB said. I still don't understand how I should be populating my byte array and what conversions I need to do.
I'm trying to manipulate the pixels of an image uploaded by the user and write the new image onto a canvas. Right now I'm just trying to copy the original image without changing the RGB of the pixels. I've been stuck in the same spot for quite a while now and can't figure out where to go next. In the first section of the code I make a copy of the original image uploaded by the user.
Image image = imageView.getImage();
PixelReader pixelReader = image.getPixelReader();
PixelFormat format = pixelReader.getPixelFormat();
int width= (int)image.getWidth();
int height = (int) image.getHeight();
GraphicsContext gc = canvas.getGraphicsContext2D();
PixelWriter pw = gc.getPixelWriter();
byte[] imageData = new byte[width * height * 4];
imageData = createImageData(imageData, pixelReader, width, height);
//..
//...the next function populates my byte array with what's in the image
public byte[] createImageData(byte[] imageData, PixelReader pr, int width, int height){
int i = 0;
for(int y=0; y<height; y++){
for(int x = 0; x < width; x++){
int argb = pixelReader.getArgb(x, y);
imageData[i] = (byte) argb;
imageData[i+1] = (byte) argb;
imageData[i+2] = (byte) argb;
i+=3;
pw.setArgb(x, y, argb);
}
}
return imageData;
}
EDIT: No longer using this function. The next function is really confusing me. I'm referring to this http://docs.oracle.com/javafx/2/image_ops/jfxpub-image_ops.htm as a reference but I can't figure out what's going on.
//...
//... the next function sets the pixels of the canvas to what's in the byte array
public void drawImageData(byte[] imageData, PixelWriter pw, int width, int height){
boolean on = true;
PixelFormat<ByteBuffer> pixelFormat = PixelFormat.getByteRgbInstance();
for(int y = 50; y < 150; y+=height){
for(int x = 50; x < 150; x+=width){
if(on){
pw.setPixels(x, y, width, height, pixelFormat, imageData, 0, width*3);
}
on = !on;
}
on=!on;
}
}
Considering that PixelReader provides getARGB your best bet in this case is probably to use 32-bit representation. Subsequently converting it to a 4-byte array, i.e. width * height * 4. Finally use setARGB to draw the image.
int argb = pixelReader.getArgb(x, y);
// populate imageData[i,i+1,i+2,i+3] by converting argb
...
// convert imageData[i,i+1,i+2,i+3] into a 32bit argb, say int argb2
pixelWriter.setArgb(x, y, argb2);
Alternatively, you can use Color's getOpacity() to get the A from RGBA. Again, using the 32-bit representation.
At the very least this will provide you with a minimal working example, which you can then extend.
Here's a quick version (self-contained, just copy and paste):
import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.*;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import java.nio.ByteBuffer;
import java.util.Arrays;
public class ImageManipulationApp extends Application {
private static final int APP_W = 800;
private static final int APP_H = 600;
private Parent createContent() {
Image image = makeMockImage();
byte[] imageData = imageToData(image);
byte[] modifiedImageData = modify(imageData);
Image modifiedImage = dataToImage(modifiedImageData);
HBox root = new HBox(25);
root.getChildren().addAll(new ImageView(image), new ImageView(modifiedImage));
return root;
}
private Image makeMockImage() {
WritableImage image = new WritableImage(APP_W / 2, APP_H);
PixelWriter writer = image.getPixelWriter();
for (int y = 0; y < APP_H; y++) {
for (int x = 0; x < APP_W / 2; x++) {
writer.setColor(x, y, Math.random() < 0.00005 ? Color.YELLOW : Color.BLACK);
}
}
return image;
}
/**
* Modifies the pixel data.
*
* #param data original image data
* #return modified image data
*/
private byte[] modify(byte[] data) {
// this is where changes happen
return data;
}
private byte[] imageToData(Image image) {
int width = (int) image.getWidth();
int height = (int) image.getHeight();
byte[] data = new byte[width * height * 4];
int i = 0;
for (int y = 0; y < height; y++){
for (int x = 0; x < width; x++){
int argb = image.getPixelReader().getArgb(x, y);
byte[] pixelData = ByteBuffer.allocate(4).putInt(argb).array();
data[i++] = pixelData[0];
data[i++] = pixelData[1];
data[i++] = pixelData[2];
data[i++] = pixelData[3];
}
}
return data;
}
private Image dataToImage(byte[] data) {
// if we don't know the image size beforehand we can encode width and height
// into image data too
WritableImage image = new WritableImage(APP_W / 2, APP_H);
PixelWriter writer = image.getPixelWriter();
int i = 0;
for (int y = 0; y < APP_H; y++) {
for (int x = 0; x < APP_W / 2; x++) {
int argb = ByteBuffer.wrap(Arrays.copyOfRange(data, i, i + 4)).getInt();
writer.setArgb(x, y, argb);
i += 4;
}
}
return image;
}
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I'm constructing a flood fill algorithm that will hopefully, eventually, find a face in the center of a photograph based on the color at the exact center, and similar colors surrounding that. At the moment, however, my algorithm should take in any color within the bounds of the int array and transfer it over to a holder array, essentially making a copy of the original image. But this isn't working, and is resulting in a black image when I run it. Can anyone see the problem I'm missing?
public class TemplateMaker {
public static void main(String[] args) throws IOException {
importPhoto();
}
public static void importPhoto() throws IOException {
File imgPath = new File("/Pictures/BaseImage.JPG");
BufferedImage bufferedImage = ImageIO.read(imgPath);
establishArray(bufferedImage);
}
public static void establishArray(BufferedImage bufferedImage) throws IOException {
//byte[] pixels = hugeImage.getData();
int width = bufferedImage.getWidth();
System.out.println(width);
int height = bufferedImage.getHeight();
System.out.println(height);
int[][] result = new int[height][width];
for (int i = 0; i < height; i++)
for (int j = 0; j < width; j++) {
result[i][j] = bufferedImage.getRGB(j, i);
}
findFace(result);
}
public static void findFace(int[][] image) throws IOException {
int height = image.length;
int width = image[0].length;
Color centerStart = new Color(image[height / 2][width / 2], true);
System.out.println(centerStart.getRGB());
System.out.println(Color.blue.getRGB());
int[][] filled = new int[height][width];
floodFill(height / 2, width / 2, centerStart, image, filled, height, width);
//construct the filled array as image.
BufferedImage bufferImage2 = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < height; x++) {
for (int y = 0; y < width; y++) {
bufferImage2.setRGB(y, x, filled[x][y]);
}
}
//save filled array as image file
File outputfile = new File("/Pictures/saved.jpg");
ImageIO.write(bufferImage2, "jpg", outputfile);
}
public static int[][] floodFill(int x, int y, Color targetColor, int[][] image, int[][] filled, int height, int width) {
//execute something similar once algorithm works.
// if (image[x][y] < targetColor.getRGB()/2 || image[x][y] > targetColor.getRGB()*2) return filled;
if (image[x][y] == Color.blue.getRGB()) {
return filled;
}
if (image.length < 0 || image[0].length < 0 || image.length >= height || image[0].length >= width) {
return filled;
}
filled[x][y] = image[x][y];
image[x][y] = Color.blue.getRGB();
floodFill(x - 1, y, targetColor, image, filled, height, width);
floodFill(x + 1, y, targetColor, image, filled, height, width);
floodFill(x, y - 1, targetColor, image, filled, height, width);
floodFill(x, y + 1, targetColor, image, filled, height, width);
return filled;
}
}
You create int[][] called filled and then call floodFill(...) which returns without doing anything to the array. image.length is always equal to height and image[0].length is always equal to width, so it always returns from the second if statement.
You then build a BufferedImage from that blank array and write it to a file. All of the values in the array are initialized to 0, which gives you black.
Changing the for loop in findFace(..) to the below will save out the original image from your holder array.
for (int x = 0; x < height; x++) {
for (int y = 0; y < width; y++) {
bufferImage2.setRGB(y, x, image[x][y]);
}
}
But I'm not sure if this is what you're asking or not.
Edit: Try this out and see if it sends you in the right direction:
public static int[][] floodFill(int x, int y, Color targetColor, int[][] image, int[][] filled, int height, int width) {
//execute something similar once algorithm works.
// if (image[x][y] < targetColor.getRGB()/2 || image[x][y] > targetColor.getRGB()*2) return filled;
if (image[x][y] == Color.blue.getRGB()) {
System.out.println("returned if 1");
return filled;
}
/*if (image.length < 0 || image[0].length < 0 || image.length >= height || image[0].length >= width) {
return filled;
}*/
filled[x][y] = image[x][y];
image[x][y] = Color.blue.getRGB();
if (x - 1 <= 0 && y < width) {
floodFill(x - 1, y, targetColor, image, filled, height, width);
}
if(x + 1 < height && y >= 0 && y < width) {
floodFill(x + 1, y, targetColor, image, filled, height, width);
}
if(x >= 0 && x < height && y - 1 <= 0) {
floodFill(x, y - 1, targetColor, image, filled, height, width);
}
if(x >= 0 && x < height && y + 1 < width) {
floodFill(x, y + 1, targetColor, image, filled, height, width);
}
return filled;
}
I have a screenshot method in my code and a BufferedImage instance. I'm wondering if it's possible to search the image data for a specific RGB, then return the X,Y coordinates for the pixel.
What could I use for that? Is it possible at all?
public int[] searchForColor(BufferedImage bi, int searchColor)
{
for (int x = 0; x < bi.getWidth(); ++x)
for (int y = 0; y < bi.getHeight(); ++y)
{
if ((bi.getRGB(x, y) & 0x00FFFFFF) == searchColor)
return new int[]{x, y};
}
}
Usage:
BufferedImage bi = takeScreenShot();
int searchColor = 0x2D5E83; // A random color
int[] coordinate = searchForColor(bi, searchColor);
int x = coordinate[0];
int y = coordinate[1];
http://www.roseindia.net/java/java-get-example/get-color-of-pixel.shtml and loop thru the image data