I have an 12-bit grayscale image. I want to display it in Java with BufferedImage, and I find BufferedImage.TYPE_USHORT_GRAY is the most suitable. However, it makes my display image almost black (my pixels are in 0 ~ 4095 range).
How can I autoscale it to clearly display it?
Thanks so much.
If your data is stored as 16 bit samples (as indicated in the comments), then you can either:
Multiply the samples (by 16) to the full 16 bit range, and create a BufferedImage.TYPE_USHORT_GRAY image. Straight forward, but requires massaging and copying the data.
Or, you could also create a custom BufferedImage with a 12 bit ColorModel, and just use the data as is. Probably faster, but more verbose/complicated code.
Here's the code to create an image using a 12 bit gray color model:
WritableRaster raster = Raster.createInterleavedRaster(new DataBufferUShort(twelveBitData, twelveBitData.length), w, h, w, 1, new int[]{0}, null);
ColorSpace gray = ColorSpace.getInstance(ColorSpace.CS_GRAY);
ColorModel twelveBitModel = new ComponentColorModel(gray, new int[]{12}, false, false, Transparency.OPAQUE, DataBuffer.TYPE_USHORT);
BufferedImage image = new BufferedImage(twelveBitModel, raster, twelveBitModel.isAlphaPremultiplied(), null);
Full, runnable demo program below.
Alternatively, if your data had been stored as "one and a half byte" packed 12 bit samples, the simplest solution would be to first pad the data to full 16 bit samples.
Demo program:
public class TwelveBit {
public static void main(String[] args) {
int w = 320;
int h = 200;
short[] twelveBitData = new short[w * h];
createRandomData(twelveBitData);
WritableRaster raster = Raster.createInterleavedRaster(new DataBufferUShort(twelveBitData, twelveBitData.length), w, h, w, 1, new int[]{0}, null);
ColorSpace gray = ColorSpace.getInstance(ColorSpace.CS_GRAY);
ColorModel twelveBitModel = new ComponentColorModel(gray, new int[]{12}, false, false, Transparency.OPAQUE, DataBuffer.TYPE_USHORT);
BufferedImage image = new BufferedImage(twelveBitModel, raster, twelveBitModel.isAlphaPremultiplied(), null);
showIt(image);
}
private static void createRandomData(short[] twelveBitData) {
Random random = new Random();
for (int i = 0; i < twelveBitData.length; i++) {
twelveBitData[i] = (short) random.nextInt(1 << 12);
}
}
private static void showIt(final BufferedImage image) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("test");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(new JLabel(new ImageIcon(image)));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
You need to scale from 12 bit to 16bit. Multiply your values by 16
Related
I need to do that in my program. I have to do it in two ways:
1.) by my own, with the following code:`
private Image convertToGrayScale(Image image) {
WritableImage result = new WritableImage((int) image.getWidth(), (int)
image.getHeight());
PixelReader preader = image.getPixelReader();
PixelWriter pwriter = result.getPixelWriter();
for (int i = 0; i < result.getWidth(); i++) {
for (int j = 0; j < result.getHeight(); j++) {
Color c = preader.getColor(i, j);
double red = (c.getRed() * 0.299);
double green = (c.getGreen() * 0.587);
double blue = (c.getBlue() * 0.114);
double sum = c.getRed() + c.getBlue() + c.getGreen();
pwriter.setColor(i , j, new Color(sum, sum, sum, 1.0));
}
}
return result;
}
2.) with the help of the openCV library, with the following code (it was copied almost perfectly from their site) :
public WritableImage loadAndConvert() throws Exception {
//Loading the OpenCV core library
System.loadLibrary( Core.NATIVE_LIBRARY_NAME );
String input = "C:/Users/Dan Ivgy/eclipse-workspace/LuckyBride/sample/20180402_170204.jpg";
//Reading the image
Mat src = Imgcodecs.imread(input);
//Creating the empty destination matrix
Mat dst = new Mat();
//Converting the image to gray scale and saving it in the dst matrix
Imgproc.cvtColor(src, dst, Imgproc.COLOR_RGB2GRAY);
// Imgcodecs.imwrite("C:/opencv/GeeksforGeeks.jpg", dst);
//Extracting data from the transformed image (dst)
byte[] data1 = new byte[dst.rows() * dst.cols() * (int)(dst.elemSize())];
dst.get(0, 0, data1);
//Creating Buffered image using the data
BufferedImage bufImage = new BufferedImage(dst.cols(),dst.rows(),
BufferedImage.TYPE_BYTE_GRAY);
//Setting the data elements to the image
bufImage.getRaster().setDataElements(0, 0, dst.cols(), dst.rows(), data1);
//Creating a WritableImage
WritableImage writableImage = SwingFXUtils.toFXImage(bufImage, null);
System.out.println("Converted to Grayscale");
return writableImage;
}
In both cases, the problem was that i have'nt got a "greyscale" output, just somthing different (and before you
asked: yeas, i have tried to to do it on several pictures, not only one)
Here's the input picture and the output picture:
Well, as you can see, this is Not a greyscale! maybe a sunset-scale..
I'd really appraciate any help, Thank you :)
(espcially if there's something faster out there since those solutions rather takes a while to run)
If someone knows, why there is not some built in option in javaFX as there is a lot of sophisticated imageview effects and this one is so simple and so prevalent.
UPDATE:
i found a website that do somthing similar to ehat i did - and somehow i got a different output! i don't get it
here's the website.
and here's the output from my computer:
my output
UPDATE#2: as #matt correctly asked, here's the code that use this method:
ImageIO.write(SwingFXUtils
.fromFXImage
(convertToGrayScale(new Image(getClass().getResource("1_CNc4RxV85YgthtvZh2xO5Q.jpeg").toExternalForm()) ), null), "jpg", file);
the original target was to show the image to rhe user, and the problem was there, so i changed the code to this one which save the image so i could isolate the problem more easly..
Ok guys, after some research, and endless number of attememts i got what i need
to solve the problem ill show my solution here and come to a coclude of my insights about this issue..
first, ill give the the disticntion between "SAVING the file" and "Setting the file in the ImageView.
when we want just to show it in image view, i have'ne expereinced any problem using almost every solution suggested here. the most simple, short and sweet (in my opinion) is the following one:
private Image convertToGrayScale(Image image) {
WritableImage result = new WritableImage((int) image.getWidth(), (int) image.getHeight());
PixelReader preader = image.getPixelReader();
PixelWriter pwriter = result.getPixelWriter();
for (int i = 0; i < result.getWidth(); i++)
for (int j = 0; j < result.getHeight(); j++)
pwriter.setColor(i , j, preader.getColor(i, j).grayscale());
return result;
}
(for convinience, i ignored exception handling)
it works fine when i use it along when i use it along with the following code:
Image img1 = convertToGrayScale(new Image(filepath);
imageView.setImage(img1);
about SAVING this output image, after some research and using #trashgold 's references, and this important one :
i got my solution as the following:
private void saveBadImage(BufferedImage originalImage, File dest) throws IOException
{
// use the following line if you want the first parameter to be a filepath to src image instead of Image itself
//BufferedImage originalImage = ImageIO.read(file);
// jpg needs BufferedImage.TYPE_INT_RGB
// png needs BufferedImage.TYPE_INT_ARGB
// create a blank, RGB, same width and height
BufferedImage newBufferedImage = new BufferedImage(
originalImage.getWidth(),
originalImage.getHeight(),
BufferedImage.TYPE_INT_RGB);
// draw a white background and puts the originalImage on it.
newBufferedImage.createGraphics().drawImage(originalImage,0,0,null);
// save an image
ImageIO.write(newBufferedImage, "jpg", dest);
}
and, i use it with the following code:
Image img1 = convertToGrayScale(new Image(filepath));
BufferedImage img2 = new BufferedImage((int) img1.getWidth(), (int) img1.getHeight(), BufferedImage.TYPE_INT_ARGB);
saveBadImage(img2, file);
and, it works perfectly!
Thank you all guys, and i hope my insights will help to some people
I decided to work in awt, then create a javafx image.
public class App extends Application {
#Override
public void start(Stage stage) {
WritableImage gray = null;
try {
BufferedImage awtImage = ImageIO.read(new URL("https://i.stack.imgur.com/ysIrl.jpg"));
gray = new WritableImage(awtImage.getWidth(), awtImage.getHeight());
BufferedImage img2 = new BufferedImage(awtImage.getWidth(), awtImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
for(int i = 0; i<awtImage.getWidth(); i++){
for(int j = 0; j<awtImage.getHeight(); j++){
int c = awtImage.getRGB(i, j);
int r = (c>>16)&255;
int g = (c>>8)&255;
int b = (c)&255;
int s = (int)(r*0.299 + g*0.587 + b*0.114);
int gr = (255<<24) + (s<<16) + (s<<8) + s;
img2.setRGB(i, j, gr);
}
}
gray = SwingFXUtils.toFXImage(img2, gray);
} catch (IOException e) {
e.printStackTrace();
}
ImageView view = new ImageView(gray);
ScrollPane pane = new ScrollPane(view);
Scene scene = new Scene(new StackPane(pane), 640, 480);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
Both methods work for me, so I'm not sure if this helps you. I can save the result too.
System.out.println("saving " +
ImageIO.write(img2, "PNG", new File("tested.png")) );
If you're using oracles jdk, I think you can save JPEG. I am using OpenJDK which doesn't seem to be able to write JPEG.
Now I would like to generate QR Code dynamically from the UUID from the device. I am wonder how to do it in order to support multi-platform in gluon? Please also recommended me If I simplfy using normall java library or special lib which developed by gluon Team.
You can use the Zxing library to generate the QR on your device. This is the same library that is used by the Charm Down BarcodeScan service on Android.
First of all, add this dependency to your build:
compile 'com.google.zxing:core:3.3.3'
Now you can combine the Device service to retrieve the UUID with the QR generator.
Once you have the QR in zxing format, you will need to either generate an image or a file.
Given that you can't use Swing on Android/iOS, you have to avoid MatrixToImageWriter, and do it manually, based on the generated pixels.
Something like this:
public Image generateQR(int width, int height) {
String uuid = Services.get(DeviceService.class)
.map(DeviceService::getUuid)
.orElse("123456789"); // <--- for testing on desktop
QRCodeWriter qrCodeWriter = new QRCodeWriter();
try {
BitMatrix bitMatrix = qrCodeWriter.encode(uuid, BarcodeFormat.QR_CODE, width, height);
WritablePixelFormat<IntBuffer> wf = PixelFormat.getIntArgbInstance();
WritableImage writableImage = new WritableImage(width, height);
PixelWriter pixelWriter = writableImage.getPixelWriter();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
pixelWriter.setColor(x, y, bitMatrix.get(x, y) ?
Color.BLACK : Color.WHITE);
}
}
return writableImage;
} catch (WriterException e) {
e.printStackTrace();
}
return null;
}
Now you can call this method from your view, adding an ImageView to render the generated image:
ImageView imageView = new ImageView();
imageView.setFitWidth(256);
imageView.setFitHeight(256);
imageView.setImage(service.generateQR(256, 256));
EDIT
If you want to generate either a QR code or a barcode, you can replace the above code in generateQR with this:
MultiFormatWriter codeWriter = new MultiFormatWriter();
BitMatrix bitMatrix = codeWriter.encode(uuid, format, width, height);
...
and set an argument with the format to:
For QR code: BarcodeFormat.QR_CODE, and use square size like 256x 256
For Barcode: BarcodeFormat.CODE_128, and use rectangular size like 256 x 64
I am new to programming, I need to generate 13 random number using loop (while) number + 1 when the number reaches 13, I will like to store that number in text box.
Any help will be welcomed
What you see below is a 'static' class, which needs no initialization. So, with this in your project, you can just call RandomInts.GetRandomInts() and pass it on into your model for the input box to present to your user(s).
public static class RandomInts
{
private static int _x = 0;
public static int GetRandomInts()
{
var i = 0;
var rnd = new Random();
while (i < 13)
{
_x = rnd.Next();
i++;
}
return _x;
}
}
Here is with upper and lower bounds:
var x = random.[Next](minr, maxr)
I ask IN NUTITEQ, I know the way to perform this with GoogleApi with computeArea() but I have not found anything in Nutiteq sdk/snapshot.
Thanks in advance.
p.s. I know many methods to compute the area but I want to call something own of Nutiteq
EDIT:
There are no built-in Method, thanks for the fast answer Jaak, so I researched and found 2 Methods, both of them under WSG84 Projection. I developed a little programm to compare both of them and then I compared to a KML Tool, which computes area of a Polygon.
how-can-i-measure-area-from-geographic-coordinates
A-link-to-a-Geographic-Framework
And the results with a kml tool of University of New Hampshire
12197.38184
import java.lang.Math.*;
import net.sf.geographiclib.*;
public class ComputeAreaTest {
private static double[][] moorwiese_coords = {{12.925634f,48.427168f},
{12.926825f,48.427217f},
{12.926788f,48.428385f},
{12.926069f,48.428374f},
{12.925431f,48.42825f},
{12.925624f,48.427192f},
{12.925634f,48.427168f}};
protected static double computeArea() {
double area=0.0;
double earthRadius = 6378137.0f;
int size = moorwiese_coords.length;
if (size > 2) {
double[] p1 = new double[2];
double[] p2 = new double[2];
for (int i=0; i<size-1; i++) {
p1 = moorwiese_coords[i];
p2 = moorwiese_coords[i+1];
area += Math.toRadians(p2[0] - p1[0]) * (2 +
Math.sin(Math.toRadians(p1[1]) ) + Math.sin(Math.toRadians(p2[1])) );
}
area = area * earthRadius * earthRadius / 2.0;
}
return area;
}
protected static double computeAreaWithGeographicLib() {
int size = moorwiese_coords.length;
PolygonArea p = new PolygonArea(Geodesic.WGS84, false);
try {
for (int i=0;i<size;i++) {
p.AddPoint(moorwiese_coords[i][4], moorwiese_coords[i][0]);
}
}
catch (Exception e) {}
PolygonResult r = p.Compute();
return r.area;
}
public static void main(String[] args) {
double areaGeoLib = computeAreaWithGeographicLib();
double area = computeArea();
System.out.println("Area: " + (-1)*area + "\nArea(GeoLib): "+areaGeoLib);
}
}
Output
Area: 12245.282587113787
Area(GeoLib): 12254.95547034964
I found not very suitable for accurate use ( Yes, under 0.5% Error may be unaccurate for many environments) but useful to learn how to compute the area of a irregular Polygon.
Thx, Jaak, I had not seen this freen Symbol to click "Right Answer", now I have seen, I must explicitly. So, an "answer" is in initial post.
I need to do two things to an image: resize it, then crop it.
I'm resizing like this:
nonResizedImage = new Bitmap(imagePath);
Bitmap scaled = new Bitmap(preCropWidth, preCropHeight);
using (Graphics scaledGraphics = Graphics.FromImage(scaled))
{ // scale image to the sizeo f the image the user cropped on
scaledGraphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
scaledGraphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
scaledGraphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
scaledGraphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
scaledGraphics.Clear(ColorTranslator.FromHtml("#FFFFFF"));
scaledGraphics.DrawImage(nonResizedImage, 0, 0, preCropWidth, preCropHeight);
}
Now I need to crop the image. I've found a function that does this:
static byte[] Crop(string Img, int Width, int Height, int X, int Y)
{
try
{
using (SD.Image OriginalImage = SD.Image.FromFile(Img))
{
using (SD.Bitmap bmp = new SD.Bitmap(Width, Height))
{
bmp.SetResolution(OriginalImage.HorizontalResolution, OriginalImage.VerticalResolution);
using (SD.Graphics Graphic = SD.Graphics.FromImage(bmp))
{
Graphic.SmoothingMode = SmoothingMode.AntiAlias;
Graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
Graphic.PixelOffsetMode = PixelOffsetMode.HighQuality;
Graphic.DrawImage(OriginalImage, new SD.Rectangle(0, 0, Width, Height), X, Y, Width, Height, SD.GraphicsUnit.Pixel);
MemoryStream ms = new MemoryStream();
bmp.Save(ms, OriginalImage.RawFormat);
return ms.GetBuffer();
}
}
}
}
catch (Exception Ex)
{
throw Ex;
}
}
But this requires a image as input. So, I could save the output of my resize code to the disk, then read it back in again to do the crop, but this seems needlessly inefficient. I don't really know much about image manipulation in c# though.
How do I crop the scaledGraphics I have, without first saving it to the disk?
One of the overloads for new Bitmap is Width, Height, Graphics Object. You should be able to just pass the graphics object in and then create the bitmap from that. Something like this
static byte[] Crop(Graphics g, int Width, int Height, int X, int Y)
{
try
{
using (Bitmap bmp = new Bitmap(Width, Height, g))
{
...
}
}
......
}