C#, Bild->BMP konvertieren,skalieren,via STREAM in Blob

16. Juni 2011 14:50

Hallo an Alle, vielleicht kann das jemand brauchen.

Ich hatte in einem alten Thread die Anregung gefunden, wie man über C# Daten via Stream in ein Blob schreiben kann. Ich suchte allerdings nach einer Lösung, ein .TIF Bild nach BMP zu konvertieren, es zu
skalieren um ein Preview Bild zu haben, und dieses direkt in einen BLOB zu laden.

Da ich auch nirgendwo eine Antwort auf die Frage finden konnte, wozu denn diese Anweisung "byte[] buffer1 = new byte[0xafc8];" in dem erwähnten Beispiel gut ist, habe ich selber etwas experimentiert.

Hier der C# Code, der das zuvor beschriebene kann:
Code:
        public void ConvertFileToBmpStream(object ObjPictureStream,
                                           string StrSourceFile,
                                           int NewHeigth,
                                           int NewWidth)
        {
            IStream StmPicStream = ObjPictureStream as IStream;         // soll später das Bild aufnehmen
            MemoryStream ms = new MemoryStream();                       // ein Memory Stream unterstützt .SAVE as BitMap

            IntPtr rwBytes = Marshal.AllocHGlobal(4);                   // 4 Byte allozieren - <stream>.Write benötigt
                                                                        // später diesen Pointer

            Image img = Image.FromFile(StrSourceFile);                  // Ein Image z.B. aus einer .TIF Datei lesen
            Size MySize = new Size(NewWidth, NewHeigth);                // Neues Size Objekt für die Skalierung
            Bitmap MyBitMap = new Bitmap(img, MySize);                  // skaliertes BitMap erzeugen
            MyBitMap.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);  // Neues BitMap im Memory-Stream speichern

            int StreamLength = Convert.ToInt32(ms.Length);              // wie lang ist der memory Stream? Könnte > 32 Bit sein,
                                                                        // deshalb hier ein ToInt32.
            byte[] buffer = new byte[StreamLength];                     // Den memory-Stream in ein Byte-Array übertragen
            buffer = ms.ToArray();                                      // den Inhalt des memory-Stream in das byte-Array übertragen

            StmPicStream.Write(buffer, StreamLength, rwBytes);          // Das Byte-Array kann nun in den als Reference übergebenen
                                                                        // Stream geschrieben werden. Hier braucht´s wohl den Pointer,
                                                                        // uns ansonsten gar nicht interessiert.

            Marshal.FreeHGlobal(rwBytes);                               // Den 4 byte Speicherbereich für den pointer wieder freigeben
            ms.Dispose();                                               // Den Memory-Stream verwerfen
            img.Dispose();                                              // Wichtig, sonst bleibt der Handle auf die Source-Datei offen!
            MyBitMap.Dispose();                                         // ... und weg damit.
        }


Unter Navision sieht das dann so aus:
Code:
IF ISCLEAR(AutPicConvert) THEN CREATE(AutPicConvert);
...
LocRecTemp.Thumbnail.CREATEINSTREAM(LocStreamPreviewPic);
AutPicConvert.ConvertFileToBmpStream(LocStreamPreviewPic, 'D:\1.TIF', 100, 100);
...


Da ich hier auch mit einer Fremd-Bibliothek für Dokumenten-Scanning arbeite (VintasoftTwain), habe ich selbstverständlich auch sofort die Möglichkeit untersucht, ein unmittelbar vom Scanner gelesenes Image auf gleichem Wege in das Blob Feld zu schreiben. Dazu muss ich lediglich mein "Acquired" Image mit einer der Methoden dieser Fremdbibliothek konvertieren, und wieder in den MemoryStream schreiben:
Code:
 
           // Die AcquiredImages[] sind eine Collection Vintasoft Scan-Bibliothek
           Bitmap MyBitMap = new Bitmap(deviceList[DeviceIndex].AcquiredImages[ImageIndex].GetAsBitmap(true), NewWidth, NewHeigth);

           // Das Speichern des BitMap im Memory-Stream ist entscheidend, dann weiter, wie in dem großen Beispiel
           MyBitMap.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);

und voilà. Sicher ein sehr spezieller Fall, aber ich wollte einfach zeigen, dass man ggf. auch direkt ein gescanntes Image in ein BLOB Feld schreiben kann, ohne dabei irgendeine Datei zu erzeugen.

Bei mir funktionert´s wunderbar, aber Kommentare wären mir höchst willkommen, daich in C# absoluter Neuling bin.

Pidi

Re: C#, Bild->BMP konvertieren,skalieren,via STREAM in Blob

16. Juni 2011 16:03

Oha, das ist nett :D

Da ist unsere Lösung Kinderkram dagegen: wir konvertieren unsere JPEGs mit einem Kommandozeilenprogramm temporär in ein BMP und dieses importieren wir dann.

Re: C#, Bild->BMP konvertieren,skalieren,via STREAM in Blob

16. Juni 2011 16:18

Ich glaube, dieses Kommandozeilen-Tool ist mir nur zu gut bekannt :-) Wer aber nicht direkt mit Memory-Streams expertieren möchte, kann in C# auch einen 4-Zeiler implementieren:

Code:
        public void ConvertJpegToBmp(string StrSourceFile, string StrDestFile, int DivHeigth, int DivWidth)
        {
            Image img = Image.FromFile(StrSourceFile);
            Size MySize = new Size(img.Height / DivHeigth, img.Width / DivWidth);
            Bitmap MyBitMap = new Bitmap(img, MySize);
            MyBitMap.Save(StrDestFile, System.Drawing.Imaging.ImageFormat.Bmp);
        }


Unter Navision:
Code:
  IF EXISTS(InTxtSourcePath) THEN
    CS_Tools.ConvertJpegToBmp(InTxtSourcePath, TxtTempBMPpath, InIntDivH, InIntDivW);


Das ist dann ebenfalls "Datei zu Datei", man kann aber auch skalieren, und vor allem ist das wesentlich schneller. Darüberhinaus spart man sich das Gehampel mit dem SHELL-Befehl.

Pidi

Re: C#, Bild->BMP konvertieren,skalieren,via STREAM in Blob

16. Juni 2011 16:41

Vielen Dank, Pidi!

Re: C#, Bild->BMP konvertieren,skalieren,via STREAM in Blob

17. Juni 2011 15:56

Hallo.

Ich nutze genau(!!!) die Routine "ConvertFileToBmpStream" aber als .NET Komponente!!!!

Wenn ich meine entsprechende Codeunit uas dem RTC aufrufe bekomme ich den Fehler:
Meldung für C/AL-Programmierer:
Fehler beim Aufruf von Member ConvertFileToBmpStream:
Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt.

Meine Routine
lItem.GET('0001');
lItem.Picture.CREATEINSTREAM(lInStream);
ConvertFileToBmpStream(lInStream, 'd:\TifBildAudDemServiceTier.tif', 100, 100); // Service Tier!!! // hier wird .NET Komponente mit Constructor aufgerufen.
lItem.MODIFY(TRUE);

Kann mir da einer Hinweise auf den Fehler geben???
Kann man das nicht einfach als .NET Komponente nutzen???

Re: C#, Bild->BMP konvertieren,skalieren,via STREAM in Blob

17. Juni 2011 17:46

Leider habe ich von diesen ".NET Komponenten", wie sie der RTC wohl nutzt, keinen Schimmer, da ich mir in absehbarer Zeit nicht vorstellen kann, auf Pages umzustellen.

Ich nutze selber eine C# .Net Klassenbibliothek, die ich unter VisualStudio 2008 geschrieben habe bzw. permanent um Dinge ergänze, die Navision nicht kann. Dabei kommuniziert Navision mit dieser .NET Klasse über das InteropInterface, da Navision selber ja COM ist.

Eine der der auf mehrere Dateien verteilten Klassen habe ich Imaging genannt, und die sieht dann so aus:
Code:
using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Drawing;
using System.IO;

[assembly: ClassInterface(ClassInterfaceType.AutoDual)]

namespace BosCsTools
{
    public interface IImage_Funcs
    {
        void ConvertJpegToBmp(string StrSourceFile, string StrDestFile, int DivHeigth, int DivWidth);
        void ConvertFileToBmpStream(object ObjPictureStream, string StrSourceFile, int NewHeigth, int NewWidth);
    }

    [ComVisible(true)]
    [ProgId("Image_Funcs")]
    [ClassInterface(ClassInterfaceType.None)]
    public class Image_Funcs : IImage_Funcs
    {
        public void ConvertJpegToBmp(string StrSourceFile, string StrDestFile, int DivHeigth, int DivWidth)
        {
            Image img = Image.FromFile(StrSourceFile);
            Size MySize = new Size(img.Height / DivHeigth, img.Width / DivWidth);
            Bitmap MyBitMap = new Bitmap(img, MySize);
            MyBitMap.Save(StrDestFile, System.Drawing.Imaging.ImageFormat.Bmp);
        }

        public void ConvertFileToBmpStream(object ObjPictureStream,
                                   string StrSourceFile,
                                   int NewHeigth,
                                   int NewWidth)
        {
            IStream StmPicStream = ObjPictureStream as IStream;         // soll später das Bild aufnehmen
            MemoryStream ms = new MemoryStream();                       // ein Memory Stream unterstützt .SAVE as BitMap

            IntPtr rwBytes = Marshal.AllocHGlobal(4);                   // 4 Byte allozieren - <stream>.Write benötigt
            // später diesen Pointer

            Image img = Image.FromFile(StrSourceFile);                  // Ein Image z.B. aus einer .TIF Datei lesen
            Size MySize = new Size(NewWidth, NewHeigth);                // Neues Size Objekt für die Skalierung
            Bitmap MyBitMap = new Bitmap(img, MySize);                  // skaliertes BitMap erzeugen
            MyBitMap.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);  // Neues BitMap im Memory-Stream speichern

            int StreamLength = Convert.ToInt32(ms.Length);              // wie lang ist der memory Stream? Könnte > 32 Bit sein,
            // deshalb hier ein ToInt32.
            byte[] buffer = new byte[StreamLength];                     // Den memory-Stream in ein Byte-Array übertragen
            buffer = ms.ToArray();               // den Inhalt des memory-Stream in das byte-Array übertragen

            StmPicStream.Write(buffer, StreamLength, rwBytes);      // Das Byte-Array kann nun in den als Reference übergebenen
            // Stream geschrieben werden. Hier braucht´s wohl den Pointer,
            // uns ansonsten gar nicht interessiert.

            Marshal.FreeHGlobal(rwBytes);      // Den 4 byte Speicherbereich für den pointer wieder freigeben
            ms.Dispose();                              // Den Memory-Stream verwerfen
            img.Dispose();                             // Wichtig, sonst bleibt der Handle auf die Source-Datei offen!
            MyBitMap.Dispose();                    // ... und weg damit.
        }
    }
}


Wenn ich diese Klasse dann unter VisualStudio "erstelle", wird das neue Assembly ja auch direkt registriert (spart das regAsm) und ich kann unter navision eine Autoamtion Variable anlegen:
Code:
LocRecItem Record Item   
LocStreamPreviewPic   InStream      
LocAutImaging Automation 'BosCsTools'.Image_Funcs


Ein Code unter Navision könnte dann so aussehen:
Code:
IF ISCLEAR(LocAutImaging) THEN CREATE(LocAutImaging);

IF LocRecItem.GET('10018') THEN BEGIN
  LocRecItem.Picture.CREATEINSTREAM(LocStreamPreviewPic);
  LocAutImaging.ConvertFileToBmpStream(LocStreamPreviewPic, 'D:\1.TIF', 370, 270);
  LocRecItem.MODIFY;
  COMMIT;

  FORM.RUNMODAL(FORM::ShowPic, LocRecItem);
END;


Mehr kann ich zu dem Problem momentan leider nicht beitragen.

Pidi

Re: C#, Bild->BMP konvertieren,skalieren,via STREAM in Blob

20. Juni 2011 09:08

muss man da nicht auch mit create arbeiten?
Du hast da nirgends die DotNet Variable instanziert.

Re: C#, Bild->BMP konvertieren,skalieren,via STREAM in Blob

20. Juni 2011 12:35

Hallo.

Da habe ich nicht allesn Sourcecode publiziert.

Meine Routine "ConvertFileToBmpStream":
// Constructor
NavNetCommon := NavNetCommon.Coding;

NavNetCommon.ConvertFileToBmpStream(pOutputStream, pInputFilename, pNewHeight, pNewWidth);


mit NavNetCommon meiner .Net Komponente

Re: C#, Bild->BMP konvertieren,skalieren,via STREAM in Blob

26. Juni 2012 18:29

Ich wurde nun angesprochen, ob man nicht auch ein .TIF oder .JPG direkt aus einem Blob über C# konvertieren und skalieren könnte, um es dann wieder für die Anzeige als BMP in einen anderen Blob zu laden.

In meiner 1. Message habe ich den C# Code veröffentlicht, um ein Bild in einem NICHT-BMP Format aus einer Datei zu laden, in ein BMP zu konvertieren und bei Bedarf auch zu skalieren, um es dann letztlich über einen Stream in ein Blob-Feld (nehme dazu immer eine temporäre Tabelle) zu laden, und dieses auf einer Navision Form anzuzeigen. Macht aber nur Sinn, wenn man (wie ich) seine Bilder in einem Share speichert.

Natürlich dachte ich, das könne ja kein großes Problem sein ... Allerdings sind die COM-Streams (IStream), die Navision verwendet, in keiner Weise kompatibel mit den .Net Streams, so dass auch hier konvertiert werden muss. Das ist auch deshalb ein wenig tricky, weil die COM-Streams keine "length" property besitzen, funktioniert aber auch ohne dabei "unsafe" code zu verwenden (wie in einigen Beispielen im Netz)

Code:
       public void PicStreamToBMP(object objPicture, object ObjConvertedPicture, int NewHeigth, int NewWidth)
        {
            Bitmap MyBitMap = null;

            byte[] buffer1 = new byte[0xafc8];
            IntPtr rwBytes = Marshal.AllocHGlobal(4);

            IStream ComStmInPic = objPicture as IStream;   // Übergebenes Bild in COM-Stream konvertieren
            MemoryStream MemStmInPic = new MemoryStream();  // In diesem Memory Buffer wird der COM-Stream gelesen ...

            int _rwBytes = 0;                                           
            do
            {
                ComStmInPic.Read(buffer1, 0xafc8, rwBytes);
                _rwBytes = Marshal.ReadInt32(rwBytes);
                MemStmInPic.Write(buffer1, 0, _rwBytes);      // ... um ihn in einen .Net Stream umzuwandeln!
            } while (_rwBytes > 0);

            Image img = Image.FromStream(MemStmInPic);  // Das Image aus dem MemoyStream lesen
            Size MySize = new Size(NewWidth, NewHeigth);   // Neues Size Objekt für die Skalierung
           
            if (NewHeigth == 0 & NewWidth == 0)
            { MyBitMap = new Bitmap(img); }
            else
            { MyBitMap = new Bitmap(img, MySize); }                             // skaliertes BitMap erzeugen

            IStream StmPicStream = ObjConvertedPicture as IStream;

            MemoryStream MemStmOutPic = new MemoryStream();       // Memory Stream für die Rückgabe

            MyBitMap.Save(MemStmOutPic,
                          System.Drawing.Imaging.ImageFormat.Bmp);   // Neues BitMap im Memory-Stream speichern

            int StreamLength = Convert.ToInt32(MemStmOutPic.Length);   // MemoryStream kann bis zu 32 Bit lang sein
            byte[] buffer = new byte[StreamLength];     // Den memory-Stream in ein Byte-Array übertragen
            buffer = MemStmOutPic.ToArray();

            StmPicStream.Write(buffer, StreamLength, rwBytes);

            MemStmInPic.Dispose();
            MemStmOutPic.Dispose();                                     
            img.Dispose();                                           
            MyBitMap.Dispose();
            Marshal.FreeHGlobal(rwBytes);
        }


und hier ein Beispiel für den Aufruf aus Navision

Code:
ConvertPictureFromStream(VAR InRecItem : Record Item;VAR InTmpRecThumbnail : TEMPORARY Record TempStruct;InIntHeight : Integer;InIntWid
CLEAR(InTmpRecThumbnail);

InRecItem.CALCFIELDS(Picture);
IF InRecItem.Picture.HASVALUE THEN BEGIN
   IF ISCLEAR(LocAutImaging) THEN CREATE(LocAutImaging);
    InRecItem.Picture.CREATEOUTSTREAM(LocStreamPicture);
    InTmpRecThumbnail.Thumbnail.CREATEINSTREAM(LocStreamConverted);
    LocAutImaging.PicStreamToBMP(LocStreamPicture, LocStreamConverted, InIntHeight, InIntWidth);
   CLEAR(LocAutImaging);
END;


Man lädt also das Bild im Non-Nav Format aus einem Blob-Feld in einen Stream, erzeugt einen InStream für den Ziel-Blob und übergibt beide Streams als Parameter an die C# Konvertierungs-Funktion.

Fragt mich jetzt bitte nicht, warum jemand so seine Bilder speichert (klein, aber trotzdem Teil der nav DB ?) aber vielleicht ist ja grundsätzlich interessant, wie man einen IStream in einen .Net Stream konvertiert. Ich musste jedenfalls einige Zeit googeln ... :-?

Pidi

Re: C#, Bild->BMP konvertieren,skalieren,via STREAM in Blob

17. August 2012 11:16

Hallo Pidi

Vielen Dank.
Dein Tipp mit den Streams hat mir sehr geholfen.

gruss

Re: C#, Bild->BMP konvertieren,skalieren,via STREAM in Blob

27. August 2012 09:52

Wir haben mittlerweile unsere Lösung auch umgestellt. Danke! :)