2016-08-31 20 views
1

我假设用EmguCV库执行的摄像机校准是成功的,因为我可以将校准应用于图像,并且它们看起来至少有些更正。现在我想将校准存储到磁盘,以便在程序运行时加载它。 IntrinsicCameraParameters具有分别以XmlWriter和XmlReader作为参数的称为writeXML()和readXML()的方法。似乎要走的路。我找到一个例子,其中有人刚实例化了一个默认的XmlWriter,并立即用它来调用WriteXml()。但是当我尝试这样做时,我得到的运行时异常似乎与XML的结构有关(即只能在XML中有一个根节点)。所以我调整了代码并在下面进行了分享。如果我不包含愚蠢的根元素,那么对“WriteXml”的调用会抛出关于不正确形成的XML的异常。所以我似乎可以写出来,但我不知道如何阅读它。也许愚蠢的根元素阻止读取成功。找不到任何人阅读它的任何示例。有没有人有一个共享的例子?如何使用XML存储/加载IntrinsicCameraParameters

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)实际上实现了['IXmlSerializable'](https://msdn2.microsoft.com/en-us/library/fhd7bk0a),所以你试着简单地使用['XmlSerializer' ](https://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer(v = vs.110).aspx)在您的['IntrinsicCameraParameters'](http://www.emgu .com/wiki/files/2.0.0.0/html/1ad4e83e-3c4b-d128-cd44-c9e8a9da007f.htm)? – dbc

+0

我还没有尝试过。我对XML不太擅长。你能详细说明如何使用XmlSerializer吗? –

回答

1

净包含一个类XmlSerializer,可以自动从和到XML经由公共属性的反射序列的类型的实例。它还支持接口允许类型重写其默认行为,并完全控制它们如何序列化为XML。

事实证明,IntrinsicCameraParameters.IntrinsicMatrixIntrinsicCameraParameters.DistortionCoeffs均为Matrix<double>类型,其基类CvArray<TDepth>实现该接口的。因此,这种类型的对象应该是可序列化使用XmlSerializer使用下面的扩展方法的XML:

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); 
     } 
    } 
} 

然后,你可以这样做:

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

后来,读回,这样做:

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

有关确认,请参阅the documentation,以获取序列化来自和来自XML的矩阵的另一个示例。

从逻辑上讲,它也应该可以保存和使用相同的通用扩展方法在一个XML文件恢复整个IntrinsicCameraParameters

ICP.SerializeToXmlFile(fileName); 

不幸的是,有一个问题。 CvArray<TDepth>的执行ReadXml()已中断。从Proper way to implement IXmlSerializable?

ReadXml方法必须使用 写由WriteXml方法的信息重建 你的对象。

当这个方法被调用,读者 位于该包装为 你的类型的信息 元素的开始。也就是说,就在 开始标记之前,该标记指示序列化对象的开始 。当此方法返回时,它必须从头至尾读取整个元素 ,包括其所有内容。与 WriteXml方法不同,框架 不自动处理包装器元素 。您的实施 必须这样做。未能遵守这些定位规则可能导致代码 生成意外的运行时异常 或损坏的数据。

而且source codeCvArray<TDepth>的快速检查表明,包装元素的到底是不读。这将导致XML中第一个CvArray<>之后的任何数据无法反序列化。因此,如果您想要将Matrix<T>嵌入到更大的XML文件中,则需要引入序列化代理类型(或者您喜欢的数据传输对象类型),如下所示。 (注引进Emgu.CV.SerializationSurrogates命名空间):

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, 
      }; 
     } 
    } 
} 

现在您可以保存和检索IntrinsicCameraParameters具有以下扩展方法:

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; 
    } 
} 

之所以这么说,IntrinsicCameraParameters是在当前版本marked as obsolete

[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> 

所以你可能想重新考虑这个设计。

顺便提及,CvArray<TDepth>.ReadXml()非破版本将如下所示:

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

感谢所有在这个答案中的工作。我从你以前的[现在删除]答案的2文件解决方案中工作。我不想花费更多的时间来获得1个文件的面向未来的解决方案。 C#工具只是一个测试平台,最终我将创建的东西将在常规OpenCV中用C++实现。我觉得Emgu是一团糟,事实上,我甚至试图在C#中使用OpenCV开始可能是一个坏主意。我认为这很容易,因为它是C#,但我已经整整一周了。我的同事在两天内用python写了同样的东西。 –