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.
Related
My target is to generate image with A5 format and place some text on it. At the beggining i was trying to accomplish that using System.Drawing.Imaging but seems like Xamarin is not able to work with this. Then I found out SkiaSharp library which seems like would be valid to do this job but i am a bit lost here. How can i correctly generate an image, put some text on it and send via Stream. Note that if there is more text that couldn't fit on single A5 next image should be created.
This is what i got so far:
private void CreateBitmapFromText(List<string> texts)
{
using var surface = SKSurface.Create(width: 640, height: 480, SKColorType.Gray8, SKAlphaType.Premul);
SKCanvas myCanvas = surface.Canvas;
// clear the canvas / fill with white
myCanvas.DrawColor(SKColors.White);
// set up drawing tools
using (var paint = new SKPaint())
{
paint.TextSize = 64.0f;
paint.IsAntialias = true;
paint.Color = new SKColor(0x42, 0x81, 0xA4);
paint.IsStroke = false;
// draw the text
foreach (var text in texts)
{
myCanvas.DrawText(text, ??, ??, paint);
}
}
}
EDIT: with help of #Jason i did this:
var aaa = ToStream(CreateBitmapFromText(new List<string>() {"asas", "vvvv"}), SKEncodedImageFormat.Png);
_printService.PrintImage(aaa);
public Stream ToStream(SKImage image, SKEncodedImageFormat format)
{
SKData encoded = image.Encode(format, 90);
return encoded.AsStream();
}
public SKImage CreateBitmapFromText(List<string> texts)
{
using var surface = SKSurface.Create(width: 640, height: 480, SKColorType.Gray8, SKAlphaType.Premul);
SKCanvas myCanvas = surface.Canvas;
// clear the canvas / fill with white
myCanvas.DrawColor(SKColors.White);
// set up drawing tools
using (var paint = new SKPaint())
{
paint.TextSize = 64.0f;
paint.IsAntialias = true;
paint.Color = new SKColor(0x42, 0x81, 0xA4);
paint.IsStroke = false;
// draw the text
foreach (var text in texts)
{
myCanvas.DrawText(text, 0.0f, 0.0f, paint);
}
}
return surface.Snapshot();
}
When i send it to printer i see this (white paper with some little black chars ?? at the top?)
I was able to easily modify the Xamarin FramedText sample to save a bitmap by adding this to the end of OnCanvasViewPaintSurface
// get the SKImage from SKSurface
var image = surface.Snapshot();
// Encode as PNG, returns SKData
var data = image.Encode(SKEncodedImageFormat.Png, 100);
string path = Path.GetTempFileName();
// write byte[] to file, verify data with image viewer
File.WriteAllBytes(path, data.ToArray());
you should be able to easily modify this to return a byte[] or stream
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 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
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 use this code to create thumbnails
System.Drawing.Image.GetThumbnailImageAbort abort = new System.Drawing.Image.GetThumbnailImageAbort(this.ThumbnailCallback);
System.Drawing.Image image2 = image.GetThumbnailImage((int)Math.Round((double)wid / difference), (int)Math.Round((double)hei / difference), abort, IntPtr.Zero);
image2.Save(str2, System.Drawing.Imaging.ImageFormat.Jpeg);
image2.Dispose();
but i get this very low quality image
but it is suposed to be like this one
what i am making wrong
or how can achieve this
Your problem is not really with the GetThumbnailImage() method, but instead in how you are saving the file. You need to specify the quality level of the JPEG you are saving, or it seems it always defaults to a very low value.
Consider this code as a guide (it's from an old .NET 2.0 project; the code still works fine compiled against 4.0, but there may be a more direct method in 4.0; I've never had reason to check)
ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();
ImageCodecInfo jpegEncoder = null;
for (int x = 0; x < encoders.Length; x++) {
if (string.Compare(encoders[x].MimeType, "image/jpeg", true) == 0) {
jpegEncoder = encoders[x];
break;
}
}
if (jpegEncoder == null) throw new ApplicationException("Could not find JPEG encoder!");
EncoderParameters prms = new EncoderParameters(1);
prms.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 80L);
bitmap.Save(fileName, jpegEncoder, prms);
Here is another solution that should always work without fetching out the encoder. It resizes keeping relation between width & heigh ... modify for your needs.
/// <summary>
/// Resize an image with high quality
/// </summary>
public static Image ResizeImage(Image srcImage, int width)
{
var b = new Bitmap(width, srcImage.Height * width / srcImage.Width);
using (var g = Graphics.FromImage((Image)b))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(srcImage, 0, 0, b.Width, b.Height);
}
return b;
}