I am trying to display some pictures from the sd storage in GridView in a Xamarin.Android app.
The only documentation I've found is this: http://docs.xamarin.com/recipes/android/data/files/selecting_a_gallery_image
But it only works for one image, opening two or more pictures gives me an Java.Lang.OutOfMemoryError
on this row:
imageView.SetImageURI (bitmapList [position]);
Found some Android answers here on SO:
https://stackoverflow.com/a/823966/511299
Translated to C#:
public static Bitmap DecodeFile (String s)
{
try
{
s = s.Replace ("file://", "");
//Decode image size
BitmapFactory.Options o = new BitmapFactory.Options ();
o.InJustDecodeBounds = true;
BitmapFactory.DecodeStream (new System.IO.FileStream (s, System.IO.FileMode.Open), null, o);
//The new size we want to scale to
int REQUIRED_SIZE = 70;
//Find the correct scale value. It should be the power of 2.
int scale = 1;
while (o.OutWidth/scale/2>=REQUIRED_SIZE && o.OutHeight/scale/2>=REQUIRED_SIZE)
{
scale *= 2;
}
//Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options ();
o2.InSampleSize = scale;
return BitmapFactory.DecodeStream (new System.IO.FileStream (s, System.IO.FileMode.Open), null, o2);
}
catch (FileNotFoundException e)
{
}
return null;
}
This gives me an
System.IO.IOException: Sharing violation on path storage/sdcard0/Pictures/MyAppPhotos/44cdbcf0-a488-40b8-98a9-79f3dc8d9deb.jpg
on the line of
return BitmapFactory.DecodeStream (new System.IO.FileStream (s, System.IO.FileMode.Open), null, o2);
Is it possible I access the file twice?
Or is it perhaps because I use FileStream instead of FileInputStream. Trying to use BitmapFactory.DecodeStream requires a System.IO.Stream as parameter, opposed to the example requiring a string :(
More detailed, here is the Android version of decodeStream:
public static Bitmap decodeStream (InputStream is, Rect outPadding, BitmapFactory.Options opts)
while this is all I can find for Xamarin.Android:
public static Bitmap DecodeStream (System.IO.Stream is, Rect outPadding, BitmapFactory.Options opts)
Solved it. Used BitmapFactory.DecodeFile(String path, BitmapFactory.Options opts) instead!
Related
I need to extract images from PDF.
I know that some images are rotated 90 degrees (I checked with online tools).
I'm using this code:
PdfRenderListener:
public class PdfRenderListener : IExtRenderListener
{
// other methods ...
public void RenderImage(ImageRenderInfo renderInfo)
{
try
{
var mtx = renderInfo.GetImageCTM();
var image = renderInfo.GetImage();
var fillColor = renderInfo.GetCurrentFillColor();
var color = Color.FromArgb(fillColor?.RGB ?? Color.Empty.ToArgb());
var fileType = image.GetFileType();
var extension = "." + fileType;
var bytes = image.GetImageAsBytes();
var height = mtx[Matrix.I22];
var width = mtx[Matrix.I11];
// rotated image
if (height == 0 && width == 0)
{
var h = Math.Abs(mtx[Matrix.I12]);
var w = Math.Abs(mtx[Matrix.I21]);
}
// save image
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
When I save images with this code the rotated images are saved with distortion.
I have read this post iText 7 ImageRenderInfo Matrix contains negative height on Even number Pages and mkl answer.
In current transfromation matrix (mtx) I have these values:
0
841.9
0
-595.1
0
0
595.1
0
1
I know image rotated 90 degrees. How can I transform an image to get a normal image?
As #mkl mentioned, the true reason was not in the rotation of the image, but with the applied filter.
I analyzed the pdf file with iText RUPS and found that the image was encoded with a CCITTFaxDecode filter:
RUPS screen
Next, I looked for ways to decode this filter and found these questions
Extracting image from PDF with /CCITTFaxDecode filter.
How to use Bit Miracle LibTiff.Net to write the image to a MemoryStream
I used the BitMiracle.LibTiff.NET library
I wrote this method:
private byte[] DecodeInternal(byte[] rawBytes, int width, int height, int k, int bitsPerComponent)
{
var compression = GetCompression(k);
using var ms = new MemoryStream();
var tms = new TiffStream();
using var tiff = Tiff.ClientOpen("in-memory", "w", ms, tms);
tiff.SetField(TiffTag.IMAGEWIDTH, width);
tiff.SetField(TiffTag.IMAGELENGTH, height);
tiff.SetField(TiffTag.COMPRESSION, compression);
tiff.SetField(TiffTag.BITSPERSAMPLE, bitsPerComponent);
tiff.SetField(TiffTag.SAMPLESPERPIXEL, 1);
var writeResult = tiff.WriteRawStrip(0, rawBytes, rawBytes.Length);
if (writeResult == -1)
{
Console.WriteLine("Decoding error");
}
tiff.CheckpointDirectory();
var decodedBytes = ms.ToArray();
tiff.Close();
return decodedBytes;
}
private Compression GetCompression(int k)
{
return k switch
{
< 0 => Compression.CCITTFAX4,
0 => Compression.CCITTFAX3,
_ => throw new NotImplementedException("K > 0"),
};
}
After decoding and rotating the image, I was able to save a normal image. Thanks everyone for the help.
You can try this. I'm using Itext 7 for java. Here you still need to write your own listener:
public class MyImageRenderListener implements IEventListener {
protected String path;
protected String extension;
public MyImageRenderListener (String path) {
this.path = path;
}
public void eventOccurred(IEventData data, EventType type) {
switch (type) {
case RENDER_IMAGE:
try {
String filename;
FileOutputStream os;
ImageRenderInfo renderInfo = (ImageRenderInfo) data;
PdfImageXObject image = renderInfo.getImage();
if (image == null) {
return;
}
byte[] imageByte = image.getImageBytes(true);
extension = image.identifyImageFileExtension();
filename = String.format(path, image.getPdfObject().getIndirectReference().getObjNumber(), extension);
os = new FileOutputStream(filename);
os.write(imageByte);
os.flush();
os.close();
} catch (com.itextpdf.io.exceptions.IOException | IOException e) {
System.out.println(e.getMessage());
}
break;
default:
break;
}
}
public Set<EventType> getSupportedEvents() {
return null;
}
}
I checked for a pdf with a random rotation angle, and 90 degrees, the resulting picture was obtained without distortion
public void manipulatePdf() throws IOException, SQLException, ParserConfigurationException, SAXException {
PdfDocument pdfDoc = new PdfDocument(new PdfReader("path to pdf"), new PdfWriter(new ByteArrayOutputStream()));
MyImageRenderListener listener = new MyImageRenderListener("path to resulting image");
PdfCanvasProcessor parser = new PdfCanvasProcessor(listener);
for (int i = 1; i <= pdfDoc.getNumberOfPages(); i++) {
parser.processPageContent(pdfDoc.getPage(i));
}
pdfDoc.close();
}
I'm new in Unity and I'm trying to save an image for get it after. I try Application.CaptureScreenshot and the method with Texture2D.ReadPixel, i try to save in persistentDataPath (/data/user/0/my.package.name/files/), in /sdcard/Download/ and in /storage/emulated/0/Download. No method worked. In every manifest of my project I have WRITE_EXTERNAL_STORAGE permission. If I save to persistent data, I don't found it I found only cache of UnityAds, and if I save to Download folder I get access denied.
Anyone can help me?
Here's my code :
IEnumerator ScreenShot(){
yield return new WaitForEndOfFrame ();
Application.CaptureScreenshot ("ball.png");
Application.CaptureScreenshot ("/sdcard/Download/ball.png");
int width = Screen.width;
int height = Screen.height;
Texture2D tex = new Texture2D (width, height, TextureFormat.RGB24, false);
tex.ReadPixels (new Rect (0, 0, width, height), 0, 0);
tex.Apply ();
byte[] bytes = tex.EncodeToJPG ();
File.WriteAllBytes ("sdcard/Download/ball.png", bytes);
File.WriteAllBytes (Application.persistentDataPath + "/ball.png", bytes);
}
Before i check if folders exist :
if(!System.IO.Directory.Exists("/sdcard/Download/"))
{
System.IO.Directory.CreateDirectory ("/sdcard/Download/");
}
if(!System.IO.Directory.Exists(Application.persistentDataPath))
{
System.IO.Directory.CreateDirectory (Application.persistentDataPath);
}
And I start IEnumerator with:
StartCoroutine(ScreenShot());
What I'm doing wrong?
Here is what works for me:
string destination = DateTime.Now.ToString ("yyyy-MM-dd-HHmmss") + ".png";
string fullDestination = Path.Combine (Application.persistentDataPath, destination);
Application.CaptureScreenshot (destination);
SharingWriter.WriteTextInstant ("Preparing...");
FileInfo fileInfo = new FileInfo(fullDestination);
while(fileInfo == null || fileInfo.Exists == false)
{
yield return null;
fileInfo = new FileInfo(fullDestination);
}
Application.CaptureScreenshot handles everything, no need to worry about reading pixels and writing to files that's redundant. Just have a while loop afterwards in the Coroutine to wait for the magic to happen! I used this to create screenshot sharing functionality for mobile.
I want to read a dicom image using simpleitk, convert it into a bitmap and then display the result in a pictureBox. But when I'm trying to do this, an ArgumentException is thrown. How can I solve this?
Here is my code:
OpenFileDialog dialog = new OpenFileDialog();
dialog.Title = "Open";
dialog.Filter = "DICOM Files (*.dcm;*.dic)|*.dcm;*.dic|All Files (*.*)|*.*";
dialog.ShowDialog();
if (dialog.FileName != "")
{
using (sitk.ImageFileReader reader = new sitk.ImageFileReader())
{
reader.SetFileName(dialog.FileName);
reader.SetOutputPixelType(sitk.PixelIDValueEnum.sitkFloat32);
sitk.Image image = reader.Execute();
var castedImage = sitk.SimpleITK.Cast(image,
sitk.PixelIDValueEnum.sitkFloat32);
var size = castedImage.GetSize();
int length = size.Aggregate(1, (current, i) => current * (int)i);
IntPtr buffer = castedImage.GetBufferAsFloat();
// Declare an array to hold the bytes of the bitmap.
byte[] rgbValues = new byte[length];
// Copy the RGB values into the array.
Marshal.Copy(buffer, rgbValues, 0, length);
Stream stream = new MemoryStream(rgbValues);
Bitmap newBitmap = new Bitmap(stream);
//I have tried in this way, but it generated ArgumentException too
//Bitmap newBitmap = new Bitmap((int)image.GetWidth(), (int)image.GetHeight(), (int)image.GetDepth(), PixelFormat.Format8bppIndexed, buffer);
Obraz.pic.Image = newBitmap;
}
}
Thank you for your comments and attempts to help. After consultations and my own searching on the internet I solved this issue. The first problem was the inadequate representation of pixel image. I had to change Float32 to UInt8 to provide an eight-bit for pixel.
var castedImage = sitk.SimpleITK.Cast(image2, sitk.PixelIDValueEnum.sitkUInt8);
Then I would already create a Bitmap using the constructor that was was commented out in question, but with (int)image.GetWidth() instead of (int)image.GetDepth().
Bitmap newBitmap = new Bitmap((int)image.GetWidth(), (int)image.GetHeight(), (int)image.GetWidth(), PixelFormat.Format8bppIndexed, buffer);
Unfortunately, a new problem appeared. The image, that was supposed to be in gray scale, was displayed in strange colors. But I found the solution here
ColorPalette pal = newBitmap.Palette;
for (int i = 0; i <= 255; i++)
{
// create greyscale color table
pal.Entries[i] = Color.FromArgb(i, i, i);
}
newBitmap.Palette = pal; // you need to re-set this property to force the new ColorPalette
I want to get the coordinates of a PowerPoint shape.
here is my c# implementation
Microsoft.Office.Interop.PowerPoint._Application myPPT = Globals.ThisAddIn.Application;
Microsoft.Office.Interop.PowerPoint.Slide curSlide = myPPT.ActiveWindow.View.Slide;
foreach (Shape current in curSlide.Shapes)
{
Debug.Print(current.Type.ToString());
foreach (ShapeNode n in current.Nodes)
{
double x = n.Points[1, 1];
double y = n.Points[1, 2];
Debug.Print("X: " + x);
Debug.Print("Y: " + y);
}
}
This works fine for a "freehand"-shape, but not for the pre-installed ones (like rectangle, big arrow, star, ...) there I get error COMException.
Has anyone an idea how to implement that and access the points of a shape ?
I tried numerous ways and always ended up with the HRESULT: 0x800A01A8 error.
A workaround was to export shape as an image and use AForge to determine the edges of this image. Not pretty but did the work. I haven't find-tuned the code below to handle diffs in transformation and offsets.
static List<Tuple<float,float>> ShapeToPoints(Microsoft.Office.Interop.PowerPoint.Shape shape)
{
string path = #"C:\Temp\bpmimg\Test1.png";
if (File.Exists(path))
File.Delete(path);
List<Tuple<float, float>> points = new List<Tuple<float, float>>();
try
{
//Shape.Export is an internal method, but we access it via reflection this way
var args = new object[] { path, Microsoft.Office.Interop.PowerPoint.PpShapeFormat.ppShapeFormatPNG, 0, 0, Microsoft.Office.Interop.PowerPoint.PpExportMode.ppRelativeToSlide };
shape.GetType().InvokeMember("Export", System.Reflection.BindingFlags.InvokeMethod, null, shape, args); // Export to file on disk
// locating objects
BlobCounter blobCounter = new BlobCounter();
blobCounter.FilterBlobs = true;
blobCounter.MinHeight = 5;
blobCounter.MinWidth = 5;
//Important that the dispose is called so that the temp-file cleanup File.Delete doesn't crash due to locked file
using (System.Drawing.Bitmap image = (System.Drawing.Bitmap)System.Drawing.Bitmap.FromFile(path))
{
blobCounter.ProcessImage(image);
}
Blob[] blobs = blobCounter.GetObjectsInformation();
SimpleShapeChecker shapeChecker = new SimpleShapeChecker();
if (blobs.Length != 1)
{
//Unexpected number of blobs
return null;
}
var blob = blobs[0];
List<IntPoint> edgePoints = blobCounter.GetBlobsEdgePoints(blob);
List<IntPoint> untransformedPoints;
// use the shape checker to extract the corner points
if (!shapeChecker.IsConvexPolygon(edgePoints, out untransformedPoints))
{
IConvexHullAlgorithm hullFinder = new GrahamConvexHull();
untransformedPoints = hullFinder.FindHull(edgePoints);
}
var untransformedWidth = untransformedPoints.Max(p => p.X);
var untransformedHeight = untransformedPoints.Max(p => p.Y);
var widthRatio = untransformedWidth / shape.Width;//How many time bigger is the image-based figure than the shape figure
var heightRatio = untransformedHeight / shape.Height;//How many time bigger is the image-based figure than the shape figure
points = untransformedPoints.Select(p => new Tuple<float, float>(shape.Left + p.X / widthRatio, shape.Top + p.Y / heightRatio)).ToList();//Transform
}
finally
{
if (File.Exists(path))
File.Delete(path);
}
return points;
}
I have written some code to create dynamic banners. It returns a bitmap variable. Is there some way that I can use this variable as the ImageUrl for an <asp:Image /> ?
Here is the code that creates the image:
public class SideImage
{
protected const int ImgCt = 4;
protected const int ImgW = 130;
protected const int ImgH = 150;
public Bitmap GenerateImage()
{
string serializedImage = CreateImage("side");
if(!string.IsNullOrEmpty(serializedImage))
{
using(MemoryStream ms = new MemoryStream(Convert.FromBase64String(serializedImage)))
{
Bitmap bitmap = new Bitmap(ms);
return bitmap;
}
}
return null;
}
protected string CreateImage(string path)
{
try
{
using (Bitmap canvas = new Bitmap(ImgW, (ImgCt * ImgH)))
{
using (Graphics canvasGraphic = Graphics.FromImage(canvas))
{
List<FileInfo> fileList = new List<FileInfo>();
DirectoryInfo directoryInfo = new DirectoryInfo(HttpContext.Current.Server.MapPath(path + "/"));
fileList.AddRange(directoryInfo.GetFiles("*.jpg"));
Randomizer<FileInfo> randomizer = new Randomizer<FileInfo>();
fileList.Sort(randomizer);
int yOffset = 0;
for (int i = 0; i <= fileList.Count - 1; i++)
{
using (Image image = Image.FromFile(fileList[i].FullName))
{
Rectangle rectangle = new Rectangle(0, yOffset, ImgW, ImgH);
canvasGraphic.DrawImage(image, rectangle);
}
yOffset += ImgH;
}
ImageCodecInfo[] imageCodecInfo = ImageCodecInfo.GetImageEncoders();
using (EncoderParameters encoderParameters = new EncoderParameters(2))
{
using (MemoryStream memoryStream = new MemoryStream())
{
encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, 100L);
encoderParameters.Param[1] = new EncoderParameter(Encoder.ColorDepth, 16L);
canvas.Save(memoryStream, imageCodecInfo[1], encoderParameters);
return Convert.ToBase64String(memoryStream.GetBuffer());
}
}
}
}
}
catch (Exception ex)
{
return null;
}
}
}
public class Randomizer<T> : IComparer<T>
{
protected Random Salter;
protected int Salt;
protected SHA1 Sha1;
public Randomizer()
{
Salter = new Random();
Salt = Salter.Next();
Sha1 = new SHA1Managed();
}
public Randomizer(int seed)
{
Salter = new Random(seed);
Salt = Salter.Next();
Sha1 = new SHA1Managed();
}
private int HashAndSalt(int value)
{
byte[] b = Sha1.ComputeHash(BitConverter.GetBytes(value));
int r = 0;
for (int i = 0; i < (b.Length - 1); i += 4)
{
r = r ^ BitConverter.ToInt32(b, i);
}
return r ^ Salt;
}
public int Compare(T x, T y)
{
return HashAndSalt(x.GetHashCode().CompareTo(HashAndSalt(y.GetHashCode())));
}
}
See the content of this question for couple of different approaches to this sort thing.
(Obviously I prefer mine ;) ).
This sounds similar to how chart generators work. There are two approaches to this that I've seen. One is to create a file on the server and then point to that. The other is to store the bitmap in memory and then call an aspx page in place of the image. The ASP page would read the bitmap from memory and return to the browser.
I'd create an HTTP module to do this.
You could setup the HTTP module to intercept all requests to a particular folder ('/images/generated/') for example.
Then, when you get a request for an image in this folder, the code in your HTTP module will be called. Create your image in memory and write it out to the Response object (having set any appropriate MIME type headers and things first).
In your HTML you can then write an image tag like <img src="/images/generated/image-doesnt-physically-exist.jpg" /> and still get an image back.
Hope that helps point you in the right direction!