2016-08-31 3 views
1

Ich nehme an, dass meine Kamerakalibrierung, die mit der EmguCV-Bibliothek durchgeführt wurde, erfolgreich ist, da ich die Kalibrierung auf Bilder anwenden kann und sie scheinen zumindest etwas korrigiert zu sein. Jetzt möchte ich die Kalibrierung auf Diskette speichern, damit ich sie einfach laden kann, wenn mein Programm läuft. IntrinsicCameraParameters hat Methoden namens writeXML() und readXML(), die XmlWriter bzw. XmlReader als Argumente verwenden. Scheint wie der Weg zu gehen. Ich habe ein Beispiel gefunden, bei dem jemand einen Standard-XmlWriter instanziiert und ihn sofort verwendet hat, um WriteXml() aufzurufen. Aber wenn ich das versuche, bekomme ich Runtime-Exceptions, die mit der Struktur des XML zu tun haben (d. H. Nur einen Wurzelknoten im XML haben). Also habe ich den Code angepasst und weiter unten geteilt. Wenn ich das dumme Wurzelelement nicht einschließe, dann löst der Aufruf von "WriteXml" die Ausnahme über falsch gebildetes XML aus. Ich scheine es schreiben zu können, aber ich weiß nicht, wie ich es zurücklesen soll. Vielleicht verhindert das dumme Wurzelelement, dass das Lesen erfolgreich ist. Ich kann keine Beispiele finden, in denen jemand es liest. Hat jemand ein Beispiel zu teilen?So speichern/laden Sie IntrinsicCameraParameters mit XML

public void SaveCalibrationToFile(IntrinsicCameraParameters ICP) 
     { 
      XmlWriterSettings settings = new XmlWriterSettings(); 
      settings.ConformanceLevel = ConformanceLevel.Auto; 

      // DISTORTION COEFFICIENTS 
      XmlWriter writer = XmlWriter.Create("C:\\Video\\Cal\\DistortionCoeff.xml", settings);    
      writer.WriteStartDocument(); 
      writer.WriteStartElement("TheStupidRootElement"); 

       writer.WriteStartElement("TheDistortionCoefficients"); 
       ICP.DistortionCoeffs.WriteXml(writer); 
       writer.WriteEndElement(); // end TheDistortionCoefficients 

      writer.WriteEndElement(); // end TheStupidRootElement 
      writer.WriteEndDocument(); 
      writer.Close(); 

      // CAMERA MATRIX 
      writer = XmlWriter.Create("C:\\Video\\Cal\\CameraMatrix.xml", settings);     
      writer.WriteStartDocument(); 
      writer.WriteStartElement("TheStupidRootElement"); 

       writer.WriteStartElement("TheCameraMatrix"); 
       ICP.IntrinsicMatrix.WriteXml(writer); 
       writer.WriteEndElement(); // end TheCameraMatrix 

      writer.WriteEndElement(); // end TheStupidRootElement 
      writer.WriteEndDocument(); 
      writer.Close(); 

      // now [just to see if it worked] try to load from the XML 
      XmlReaderSettings readerSettings = new XmlReaderSettings(); 
      readerSettings.ConformanceLevel = ConformanceLevel.Auto; 
      XmlReader reader = XmlReader.Create("C:\\Video\\Cal\\DistortionCoeff.xml", readerSettings); 

      IntrinsicCameraParameters ICP_read = new IntrinsicCameraParameters(); 
      IC.DistortionCoeffs.ReadXml(reader);     
     } 
+0

[ 'IntrinsicCameraParameters.DistortionCoeffs'] (http://www.emgu.com/wiki/files/2.0.0.0/html /ecc3c8f8-9f92-10b1-96a2-b0b1ca6501e9.htm) implementiert tatsächlich ['IXmlSerializable'] (https://msdn2.microsoft.com/en-us/library/fhd7bk0a), also haben Sie einfach versucht,' XmlSerializer' zu verwenden ] (https://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer (v = vs.110) .aspx) auf Ihrem ['IntrinsicCameraParameters'] (http: //www.emgu .com/wiki/files/2.0.0.0/html/1ad4e83e-3c4b-d128-cd44-c9e8a9da007f.htm)? – dbc

+0

Ich habe das nicht versucht. Ich bin nicht sehr gut mit XML. Können Sie näher erläutern, wie XmlSerializer verwendet wird? –

Antwort

1

Net enthält eine Klasse XmlSerializer, die automatisch Instanzen von Typen von und nach XML über Reflexion von öffentlichen Eigenschaften serialisiert werden kann. Es unterstützt auch die IXmlSerializable Schnittstelle, um Typen zu erlauben, ihr Standardverhalten zu überschreiben und vollständige Kontrolle darüber zu haben, wie sie in XML serialisiert werden.

Wie sich herausstellt, IntrinsicCameraParameters.IntrinsicMatrix und IntrinsicCameraParameters.DistortionCoeffs sind beide vom Typ Matrix<double> deren Basisklasse CvArray<TDepth> implementiert diese Schnittstelle. So Objekte dieses Typs serializeable sein sollte XML XmlSerializer mit den folgenden Erweiterungsmethoden verwenden:

public static partial class XmlSerializationExtensions 
{ 
    public static void SerializeToXmlFile<T>(this T obj, string fileName) 
    { 
     var settings = new XmlWriterSettings { Indent = true }; 
     using (var writer = XmlWriter.Create(fileName, settings)) 
     { 
      new XmlSerializer(typeof(T)).Serialize(writer, obj); 
     } 
    } 

    public static T DeserializeFromXmlFile<T>(string fileName) 
    { 
     using (var reader = XmlReader.Create(fileName)) 
     { 
      return (T)new XmlSerializer(typeof(T)).Deserialize(reader); 
     } 
    } 
} 

Dann können Sie einfach tun:

var fileName = "C:\\Video\\Cal\\CameraMatrix.xml"; 
ICP.IntrinsicMatrix.SerializeToXmlFile(fileName); 

Und später, es wieder zu lesen, tun:

var newIntrinsicMatrix = XmlSerializationExtensions.DeserializeFromXmlFile<Matrix<double>>(fileName); 

Zur Bestätigung, siehe the documentation für ein weiteres Beispiel für das Serialisieren einer Matrix von und nach XML.

Logischerweise sollte es auch möglich sein, die gesamten IntrinsicCameraParameters in einer einzelnen XML-Datei mit der gleichen generischen Erweiterungsmethode zum Speichern und Wiederherstellen:

ICP.SerializeToXmlFile(fileName); 

Leider gibt es ein Problem. Die Implementierung von ReadXml() für CvArray<TDepth> ist unterbrochen. Von Proper way to implement IXmlSerializable?

Die ReadXml Methode muss Ihr Objekt Rekonstitution unter Verwendung der Information, dass durch die WriteXml Methode geschrieben wurde.

Wenn diese Methode aufgerufen wird, wird der Leser zu Beginn des Element positioniert, das die Informationen für Ihre Art wickelt. Das heißt, kurz vor dem Start-Tag, das den Anfang eines serialisierten Objekts angibt. Wenn diese Methode zurückgibt, muss es das gesamte Element vom Anfang bis zum Ende gelesen haben, einschließlich seines gesamten Inhalts. Im Gegensatz zu der WriteXml-Methode behandelt das Framework das Wrapper-Element nicht automatisch. Ihre Implementierung muss dies tun.Bei Nichtbeachtung dieser Positionierungsregeln kann Code unerwartete Laufzeitausnahmen generieren oder Daten korrumpieren.

Und eine schnelle Überprüfung der source code für CvArray<TDepth> zeigt, dass das Ende des Hüllenelementes ist nicht lesen. Dies führt dazu, dass alle Daten, die auf die erste CvArray<> im XML folgen, nicht deserialisiert werden können.

Wenn Sie also eine Matrix<T> in eine größere XML-Datei einbetten möchten, müssen Sie Serialisierungssurrogate-Typen (oder Datentransfer-Objekttypen, wenn Sie bevorzugen) einführen, wie folgt. (Beachten Sie die Einführung des Emgu.CV.SerializationSurrogates Namespace):

namespace Emgu.CV.SerializationSurrogates 
{ 
    using Emgu.CV; 

    public class Matix<TDepth> where TDepth : new() 
    { 
     [XmlAttribute] 
     public int Rows { get; set; } 

     [XmlAttribute] 
     public int Cols { get; set; } 

     [XmlAttribute] 
     public int NumberOfChannels { get; set; } 

     [XmlAttribute] 
     public int CompressionRatio { get; set; } 

     public byte[] Bytes { get; set; } 

     public static implicit operator Emgu.CV.SerializationSurrogates.Matix<TDepth>(Emgu.CV.Matrix<TDepth> matrix) 
     { 
      if (matrix == null) 
       return null; 
      return new Matix<TDepth> 
      { 
       Rows = matrix.Rows, 
       Cols = matrix.Cols, 
       NumberOfChannels = matrix.NumberOfChannels, 
       CompressionRatio = matrix.SerializationCompressionRatio, 
       Bytes = matrix.Bytes, 
      }; 
     } 

     public static implicit operator Emgu.CV.Matrix<TDepth>(Matix<TDepth> surrogate) 
     { 
      if (surrogate == null) 
       return null; 
      var matrix = new Emgu.CV.Matrix<TDepth>(surrogate.Rows, surrogate.Cols, surrogate.NumberOfChannels); 
      matrix.SerializationCompressionRatio = surrogate.CompressionRatio; 
      matrix.Bytes = surrogate.Bytes; 
      return matrix; 
     } 
    } 

    public class IntrinsicCameraParameters 
    { 
     [XmlElement("IntrinsicMatrix", Type = typeof(Emgu.CV.SerializationSurrogates.Matix<double>))] 
     public Emgu.CV.Matrix<double> IntrinsicMatrix { get; set; } 

     [XmlElement("DistortionCoeffs", Type = typeof(Emgu.CV.SerializationSurrogates.Matix<double>))] 
     public Emgu.CV.Matrix<double> DistortionCoeffs { get; set; } 

     public static implicit operator Emgu.CV.SerializationSurrogates.IntrinsicCameraParameters(Emgu.CV.IntrinsicCameraParameters icp) 
     { 
      if (icp == null) 
       return null; 
      return new IntrinsicCameraParameters 
      { 
       DistortionCoeffs = icp.DistortionCoeffs, 
       IntrinsicMatrix = icp.IntrinsicMatrix, 
      }; 
     } 

     public static implicit operator Emgu.CV.IntrinsicCameraParameters(Emgu.CV.SerializationSurrogates.IntrinsicCameraParameters surrogate) 
     { 
      if (surrogate == null) 
       return null; 
      return new Emgu.CV.IntrinsicCameraParameters 
      { 
       DistortionCoeffs = surrogate.DistortionCoeffs, 
       IntrinsicMatrix = surrogate.IntrinsicMatrix, 
      }; 
     } 
    } 
} 

Jetzt können Sie Ihre IntrinsicCameraParameters mit folgenden Erweiterungsmethoden speichern und abrufen: Alle

public static class IntrinsicCameraParametersExtensions 
{ 
    public static void SerializeIntrinsicCameraParametersExtensionsToXmlFile(this IntrinsicCameraParameters icp, string fileName) 
    { 
     var surrogate = (Emgu.CV.SerializationSurrogates.IntrinsicCameraParameters)icp; 
     surrogate.SerializeToXmlFile(fileName); 
    } 

    public static IntrinsicCameraParameters DeserializeIntrinsicCameraParametersFromXmlFile(string fileName) 
    { 
     var surrogate = XmlSerializationExtensions.DeserializeFromXmlFile<Emgu.CV.SerializationSurrogates.IntrinsicCameraParameters>(fileName); 
     return surrogate; 
    } 
} 

, dass gesagt wird, IntrinsicCameraParameters ist marked as obsolete in der aktuellen Version :

[SerializableAttribute] 
[ObsoleteAttribute("This class will be removed in the next release, please use separate camera matrix and distortion coefficient with the CvInvoke function instead.")] 
public class IntrinsicCameraParameters : IEquatable<IntrinsicCameraParameters> 

Sie möchten vielleicht diesen Entwurf neu denken.

übrigens eine nicht-gebrochene Version von CvArray<TDepth>.ReadXml() würde wie folgt aussehen:

public virtual void ReadXml(System.Xml.XmlReader reader) 
    { 
     #region read properties of the matrix and assign storage 

     int rows = Int32.Parse(reader.GetAttribute("Rows")); // Should really be using XmlConvert for this 
     int cols = Int32.Parse(reader.GetAttribute("Cols")); 
     int numberOfChannels = Int32.Parse(reader.GetAttribute("NumberOfChannels")); 
     SerializationCompressionRatio = Int32.Parse(reader.GetAttribute("CompressionRatio")); 

     AllocateData(rows, cols, numberOfChannels); 

     #endregion 

     #region decode the data from Xml and assign the value to the matrix 

     if (!reader.IsEmptyElement) 
     { 
      using (var subReader = reader.ReadSubtree()) 
      { 
       // Using ReadSubtree guarantees we don't read past the end of the element in case the <Bytes> element 
       // is missing or extra unexpected elements are included. 
       if (subReader.ReadToFollowing("Bytes")) 
       { 
        int size = _sizeOfElement * ManagedArray.Length; 
        if (SerializationCompressionRatio == 0) 
        { 
         Byte[] bytes = new Byte[size]; 
         subReader.ReadElementContentAsBase64(bytes, 0, bytes.Length); 
         Bytes = bytes; 
        } 
        else 
        { 
         int extraHeaderBytes = 20000; 
         Byte[] bytes = new Byte[size + extraHeaderBytes]; 
         int countOfBytesRead = subReader.ReadElementContentAsBase64(bytes, 0, bytes.Length); 
         Array.Resize<Byte>(ref bytes, countOfBytesRead); 
         Bytes = bytes; 
        } 
       } 
      } 
     } 
     // Consume the end of the wrapper element also. 
     reader.Read(); 

     #endregion 
    } 
+1

Danke für die Arbeit in dieser Antwort. Ich habe es mit der 2-Datei-Lösung von Ihrer vorherigen [jetzt gelöscht] Antwort funktioniert. Ich möchte nicht mehr Stunden damit verbringen, zu einer zukunftssicheren 1-Datei-Lösung zu kommen. Das C# -Tool ist nur ein Testbett und letztendlich wird das, was ich erstelle, in regulären OpenCV mit C++ implementiert. Ich fühle mich wie Emgu ist ein Durcheinander und die Tatsache, dass ich sogar versuche, mit OpenCV in C# zu arbeiten, könnte eine schlechte Idee gewesen sein, mit zu beginnen. Ich dachte, es wäre einfach, denn es ist C#, aber ich bin schon eine ganze Woche dabei. Mein Kollege schrieb in zwei Tagen dasselbe in Python. –