LeastSquaresTransform.cs
上传用户:sex100000
上传日期:2013-11-09
资源大小:1377k
文件大小:11k
源码类别:

GIS编程

开发平台:

C#

  1. // Copyright 2005, 2006 - Morten Nielsen (www.iter.dk)
  2. //
  3. // This file is part of SharpMap.
  4. // SharpMap is free software; you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation; either version 2 of the License, or
  7. // (at your option) any later version.
  8. // 
  9. // SharpMap is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. // GNU Lesser General Public License for more details.
  13. // You should have received a copy of the GNU Lesser General Public License
  14. // along with SharpMap; if not, write to the Free Software
  15. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
  16. using System;
  17. using System.Collections.Generic;
  18. using System.Text;
  19. namespace SharpMap.Utilities
  20. {
  21. /// <summary>
  22. /// Calculates Affine and Helmert transformation using Least-Squares Regression of input and output points
  23. /// </summary>
  24. public class LeastSquaresTransform
  25. {
  26. private List<SharpMap.Geometries.Point> inputs;
  27. private List<SharpMap.Geometries.Point> outputs;
  28. /// <summary>
  29. /// Initialize Least Squares transformations
  30. /// </summary>
  31. public LeastSquaresTransform() 
  32. {
  33. inputs = new List<SharpMap.Geometries.Point>();
  34. outputs = new List<SharpMap.Geometries.Point>();
  35. }
  36. /// <summary>
  37. /// Adds an input and output value pair to the collection
  38. /// </summary>
  39. /// <param name="input"></param>
  40. /// <param name="output"></param>
  41. public void AddInputOutputPoint(SharpMap.Geometries.Point input, SharpMap.Geometries.Point output)
  42. {
  43. inputs.Add(input);
  44. outputs.Add(output);
  45. }
  46. /// <summary>
  47. /// Removes input and output value pair at the specified index
  48. /// </summary>
  49. /// <param name="i"></param>
  50. public void RemoveInputOutputPointAt(int i)
  51. {
  52. inputs.RemoveAt(i);
  53. outputs.RemoveAt(i);
  54. }
  55. /// <summary>
  56. /// Gets the input point value at the specified index
  57. /// </summary>
  58. /// <param name="i">index</param>
  59. /// <returns>Input point value a index 'i'</returns>
  60. public SharpMap.Geometries.Point GetInputPoint(int i)
  61. {
  62. return inputs[i];
  63. }
  64. /// <summary>
  65. /// Sets the input point value at the specified index
  66. /// </summary>
  67. /// <param name="p">Point value</param>
  68. /// <param name="i">index</param>
  69. public void SetInputPointAt(SharpMap.Geometries.Point p, int i)
  70. {
  71. inputs[i] = p;
  72. }
  73. /// <summary>
  74. /// Gets the output point value at the specified index
  75. /// </summary>
  76. /// <param name="i">index</param>
  77. /// <returns>Output point value a index 'i'</returns>
  78. public SharpMap.Geometries.Point GetOutputPoint(int i)
  79. {
  80. return outputs[i];
  81. }
  82. /// <summary>
  83. /// Sets the output point value at the specified index
  84. /// </summary>
  85. /// <param name="p">Point value</param>
  86. /// <param name="i">index</param>
  87. public void SetOutputPointAt(SharpMap.Geometries.Point p, int i)
  88. {
  89. outputs[i] = p;
  90. }
  91. /// <summary>
  92. /// Return an array with the six affine transformation parameters {a,b,c,d,e,f} and the sum of the squares of the residuals (s0)
  93. /// </summary>
  94. /// <remarks>
  95. /// a,b defines scale vector 1 of coordinate system, d,e scale vector 2. c,f defines offset.
  96. /// <para>
  97. /// Converting from input (X,Y) to output coordinate system (X',Y') is done by:
  98. /// X' = a*X + b*Y + c, Y' = d*X + e*Y + f
  99. /// </para>
  100. /// <para>
  101. /// Transformation based on Mikhail "Introduction to Modern Photogrammetry" p. 399-300.
  102. /// Extended to arbitrary number of measurements by M. Nielsen
  103. /// </para>
  104. /// </remarks>
  105. /// <returns>Array with the six transformation parameters and sum of squared residuals:  a,b,c,d,e,f,s0</returns>
  106. public double[] GetAffineTransformation() 
  107. {
  108. if(inputs.Count<3)
  109. throw(new System.Exception("At least 3 measurements required to calculate affine transformation"));
  110. //double precision isn't always enough when transforming large numbers.
  111. //Lets subtract some mean values and add them later again:
  112. //Find approximate center values:
  113. SharpMap.Geometries.Point meanInput = new SharpMap.Geometries.Point(0, 0);
  114. SharpMap.Geometries.Point meanOutput = new SharpMap.Geometries.Point(0, 0);
  115. for (int i = 0; i < inputs.Count; i++) 
  116. {
  117. meanInput.X += inputs[i].X;
  118. meanInput.Y += inputs[i].Y;
  119. meanOutput.X += outputs[i].X;
  120. meanOutput.Y += outputs[i].Y;
  121. }
  122. meanInput.X = Math.Round(meanInput.X / inputs.Count);
  123. meanInput.Y = Math.Round(meanInput.Y / inputs.Count);
  124. meanOutput.X = Math.Round(meanOutput.X / inputs.Count);
  125. meanOutput.Y = Math.Round(meanOutput.Y / inputs.Count);
  126. double[][] N = CreateMatrix(3,3);
  127. //Create normal equation: transpose(B)*B
  128. //B: matrix of calibrated values. Example of row in B: [x , y , -1]
  129. for (int i = 0; i < inputs.Count; i++) 
  130. {
  131. //Subtract mean values
  132. inputs[i].X -= meanInput.X;
  133. inputs[i].Y -= meanInput.Y;
  134. outputs[i].X -= meanOutput.X;
  135. outputs[i].Y -= meanOutput.Y;
  136. //Calculate summed values
  137. N[0][0] += Math.Pow(inputs[i].X,2);
  138. N[0][1] += inputs[i].X*inputs[i].Y;
  139. N[0][2] += -inputs[i].X;
  140. N[1][1] += Math.Pow(inputs[i].Y,2);
  141. N[1][2] += -inputs[i].Y;
  142. }
  143. N[2][2] = inputs.Count;
  144. double[] t1 = new double[3];
  145. double[] t2 = new double[3];
  146. for (int i = 0; i < inputs.Count; i++) 
  147. {
  148. t1[0] += inputs[i].X * outputs[i].X;
  149. t1[1] += inputs[i].Y * outputs[i].X;
  150. t1[2] += -outputs[i].X;
  151. t2[0] += inputs[i].X * outputs[i].Y;
  152. t2[1] += inputs[i].Y * outputs[i].Y;
  153. t2[2] += -outputs[i].Y;
  154. }
  155. double[] trans = new double[7];
  156. // Solve equation N = transpose(B)*t1
  157. double frac = 1 / (-N[0][0]*N[1][1]*N[2][2]+N[0][0]*Math.Pow(N[1][2],2)+Math.Pow(N[0][1],2)*N[2][2]-2*N[1][2]*N[0][1]*N[0][2]+N[1][1]*Math.Pow(N[0][2],2));
  158. trans[0] = (-N[0][1]*N[1][2]*t1[2]+N[0][1]*  t1[1]*N[2][2]-N[0][2]*N[1][2]*t1[1]+N[0][2]*N[1][1]*t1[2]-t1[0]*N[1][1]*N[2][2]+t1[0]*Math.Pow(N[1][2],2)) * frac;
  159. trans[1] = (-N[0][1]*N[0][2]*t1[2]+N[0][1]*  t1[0]*N[2][2]+N[0][0]*N[1][2]*t1[2]-N[0][0]*t1[1]*N[2][2]-N[0][2]*N[1][2]*t1[0]+Math.Pow(N[0][2],2)*t1[1]) * frac;
  160. trans[2] = -(-N[1][2]*N[0][1]*t1[0]+Math.Pow(N[0][1],2)*t1[2]+N[0][0]*N[1][2]*t1[1]-N[0][0]*N[1][1]*t1[2]-N[0][2]*N[0][1]*t1[1]+N[1][1]*N[0][2]*t1[0]) * frac;
  161. trans[2] += - meanOutput.X + meanInput.X;
  162. // Solve equation N = transpose(B)*t2
  163. trans[3] = (-N[0][1]*N[1][2]*t2[2]+N[0][1]*  t2[1]*N[2][2]-N[0][2]*N[1][2]*t2[1]+N[0][2]*N[1][1]*t2[2]-t2[0]*N[1][1]*N[2][2]+t2[0]*Math.Pow(N[1][2],2)) * frac;
  164. trans[4] = (-N[0][1]*N[0][2]*t2[2]+N[0][1]*  t2[0]*N[2][2]+N[0][0]*N[1][2]*t2[2]-N[0][0]*t2[1]*N[2][2]-N[0][2]*N[1][2]*t2[0]+Math.Pow(N[0][2],2)*t2[1]) * frac;
  165. trans[5] = -(-N[1][2]*N[0][1]*t2[0]+Math.Pow(N[0][1],2)*t2[2]+N[0][0]*N[1][2]*t2[1]-N[0][0]*N[1][1]*t2[2]-N[0][2]*N[0][1]*t2[1]+N[1][1]*N[0][2]*t2[0]) * frac;
  166. trans[5] += - meanOutput.Y + meanInput.Y;
  167. //Restore values
  168. for (int i = 0; i < inputs.Count; i++) 
  169. {
  170. inputs[i].X += meanInput.X;
  171. inputs[i].Y += meanInput.Y;
  172. outputs[i].X += meanOutput.X;
  173. outputs[i].Y += meanOutput.Y;
  174. }
  175. //Calculate s0
  176. double s0=0;
  177. for (int i = 0; i < inputs.Count; i++) 
  178. {
  179. double x = inputs[i].X * trans[0] + inputs[i].Y * trans[1] + trans[2];
  180. double y = inputs[i].X * trans[3] + inputs[i].Y * trans[4] + trans[5];
  181. s0 += Math.Pow(x-outputs[i].X,2) + Math.Pow(y-outputs[i].Y,2);
  182. }
  183. trans[6] = Math.Sqrt(s0) / (inputs.Count);
  184. return trans;
  185. }
  186. /// <summary>
  187. /// Calculates the four helmert transformation parameters {a,b,c,d} and the sum of the squares of the residuals (s0)
  188. /// </summary>
  189. /// <remarks>
  190. /// <para>
  191. /// a,b defines scale vector 1 of coordinate system, d,e scale vector 2.
  192. /// c,f defines offset.
  193. /// </para>
  194. /// <para>
  195. /// Converting from input (X,Y) to output coordinate system (X',Y') is done by:
  196. /// X' = a*X + b*Y + c, Y' = -b*X + a*Y + d
  197. /// </para>
  198. /// <para>This is a transformation initially based on the affine transformation but slightly simpler.</para>
  199. /// </remarks>
  200. /// <returns>Array with the four transformation parameters, and sum of squared residuals: a,b,c,d,s0</returns>
  201. public double[] GetHelmertTransformation() 
  202. {
  203. if (inputs.Count < 2)
  204. throw(new System.Exception("At least 2 measurements required to calculate helmert transformation"));
  205. //double precision isn't always enough. Lets subtract some mean values and add them later again:
  206. //Find approximate center values:
  207. SharpMap.Geometries.Point meanInput = new SharpMap.Geometries.Point(0, 0);
  208. SharpMap.Geometries.Point meanOutput = new SharpMap.Geometries.Point(0, 0);
  209. for (int i = 0; i < inputs.Count; i++) 
  210. {
  211. meanInput.X += inputs[i].X;
  212. meanInput.Y += inputs[i].Y;
  213. meanOutput.X += outputs[i].X;
  214. meanOutput.Y += outputs[i].Y;
  215. }
  216. meanInput.X = Math.Round(meanInput.X / inputs.Count);
  217. meanInput.Y = Math.Round(meanInput.Y / inputs.Count);
  218. meanOutput.X = Math.Round(meanOutput.X / inputs.Count);
  219. meanOutput.Y = Math.Round(meanOutput.Y / inputs.Count);
  220. double b00=0;
  221. double b02=0;
  222. double b03=0;
  223. double[] t = new double[4];
  224. for (int i = 0; i < inputs.Count; i++) 
  225. {
  226. //Subtract mean values
  227. inputs[i].X -= meanInput.X;
  228. inputs[i].Y -= meanInput.Y;
  229. outputs[i].X -= meanOutput.X;
  230. outputs[i].Y -= meanOutput.Y;
  231. //Calculate summed values
  232. b00 += Math.Pow(inputs[i].X,2) + Math.Pow(inputs[i].Y,2);
  233. b02 -= inputs[i].X;
  234. b03 -= inputs[i].Y;
  235. t[0] += -(inputs[i].X*outputs[i].X) - (inputs[i].Y*outputs[i].Y);
  236. t[1] += -(inputs[i].Y*outputs[i].X) + (inputs[i].X*outputs[i].Y);
  237. t[2] += outputs[i].X;
  238. t[3] += outputs[i].Y;
  239. }
  240. double frac = 1 / (-inputs.Count * b00 + Math.Pow(b02, 2) + Math.Pow(b03, 2));
  241. double[] result = new double[5];
  242. result[0] = (-inputs.Count * t[0] + b02 * t[2] + b03 * t[3]) * frac;
  243. result[1] = (-inputs.Count * t[1] + b03 * t[2] - b02 * t[3]) * frac;
  244. result[2] = (b02*t[0]+b03*t[1]-t[2]*b00) * frac + meanOutput.X;
  245. result[3] = (b03*t[0]-b02*t[1]-t[3]*b00) * frac + meanOutput.Y;
  246. //Restore values
  247. for (int i = 0; i < inputs.Count; i++) 
  248. {
  249. inputs[i].X += meanInput.X;
  250. inputs[i].Y += meanInput.Y;
  251. outputs[i].X += meanOutput.X;
  252. outputs[i].Y += meanOutput.Y;
  253. }
  254. //Calculate s0
  255. double s0=0;
  256. for (int i = 0; i < inputs.Count; i++) 
  257. {
  258. double x = inputs[i].X * result[0] + inputs[i].Y * result[1] + result[2];
  259. double y = -inputs[i].X * result[1] + inputs[i].Y * result[0] + result[3];
  260. s0 += Math.Pow(x-outputs[i].X,2) + Math.Pow(y-outputs[i].Y,2);
  261. }
  262. result[4] = Math.Sqrt(s0) / (inputs.Count);
  263. return result;
  264. }
  265. /// <summary>
  266. /// Creates an n x m matrix of doubles
  267. /// </summary>
  268. /// <param name="n">width of matrix</param>
  269. /// <param name="m">height of matrix</param>
  270. /// <returns>n*m matrix</returns>
  271. private double[][] CreateMatrix(int n, int m) 
  272. {
  273. double[][] N = new double[n][];
  274. for(int i=0;i<n;i++) 
  275. {
  276. N[i] = new double[m];
  277. }
  278. return N;
  279. }
  280. }
  281. }