Reshaping an IplImage in javacv - java

I am trying to resize an IplImage image in javacv. I found the cvResize function that does such a thing. MY workflow is: to open the image to transform it to gray_scale to resize it to desirable size and finally to reshape it. I have already read it transform it to gray_scale and resize it. What is my problem the final step, reshaping. I have the cvReshape which does what I want. I ve got an IplImage, I have to transform it to cvMat. The next step is reshaping, which takes 4 arguments, a cvArr a cvMat and the desirable dimensions.
// read an image
final IplImage image = cvLoadImage("ef.jpg");
//create image window named "My Image"
final CanvasFrame canvas = new CanvasFrame("My Image");
// request closing of the application when the image window is closed
canvas.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
// show image on window
canvas.showImage(image);
IplImage GrayImage = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, 1);
cvCvtColor(image, GrayImage, CV_BGR2GRAY);
IplImage img = IplImage.create(60, 60, IPL_DEPTH_8U, 1);
//resize the image
cvResize(GrayImage, img, CV_INTER_LINEAR);
cvSaveImage("4-rjb" + ".jpg", img);
//cvReleaseImage(result1);
System.out.println("The size of the image: "+img);
CvMat mtx = CvMat.createHeader(img.height(), img.width());
cvGetMat(img, mtx, null, 0);
System.out.println(mtx);
cvReshape(img, mtx, 3600, 1);
I am receiving the error:
OpenCV Error: Bad number of channels () in unknown function, file .\src\array.cpp,
line 2721
I ve just want to reshape a 2d image, why that bad number of channels error happened??

With some Googling I found that your output matrix must contain at least 3 channels. (Blue, Green and Red). Where Blue and Green will be completely empty and you put your grayscale image as Red channel of the output image. Any other number of channels will cause the error you are getting.

Related

Java OpenCV convert HSV back to BGR after inRange

I'd like to create a black&white image relying on HSV filtering. However, after converting the image from BGR to HSV and applying the inRange() method, the matrix is reduced to a single channel matrix (with values either 0 or 255) and cannot be converted back to BGR.
Is there an easy way to work around this? Do I even need that step of back-conversion or can I somehow display the new image with the information I have? I'm pretty new to OpenCV and already found a very similar question but I'm still kinda confused on what to do.
Example:
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
Mat img = Imgcodecs.imread(path);
Mat hsv = new Mat();
Mat img_new = new Mat();
Imgproc.cvtColor(img,hsv,Imgproc.COLOR_BGR2HSV);
Core.inRange(hsv, new Scalar(hue,saturation,value),new Scalar(hue,saturation,value),hsv);
Imgproc.cvtColor(hsv, img_new, Imgproc.COLOR_HSV2BGR); // This line doesn't work.
// display new image in JFrame
MatOfByte mob = new MatOfByte();
Imgcodecs.imencode(".tif", img_new, mob);
byte ba[] = mob.toArray();
BufferedImage bi = ImageIO.read(new ByteArrayInputStream(ba));
newImgLabel.setIcon(new ImageIcon (bi));
Thank you in advance!
inRange function gives you a mask(actually, a single channel image with values 0 and 255), you can use it to select which areas you want to select.
Mat mask = new Mat();
Imgproc.cvtColor(img,hsv,Imgproc.COLOR_BGR2HSV);
Core.inRange(hsv, new Scalar(hue,saturation,value),new Scalar(hue,saturation,value),mask);
img.copyTo(img_new, mask);

How to extract a subimage using PixelReader and JavaFX?

I have a .png image, and I want to extract one part of that image using the PixelReader class, and rebuild it as an image :
Image image = new Image("file:ressources/spritesheets/Zelda_Overworld.png");
byte[] buffer = new byte[1024];
PixelReader pr = image.getPixelReader();
pr.getPixels(0, 0, 16, 16, PixelFormat.getByteBgraInstance(), buffer, 0, 64);
Image tile = new Image(new ByteArrayInputStream(buffer));
I can display image and buffer seems to contain values, but I can't display tile, tile.getPixelReader() returns null, tile.getWidth() and tile.getHeight() return 0.0.
Do you know what I am doing wrong?
Paul
Let WritableImage do this for you:
Image image = new Image("file:ressources/spritesheets/Zelda_Overworld.png");
Image tile = new WritableImage(image.getPixelReader(), x, y, width, height);
Depending on the use of tile doing this may not be necessary at all. ImageView has a viewport property that allows you to choose the part of the image to display and GraphicsContext provides an overloaded version of the drawImage method to draw a part of the image to the Canvas.

How to make a TIFF transparent in Java using JAI?

I'm trying to write a TIFF from a BufferedImage using Java Advanced Imaging (JAI), and am unsure of how to make it transparent. The following method works for making PNGs and GIFs transparent:
private static BufferedImage makeTransparent(BufferedImage image, int x, int y) {
ColorModel cm = image.getColorModel();
if (!(cm instanceof IndexColorModel)) {
return image;
}
IndexColorModel icm = (IndexColorModel) cm;
WritableRaster raster = image.getRaster();
int pixel = raster.getSample(x, y, 0);
// pixel is offset in ICM's palette
int size = icm.getMapSize();
byte[] reds = new byte[size];
byte[] greens = new byte[size];
byte[] blues = new byte[size];
icm.getReds(reds);
icm.getGreens(greens);
icm.getBlues(blues);
IndexColorModel icm2 = new IndexColorModel(8, size, reds, greens, blues, pixel);
return new BufferedImage(icm2, raster, image.isAlphaPremultiplied(), null);
}
But when writing a TIFF, the background is always white. Below is my code used for writing TIFF:
BufferedImage destination = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_BYTE_INDEXED);
Graphics imageGraphics = destination.getGraphics();
imageGraphics.drawImage(sourceImage, 0, 0, backgroundColor, null);
if (isTransparent) {
destination = makeTransparent(destination, 0, 0);
}
destination.createGraphics().drawImage(sourceImage, 0, 0, null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageOutputStream ios = ImageIO.createImageOutputStream(baos);
TIFFImageWriter writer = new TIFFImageWriter(new TIFFImageWriterSpi());
writer.setOutput(ios);
writer.write(destination);
I also do some metadata manipulation later as I'm actually dealing with GeoTIFF. But still the images are white at this point. While debugging, I can view the BufferedImage and it is transparent, but when i write the image, the file has a white background. Do I need to do something specific with TiffImageWriteParam? Thanks for any help you can provide.
TIFF has no option of storing transparency info (alpha channel) in the palette (as in your IndexedColorModel). The palette only supports RGB triplets. Thus, the fact that you set a color index to transparent is lost when you write the image to a TIFF.
If you need a transparent TIFF, your options are:
Use normal RGBA instead of indexed color (RGB, 4 samples/pixel, unassociated alpha). Just use BufferedImage.TYPE_INT_ARGB or TYPE_4BYTE_ABGR, probably. This will make the output file bigger, but is easy to implement, and should be more compatible. Supported by almost all TIFF software.
Save a separate transparency mask (a 1 bit image with photometric interpretation set to 4) with the palette image. Not sure if it supported by much software, some may display the mask as a separate b/w image. Not sure how this can be achieved in JAI/ImageIO, might require writing a sequence and setting some extra metadata.
Store a custom field containing the transparent index. Will not be supported by anything but your own software, but the file will still be compatible and displayed with the white (solid) background in other software. You should be able to set this using the TIFF metadata.

Converting BufferedImage to Mat (OpenCV) in Java [duplicate]

This question already has answers here:
Converting `BufferedImage` to `Mat` in OpenCV
(9 answers)
Closed 6 years ago.
I've tried this link and have the code below. My program imports images in a BufferedImage format and then displays it to the users. I'm using the matchingTemplate function in OpenCV which requires me to convert it to a Mat format.
The code works if I import the image -> convert it to Mat then save the image using imwrite. The program also allows the user to crop an image and then use the Template matching to compare it to another image. The issue come when I tried to convert the cropped image to Mat I need to convert it from Int to Byte using this code:
im = new BufferedImage(im.getWidth(), im.getHeight(),BufferedImage.TYPE_3BYTE_BGR);
This however results in a black image. But if I get rid of it it only works with imported images and not cropped. What is going on here? I'm certain it is something to do with the coversion process as I have tested the template matching function using read in images.
// Convert image to Mat
public Mat matify(BufferedImage im) {
// Convert INT to BYTE
//im = new BufferedImage(im.getWidth(), im.getHeight(),BufferedImage.TYPE_3BYTE_BGR);
// Convert bufferedimage to byte array
byte[] pixels = ((DataBufferByte) im.getRaster().getDataBuffer())
.getData();
// Create a Matrix the same size of image
Mat image = new Mat(im.getHeight(), im.getWidth(), CvType.CV_8UC3);
// Fill Matrix with image values
image.put(0, 0, pixels);
return image;
}
You can try this method, to actually convert the image to TYPE_3BYTE_BGR (your code simply created a blank image of the same size, that is why it was all black).
Usage:
// Convert any type of image to 3BYTE_BGR
im = toBufferedImageOfType(im, BufferedImage.TYPE_3BYTE_BGR);
// Access pixels as in original code
And the conversion method:
public static BufferedImage toBufferedImageOfType(BufferedImage original, int type) {
if (original == null) {
throw new IllegalArgumentException("original == null");
}
// Don't convert if it already has correct type
if (original.getType() == type) {
return original;
}
// Create a buffered image
BufferedImage image = new BufferedImage(original.getWidth(), original.getHeight(), type);
// Draw the image onto the new buffer
Graphics2D g = image.createGraphics();
try {
g.setComposite(AlphaComposite.Src);
g.drawImage(original, 0, 0, null);
}
finally {
g.dispose();
}
return image;
}

How to control the pixel size of the color model of an ErrorDiffusionDescriptor?

I am trying to convert a direct color model image to a bitonal indexed image (1 bit per pixel) and save the indexed image as a BMP.
As stated on the Java Advanced Imaging API Home Page:
The bit depth of the encoded output is determined by that of the source image.
From looking through the source code of BMPImageWriter, the mechanism of this is the return value of ColorModel#getPixelSize().
Using a scaled-down copy of an image from Wikimedia Commons, I first perform color quantization to get a color lookup table and then error diffusion to apply Floyd–Steinberg dithering:
PlanarImage surrogateImage = PlanarImage.wrapRenderedImage(image);
PlanarImage op = ColorQuantizerDescriptor.create(surrogateImage, ColorQuantizerDescriptor.OCTTREE, 2, null, null, null, null, null);
LookupTableJAI lut = (LookupTableJAI)op.getProperty("LUT");
IndexColorModel cm = new IndexColorModel(1, lut.getByteData()[0].length, lut.getByteData()[0], lut.getByteData()[1], lut.getByteData()[2]);
op = ErrorDiffusionDescriptor.create(surrogateImage, lut, KernelJAI.ERROR_FILTER_FLOYD_STEINBERG, null);
image = op.getAsBufferedImage();
The problem is, image.getColorModel().getPixelSize() returns 8, so the image is saved as an 8bpp bitmap:
The size of this image is 167 KiB.
I saw somewhere that one way of passing a color model to error diffusion is to set a JAI.KEY_IMAGE_LAYOUT rendering hint:
ImageLayout layout = new ImageLayout();
layout.setTileWidth(image.getWidth());
layout.setTileHeight(image.getHeight());
layout.setColorModel(cm);
layout.setSampleModel(op.getSampleModel());
RenderingHints rh = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout);
op = ErrorDiffusionDescriptor.create(surrogateImage, lut, KernelJAI.ERROR_FILTER_FLOYD_STEINBERG, rh);
image.getColorModel().getPixelSize() now returns 1, but the resulting image is altered significantly:
However, the size of this image is 21 KiB, about what it is when I use MS Paint to convert the sample image to a monochrome bitmap. So, it looks like JAI's BMPImageWriter is using the correct encoding, but if you look closely at the second image, adjacent columns of pixels are eight pixels apart. In fact, you can kind of see the first image, only each column of pixels from the first image is expanded to 8 columns of pixels.
Is this a bug in JAI? Is there something that I can do to collapse these 8-wide columns of pixels to single-column pixels?
This should work with a 24 BPP png:
String filename = "jEEDL.png";
PlanarImage image = PlanarImage.wrapRenderedImage(JAI.create("fileload", filename));
LookupTableJAI lut = new LookupTableJAI(new byte[][] {{(byte)0x00, (byte)0xff}, {(byte)0x00, (byte)0xff}, {(byte)0x00, (byte)0xff}});
ImageLayout layout = new ImageLayout();
byte[] map = new byte[] {(byte)0x00, (byte)0xff};
ColorModel cm = new IndexColorModel(1, 2, map, map, map);
layout.setColorModel(cm);
SampleModel sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
image.getWidth(),
image.getHeight(),
1);
layout.setSampleModel(sm);
RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout);
PlanarImage op = ErrorDiffusionDescriptor.create(image, lut, KernelJAI.ERROR_FILTER_FLOYD_STEINBERG, hints);
BufferedImage dst = op.getAsBufferedImage();
JAI.create("filestore", dst, "jEEDL.bmp", "BMP");

Categories

Resources