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;
}
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
Current project:
ASP.NET 4.5.2
MVC 5
I am trying to leverage the TinyPNG API, and if I just pipe the image over to it, it works great. However, since the majority of users will be on a mobile device, and these produce images at a far higher resolution than what is needed, I am hoping to reduce the resolution of these files prior to them being piped over to TinyPNG. It is my hope that these resized images will be considerably smaller than the originals, allowing me to conduct a faster round trip.
My code:
public static async Task<byte[]> TinyPng(Stream input, int aspect) {
using(Stream output = new MemoryStream())
using(var png = new TinyPngClient("kxR5d49mYik37CISWkJlC6YQjFMcUZI0")) {
ResizeImage(input, output, aspect, aspect); // Problem area
var result = await png.Compress(output);
using(var reader = new BinaryReader(await (await png.Download(result)).GetImageStreamData())) {
return reader.ReadBytes(result.Output.Size);
}
}
}
public static void ResizeImage(Stream input, Stream output, int newWidth, int maxHeight) {
using(var srcImage = Image.FromStream(input)) {
var newHeight = srcImage.Height * newWidth / srcImage.Width;
if(newHeight > maxHeight) {
newWidth = srcImage.Width * maxHeight / srcImage.Height;
newHeight = maxHeight;
}
using(var newImage = new Bitmap(newWidth, newHeight))
using(var gr = Graphics.FromImage(newImage)) {
gr.SmoothingMode = SmoothingMode.AntiAlias;
gr.InterpolationMode = InterpolationMode.HighQualityBicubic;
gr.PixelOffsetMode = PixelOffsetMode.HighQuality;
gr.DrawImage(srcImage, new Rectangle(0, 0, newWidth, newHeight));
newImage.Save(output, ImageFormat.Jpeg);
}
}
}
So the ResizeArea is supposed to accept a stream and output a stream, meaning that the TinyPNG .Compress() should work just as well with the output as it would with the original input. Unfortunately, only the .Compress(input) works -- with .Compress(output) TinyPNG throws back an error:
400 - Bad Request. InputMissing, File is empty
I know TinyPNG has its own resizing routines, but I want to do this before the image is sent out over the wire to TinyPNG so that file size (and therefore transmission time) is reduced as much as possible prior to the actual TinyPNG compression.
…Aaaaand I just solved my problem by using another tool entirely.
I found ImageProcessor. Documentation is a royal b**ch to get at because it only comes in a Windows *.chm help file (it’s not online… cue one epic Whisky. Tango. Foxtrot.), but after looking at a few examples it did solve my issue quite nicely:
public static async Task<byte[]> TinyPng(Stream input, int aspect) {
using(var output = new MemoryStream())
using(var png = new TinyPngClient("kxR5d49mYik37CISWkJlC6YQjFMcUZI0")) {
using(var imageFactory = new ImageFactory()) {
imageFactory.Load(input).Resize(new Size(aspect, 0)).Save(output);
}
var result = await png.Compress(output);
using(var reader = new BinaryReader(await (await png.Download(result)).GetImageStreamData())) {
return reader.ReadBytes(result.Output.Size);
}
}
}
and everything is working fine now. Uploads are much faster now as I am not piping a full-sized image straight through to TinyPNG, and since I am storing both final-“full”-sized images as well as thumbnails straight into the database, I am now not piping the whole bloody image twice.
Posted so that other wheel-reinventing chuckleheads like me will actually have something to go on.
i want to create SQL CLR integrated function from Visual C#, now my requirement is user will pass a folder path as a paramter, and the function should get all the image file from the the folder, and get its basic property like FileSize, dimension etc.. but it seems SQL project does not supports System.Drawing Namespace... as i created the same function in normal project it worked fine, as i was able to use System.Drawing Namespace, but here i cannot use, System.Drawing Namespace.. so is there any other way to get the image dimension...
below is the code i have used in my normal project.
public DataTable InsertFile(string FolderPath)
{
DataTable dt = new DataTable();
DataColumn[] col = new DataColumn[] { new DataColumn("FileName", typeof(System.String)), new DataColumn("FileSize", typeof(System.Int32)), new DataColumn("FilePath", typeof(System.String)), new DataColumn("Width", typeof(System.Int32)), new DataColumn("Height", typeof(System.Int32)) };
dt.Columns.AddRange(col);
FileInfo info= null;
Bitmap bmp = null;
foreach (String s in Directory.GetFiles(FolderPath, "*.jpg"))
{
info = new FileInfo(s);
bmp = new Bitmap(s);
DataRow dr = dt.NewRow();
dr["FileName"] = Path.GetFileName(s);
dr["FileSize"] = info.Length / 1024;
dr["FilePath"] = s;
dr["Width"] = bmp.Width;
dr["Height"] = bmp.Height;
dt.Rows.Add(dr);
}
return dt;
}
does anyone have any idea how to get image dimension without using System.Drawing Namespace.
wow never seen anyone try this before, but if using Drawing in a SQL project isn't allowed try reading the header info like this http://www.codeproject.com/KB/cs/ReadingImageHeaders.aspx
Edit included the code, with the change to remove the dependency on Size.
while (binaryReader.ReadByte() == 0xff)
{
byte marker = binaryReader.ReadByte();
ushort chunkLength = binaryReader.ReadLittleEndianInt16();
if (marker == 0xc0)
{
binaryReader.ReadByte();
int height = binaryReader.ReadLittleEndianInt16();
int width = binaryReader.ReadLittleEndianInt16();
return new int[] { width, height };
}
binaryReader.ReadBytes(chunkLength - 2);
}
Is the Image object any better for you?
System.Drawing.Image forSize = System.Drawing.Image.FromFile(s);
dr["Width"] = forSize.Width;
and so forth.
That any better or same problem?
I'm trying to make a simple picture thumbnail application. I've searched the web and have found fairly complicated thumbnail apps, to simple ones. I have a good one working, If I could get the ImageButton Resolution looking good. Currently, everything works fine, but the resoultion of the buttons is horrible (i've tried various width/height variations).
I simply iterate through an array of button Image's and set their properties. I call ThumbnailSize() to set the width/height of the Imagebutton.
The code is kind of sloppy as of right now, but thats besides the point. I want to know if there is a way to keep or increase the ImageButton resolution while taking a picture (800x600+/-) and shrinking it into a Imagebutton.
string[] files = null;
files = Directory.GetFiles(Server.MapPath("Pictures"), "*.jpg");
ImageButton[] arrIbs = new ImageButton[files.Length];
for (int i = 0; i < files.Length; i++)
{
arrIbs[i] = new ImageButton();
arrIbs[i].ID = "imgbtn" + Convert.ToString(i);
arrIbs[i].ImageUrl = "~/Gallery/Pictures/pic" + i.ToString() + ".jpg";
ThumbNailSize(ref arrIbs[i]);
//arrIbs[i].BorderStyle = BorderStyle.Inset;
arrIbs[i].AlternateText = System.IO.Path.GetFileName(Convert.ToString(files[i]));
arrIbs[i].PostBackUrl = "default.aspx?img=" + "pic" + i.ToString();
pnlThumbs.Controls.Add(arrIbs[i]);
}
}
public ImageButton ThumbNailSize(ref ImageButton imgBtn)
{
//Create Image with ImageButton path, and determine image size
System.Drawing.Image img =
System.Drawing.Image.FromFile(Server.MapPath(imgBtn.ImageUrl));
if (img.Height > img.Width)
{
//Direction is Verticle
imgBtn.Height = 140;
imgBtn.Width = 90;
return imgBtn;
}
else
{
//Direction is Horizontal
imgBtn.Height = 110;
imgBtn.Width = 130;
return imgBtn;
}
}
This function will proportionally resize a Size structure. Just provide it the maximum height/width and it will return a size that fits within that rectangle.
/// <summary>
/// Proportionally resizes a Size structure.
/// </summary>
/// <param name="sz"></param>
/// <param name="maxWidth"></param>
/// <param name="maxHeight"></param>
/// <returns></returns>
public static Size Resize(Size sz, int maxWidth, int maxHeight)
{
int height = sz.Height;
int width = sz.Width;
double actualRatio = (double)width / (double)height;
double maxRatio = (double)maxWidth / (double)maxHeight;
double resizeRatio;
if (actualRatio > maxRatio)
// width is the determinate side.
resizeRatio = (double)maxWidth / (double)width;
else
// height is the determinate side.
resizeRatio = (double)maxHeight / (double)height;
width = (int)(width * resizeRatio);
height = (int)(height * resizeRatio);
return new Size(width, height);
}
You need to scale the image according to its original size. Just setting the size will definately cause scaling issues.
Have a look at this link
C#: Resize An Image While Maintaining Aspect Ratio and Maximum Height