Advertisement
Guest User

Untitled

a guest
Sep 20th, 2017
84
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.89 KB | None | 0 0
  1. using System;
  2.  
  3. using System.Collections.Generic;
  4.  
  5. using System.Linq;
  6.  
  7. using System.Text;
  8.  
  9. using Emgu.CV;
  10.  
  11. using Emgu.CV.Structure;
  12.  
  13. using MathNet.Numerics.LinearAlgebra;
  14.  
  15. using System.Drawing;
  16.  
  17. using Emgu.CV.Features2D;
  18.  
  19. using System.Windows.Forms;
  20.  
  21.  
  22.  
  23. namespace SS11P04ImageStitching
  24.  
  25. {
  26.  
  27. class SS11P04IS_Loesung : SS11P04IS_Aufgabe
  28.  
  29. {
  30.  
  31. /// <summary>
  32.  
  33. /// Bitte hier Namen und Matrikelnummern eintragen
  34.  
  35. /// </summary>
  36.  
  37. public SS11P04IS_Loesung()
  38.  
  39. {
  40.  
  41. authors = new String[2] { "Sebastian Lichtenfels", "Andreas Mantler" };
  42.  
  43. matrikelNummern = new int[2] { 366070, 363220 };
  44.  
  45. //Nur der Neugier halber
  46.  
  47. OS = OS.Linux;
  48.  
  49. }
  50.  
  51.  
  52.  
  53. /// <summary>
  54.  
  55. /// Zu implementieren: Berechnet eine Gerade y=mx+b zu den Punkten samples mittels RANSAC
  56.  
  57. /// </summary>
  58.  
  59. /// <param name="samples">Die Datenpunkte (müssen mindestens 2 sein)</param>
  60.  
  61. /// <param name="steps">Anzahl der Schritte</param>
  62.  
  63. /// <param name="modelThresh">Threshold bis wohin ein Punkt nicht als grober Fehler gilt (der Abstandsbegriff wird durch die Methode distance definiert)</param>
  64.  
  65. /// <returns>Eine 1x2-Matrix [m, b]</returns>
  66.  
  67. public double[,] ransac(Match[] samples, int steps, double modelThresh)
  68.  
  69. {
  70.  
  71. Random rng = new Random();
  72.  
  73. Matrix A, B, X;
  74.  
  75.  
  76.  
  77. // Anzahl der Samples:
  78.  
  79. int n = samples.Length;
  80.  
  81.  
  82.  
  83. // Speicherplätze für ConsensusSets:
  84.  
  85. // das aktuell verarbeitete; das mit bisher am meisten Elementen
  86.  
  87. List<Match> currConsensusSet, biggestConsensusSet = new List<Match>();
  88.  
  89.  
  90.  
  91. for (int i = 0; i < steps; i++)
  92.  
  93. {
  94.  
  95. // die zufällig gewählten Punkte holen:
  96.  
  97. List<Match> matches = new List<Match>();
  98.  
  99. A = new Matrix(3, 3);
  100.  
  101. B = new Matrix(3, 3);
  102.  
  103. for(int j = 0; j < 3; j++)
  104.  
  105. {
  106.  
  107. int index;
  108.  
  109. do
  110.  
  111. index = rng.Next(n);
  112.  
  113. while(matches.Contains(samples[index]));
  114.  
  115. matches.Add(samples[index]);
  116.  
  117. A[j, 0] = samples[index].p1.X;
  118.  
  119. A[j, 1] = samples[index].p1.Y;
  120.  
  121. A[j, 2] = 1;
  122.  
  123. B[j, 0] = samples[index].p2.X;
  124.  
  125. B[j, 1] = samples[index].p2.Y;
  126.  
  127. B[j, 2] = 1;
  128.  
  129. }
  130.  
  131.  
  132.  
  133. //lasse Schritte, in denen A singulär ist, aus, passiert aber sowieso fast nie
  134.  
  135. try
  136.  
  137. {
  138.  
  139. X = B.Solve(A);
  140.  
  141. X.Transpose();
  142.  
  143. }
  144.  
  145. catch { continue; }
  146.  
  147.  
  148.  
  149. // Bestimmen des ConsensusSets:
  150.  
  151. currConsensusSet = new List<Match>();
  152.  
  153. for (int j = 0; j < n; j++)
  154.  
  155. {
  156.  
  157. // Punkt holen
  158.  
  159. Match c = samples[j];
  160.  
  161. PointF p1 = new PointF();
  162.  
  163. p1.X = (float)(X[0, 0] * c.p2.X + X[0, 1] * c.p2.Y + X[0, 2]);
  164.  
  165. p1.Y = (float)(X[1, 0] * c.p2.X + X[1, 1] * c.p2.Y + X[1, 2]);
  166.  
  167. float z = (float)(X[2, 0] + X[2, 1] + X[2, 2]);
  168.  
  169. p1.X /= z;
  170.  
  171. p1.Y /= z;
  172.  
  173.  
  174.  
  175. double e = Math.Sqrt((c.p1.X - p1.X) * (c.p1.X - p1.X) + (c.p1.Y - p1.Y) * (c.p1.Y - p1.Y));
  176.  
  177.  
  178.  
  179. // Wenn der Abstand zur Geraden durch p0 und p1 noch im Rahmen liegt, hinzufügen:
  180.  
  181. if (e < modelThresh)
  182.  
  183. currConsensusSet.Add(c);
  184.  
  185. }
  186.  
  187.  
  188.  
  189. // Es wird das ConsensusSet gesucht, das die meisten Elemente enthält:
  190.  
  191. if (currConsensusSet.Count > biggestConsensusSet.Count)
  192.  
  193. biggestConsensusSet = currConsensusSet;
  194.  
  195. }
  196.  
  197.  
  198.  
  199.  
  200.  
  201. A = new Matrix(biggestConsensusSet.Count, 3);
  202.  
  203. B = new Matrix(biggestConsensusSet.Count, 3);
  204.  
  205. for(int i = 0; i < biggestConsensusSet.Count; i++)
  206.  
  207. {
  208.  
  209. A[i, 0] = biggestConsensusSet[i].p1.X;
  210.  
  211. A[i, 1] = biggestConsensusSet[i].p1.Y;
  212.  
  213. A[i, 2] = 1;
  214.  
  215. B[i, 0] = biggestConsensusSet[i].p2.X;
  216.  
  217. B[i, 1] = biggestConsensusSet[i].p2.Y;
  218.  
  219. B[i, 2] = 1;
  220.  
  221. }
  222.  
  223. X = B.Solve(A);
  224.  
  225. X.Transpose();
  226.  
  227. return X.CopyToArray();
  228.  
  229. }
  230.  
  231.  
  232.  
  233. /// <summary>
  234.  
  235. /// Findet in den korrespondierenden Punktepaaren correspondences eine Homographie
  236.  
  237. /// </summary>
  238.  
  239. /// <param name="imageL">Das linke Bild</param>
  240.  
  241. /// <param name="imageR">Das rechte Bild</param>
  242.  
  243. /// <param name="correspondences">Menge von korrespondierenden Punktepaaren</param>
  244.  
  245. /// <returns>Homographiematrix, die die Transformation zwischen linkem und rechtem Bild beschreibt</returns>
  246.  
  247. public override double[,] ransacH(
  248.  
  249. Image<Bgr, byte> imageL,
  250.  
  251. Image<Bgr, byte> imageR,
  252.  
  253. Match[] correspondences,
  254.  
  255. int ransacSteps,
  256.  
  257. double ransacThresh)
  258.  
  259. {
  260.  
  261. return ransac(correspondences, ransacSteps, ransacThresh);
  262.  
  263. }
  264.  
  265.  
  266.  
  267.  
  268.  
  269. /// <summary>
  270.  
  271. /// Beinhaltet die eigentliche Transformation. Benutzt zum Transformieren eure Implementierung von
  272.  
  273. /// transformPerspectiveBL aus P1. Kommentiert ausführlich, nach welchem Mechanismus ihr
  274.  
  275. /// die Bilder überblendet
  276.  
  277. /// </summary>
  278.  
  279. /// <param name="imageL">Das linke Bild</param>
  280.  
  281. /// <param name="imageR">Das rechte Bild</param>
  282.  
  283. /// <param name="H">Die Transformation zwischen linkem und rechtem Bild</param>
  284.  
  285. /// <returns>Das fertige Panoama</returns>
  286.  
  287. public override Image<Bgr, byte> blend(Image<Bgr, byte> imageL, Image<Bgr, byte> imageR, double[,] H)
  288.  
  289. {
  290.  
  291. Image<Bgr, byte> both = new Image<Bgr, byte>(imageL.Width+imageR.Width, Math.Max(imageL.Height, imageR.Height));
  292.  
  293.  
  294.  
  295.  
  296.  
  297. int rh = (int)(imageL.Width + H[0, 2]);
  298.  
  299. H[0, 2] = 0;
  300.  
  301.  
  302.  
  303. imageR = imageR.WarpPerspective<double>(
  304.  
  305. new Emgu.CV.Matrix<double>(H), //Matrixklasse von Emgu
  306.  
  307. Emgu.CV.CvEnum.INTER.CV_INTER_LINEAR,
  308.  
  309. Emgu.CV.CvEnum.WARP.CV_WARP_DEFAULT,
  310.  
  311. new Bgr(0, 255, 0)
  312.  
  313. ).Convert<Bgr, byte>();
  314.  
  315.  
  316.  
  317. for(int y = 0; y < imageL.Height; y++)
  318.  
  319. {
  320.  
  321. for(int x = 0; x < imageL.Width; x++)
  322.  
  323. {
  324.  
  325. if(imageL.Data[y, x, 0] == 0 && imageL.Data[y, x, 1] == 0 && imageL.Data[y, x, 2] == 0) continue;
  326.  
  327. /*float r = 0, g = 0, b = 0;
  328.  
  329. if(lh+x > imageL.Width)
  330.  
  331. {
  332.  
  333. r = (imageL.Data[y, x, 0] - both.Data[y, x+lh, 0]);
  334.  
  335. g = (imageL.Data[y, x, 1] - both.Data[y, x+lh, 1]);
  336.  
  337. b = (imageL.Data[y, x, 2] - both.Data[y, x+lh, 2]);
  338.  
  339. if(r*r + g*g + b*b < 20) break;
  340.  
  341. float f = ((float)(lh+x - imageL.Width)) / ((float)lh);
  342.  
  343. r *= f;
  344.  
  345. g *= f;
  346.  
  347. b *= f;
  348.  
  349. }*/
  350.  
  351. both.Data[y, x, 0] = (byte)(imageL.Data[y, x, 0] - 0);
  352.  
  353. both.Data[y, x, 1] = (byte)(imageL.Data[y, x, 1] - 0);
  354.  
  355. both.Data[y, x, 2] = (byte)(imageL.Data[y, x, 2] - 0);
  356.  
  357. }
  358.  
  359. }
  360.  
  361.  
  362.  
  363. for(int y = 0; y < imageR.Height; y++)
  364.  
  365. {
  366.  
  367. for(int x = 0; x < imageR.Width; x++)
  368.  
  369. {
  370.  
  371. if(imageR.Data[y, x, 0] == 0 && imageR.Data[y, x, 1] == 0 && imageR.Data[y, x, 2] == 0) continue;
  372.  
  373. both.Data[y, rh+x, 0] = imageR.Data[y, x, 0];
  374.  
  375. both.Data[y, rh+x, 1] = imageR.Data[y, x, 1];
  376.  
  377. both.Data[y, rh+x, 2] = imageR.Data[y, x, 2];
  378.  
  379. }
  380.  
  381. }
  382.  
  383.  
  384.  
  385. return both;
  386.  
  387. }
  388.  
  389.  
  390.  
  391. private const int patchsize = 2;
  392.  
  393.  
  394.  
  395. /// <summary>
  396.  
  397. /// Eure Implementierung der Funktion aus P3. Wird nicht korrigiert.
  398.  
  399. /// </summary>
  400.  
  401. /// <param name="image"></param>
  402.  
  403. /// <param name="a"></param>
  404.  
  405. /// <returns></returns>
  406.  
  407. public override PointF[] findFeaturePoints(Image<Bgr, byte> image, double a)
  408.  
  409. {
  410.  
  411.  
  412.  
  413. //zu Graubild konvertieren
  414.  
  415. Image<Gray, byte> grayimage = image.Convert<Gray, byte>();
  416.  
  417.  
  418.  
  419. List<PointF> points = new List<PointF>(); //Liste der FeaturePoints
  420.  
  421. float[,,] d = new float[grayimage.Height, grayimage.Width, 2]; //partielle Ableitungen (nach x und y) für jeden Pixel. Schema: d[row, column, {x=0 or y=1}]
  422.  
  423.  
  424.  
  425. //partielle Ableitungen für jeden Pixel bestimmen mit diskretene Werten in einer patchsize-Umgebung um den jeweiligen Pixel
  426.  
  427. //patchsize-Umgebung meint hierbei ein Intervall [p.X-patchsize, p.X+patchsize] für die Ableitung nach x und entsprechend [p.Y-patchsize, p.Y+patchsize] für y für alle Pixel p
  428.  
  429. //Der Rand des Bildes (Randsträke = patchsize) wird dabei ausgelassen
  430.  
  431. for(int r = patchsize; r < grayimage.Height - patchsize; r++)
  432.  
  433. {
  434.  
  435. for(int c = patchsize; c < grayimage.Width - patchsize; c++)
  436.  
  437. {
  438.  
  439. float fx = 0, fy = 0;
  440.  
  441. //Alle Pixel in der patchsize-Umgebung
  442.  
  443. for(int i = -patchsize; i <= patchsize; i++)
  444.  
  445. {
  446.  
  447. if(i == 0) continue; //Den Pixel selbst auslassen
  448.  
  449. fy += (float)(image.Data[r+i, c, 0] - image.Data[r, c, 0])/(float)i;
  450.  
  451. fx += (float)(image.Data[r, c+i, 0] - image.Data[r, c, 0])/(float)i;
  452.  
  453. }
  454.  
  455. //Die errechneten Ableitungen abspeichern
  456.  
  457. d[r, c, 0] = fx;
  458.  
  459. d[r, c, 1] = fy;
  460.  
  461. }
  462.  
  463. }
  464.  
  465.  
  466.  
  467. float[,] R = new float[grayimage.Height, grayimage.Width]; //Scalar interest measures (nach Harris) für jeden Pixel
  468.  
  469. float mean = 0; //Durchschnitt aller scalar interest measures (SIM)
  470.  
  471. for(int r = patchsize; r < grayimage.Height - patchsize; r++)
  472.  
  473. {
  474.  
  475. for(int c = patchsize; c < grayimage.Width - patchsize; c++)
  476.  
  477. {
  478.  
  479. // Die Einträge der Matrix M. M01 = M10, deshab nur einmal gespeichert.
  480.  
  481. //Aus Performance-Gründen wird nicht die Klasse Matrix verwendet
  482.  
  483. float M00 = 0, M01 = 0, M11 = 0;
  484.  
  485. //Als Nachbarschaft, wähle eine patchsize*patchsize-Nachbarschaft um den aktuellen Pixel
  486.  
  487. for(int nr = -patchsize; nr <= patchsize; nr++)
  488.  
  489. {
  490.  
  491. for(int nc = -patchsize; nc <= patchsize; nc++)
  492.  
  493. {
  494.  
  495. //Berechnung der Matrix-Einträge nach Vorlesung
  496.  
  497. float fx = d[r+nr, c+nc, 0];
  498.  
  499. float fy = d[r+nr, c+nc, 1];
  500.  
  501. M00 += fx*fx;
  502.  
  503. M11 += fy*fy;
  504.  
  505. M01 += fy*fx;
  506.  
  507. }
  508.  
  509. }
  510.  
  511. //Berechnung des SIM
  512.  
  513. float trace = M00 + M11; //Spur der Matrix M
  514.  
  515. R[r, c] = M00*M11 - M01*M01 - (float)a*trace*trace; //= det(M) - a * spur(M)^2
  516.  
  517. mean += R[r, c]; //Aufsummieren aller SIM
  518.  
  519. }
  520.  
  521. }
  522.  
  523. //Teilen durch Anzahl der Pixel (dies ist nicht der genaue Durchschnittswert, da der Rand des Bildes nicht berücksichtigt wird, aber praktisch gesehen ein guter Schwellenwert für Feature Points)
  524.  
  525. mean /= grayimage.Height * grayimage.Width;
  526.  
  527.  
  528.  
  529. //Diejenigen Pixel als FeaturePoints deklarieren, die über dem Schwellenwert (=mean) liegen und die ein lokales Maximum in ihrer patchsize*patchsize-Nachbarschaft darstellen
  530.  
  531. for(int r = patchsize; r < grayimage.Height - patchsize; r++)
  532.  
  533. {
  534.  
  535. for(int c = patchsize; c < grayimage.Width - patchsize; c++)
  536.  
  537. {
  538.  
  539. if(R[r, c] > mean)
  540.  
  541. {
  542.  
  543. bool peak = true;
  544.  
  545. for(int nr = -patchsize; nr <= patchsize; nr++)
  546.  
  547. {
  548.  
  549. for(int nc = -patchsize; nc <= patchsize; nc++)
  550.  
  551. {
  552.  
  553. if(R[r+nr, c+nc] > R[r, c]) //Für alle Pixel der Nachbarschaft prüfen, ob sie größeren SIM haben als der aktuelle Pixel. Ist dies der Fall, so ist der aktuelle Pixel kein lokales Maximum und daher auch kein Feature Point
  554.  
  555. peak = false;
  556.  
  557. }
  558.  
  559. }
  560.  
  561. if (peak)
  562.  
  563. points.Add(new PointF(c, r));
  564.  
  565. }
  566.  
  567. }
  568.  
  569. }
  570.  
  571. //Die iste der Feature Points als Array konvertiert zurückgeben
  572.  
  573. return points.ToArray();
  574.  
  575. }
  576.  
  577.  
  578.  
  579. // Eine einfache clampfunktion, um Bildkoordinaten an den Rändern an ihre Kanten zu clampen
  580.  
  581. private int clamp(int v, int min, int max)
  582.  
  583. {
  584.  
  585. return Math.Min(max, Math.Max(min, v));
  586.  
  587. }
  588.  
  589.  
  590.  
  591. /// <summary>
  592.  
  593. /// Eure Implementierung der Funktion aus P3. Wird nicht korrigiert.
  594.  
  595. /// </summary>
  596.  
  597. /// <param name="image"></param>
  598.  
  599. /// <param name="a"></param>
  600.  
  601. /// <returns></returns>
  602.  
  603. public override Match[] findCorrespondences(Image<Bgr, byte> refImage, Image<Bgr, byte> tarImage, System.Drawing.PointF[] refFeatures, System.Drawing.PointF[] tarFeatures, int searchRange, int k)
  604.  
  605. {
  606.  
  607.  
  608.  
  609. // Listen für Zwischenergebnisse
  610.  
  611. List<Match> matches = new List<Match>();
  612.  
  613. List<float> distances = new List<float>();
  614.  
  615.  
  616.  
  617. // Die Suchentfernung von der Mitte ist nurnoch halb so groß...
  618.  
  619. float searchRadius = searchRange/2.0f;
  620.  
  621.  
  622.  
  623. // Für jeden Referenzpunkt...
  624.  
  625. foreach(var refP in refFeatures)
  626.  
  627. {
  628.  
  629. // Variable für den momentan kleinsten gefundenen SSD-Pixel-"Abstand"
  630.  
  631. float minDifference = float.MaxValue;
  632.  
  633. // variable für den momentan besten gefundenen Korrespondenten Punkt
  634.  
  635. PointF best = tarFeatures[0];
  636.  
  637.  
  638.  
  639. // Für jeden Targetpunkt
  640.  
  641. foreach(var tarP in tarFeatures)
  642.  
  643. {
  644.  
  645. // Prüfe, ob sich dieser Targetpunkt in der searchRange befindet
  646.  
  647. // Hier wird ausgenutzt, dass die Punkte der höhe nach abgespeichert wurden
  648.  
  649. if(tarP.Y < refP.Y - searchRadius)
  650.  
  651. continue;
  652.  
  653. if(tarP.Y > refP.Y + searchRadius)
  654.  
  655. break;
  656.  
  657.  
  658.  
  659. // Der aktuelle SSD-"Abstand" der Nachbarschaft
  660.  
  661. float sumDifference = 0;
  662.  
  663.  
  664.  
  665. // Für die k-Nachbarschaft vom ref- und tar-Punkt:
  666.  
  667. for(int c = -k; c <= k; c+=2)
  668.  
  669. {
  670.  
  671. // momentane Pixelkoordinaten berechnen:
  672.  
  673. int rcol = clamp((int)refP.X+c, 0, refImage.Width-1);
  674.  
  675. int tcol = clamp((int)tarP.X+c, 0, tarImage.Width-1);
  676.  
  677. for(int r = -k; r <= k; r+=2)
  678.  
  679. {
  680.  
  681. // momentane Pixelkoordinaten berechnen:
  682.  
  683. int rrow = clamp((int)refP.Y+r, 0, refImage.Height-1);
  684.  
  685. int trow = clamp((int)tarP.Y+r, 0, tarImage.Height-1);
  686.  
  687.  
  688.  
  689. // beide Pixel holen:
  690.  
  691. byte rb = refImage.Data[rrow, rcol, 0];
  692.  
  693. byte rg = refImage.Data[rrow, rcol, 1];
  694.  
  695. byte rr = refImage.Data[rrow, rcol, 2];
  696.  
  697.  
  698.  
  699. byte tb = tarImage.Data[trow, tcol, 0];
  700.  
  701. byte tg = tarImage.Data[trow, tcol, 1];
  702.  
  703. byte tr = tarImage.Data[trow, tcol, 2];
  704.  
  705.  
  706.  
  707. // SSD-"Abstand" der aktuellen Pixel berechnen:
  708.  
  709. sumDifference += (tb-rb)*(tb-rb) + (tg-rg)*(tg-rg) + (tr-rr)*(tr-rr);
  710.  
  711. }
  712.  
  713. }
  714.  
  715.  
  716.  
  717. // Ist dieser Abstand kleiner/besser?
  718.  
  719. if(sumDifference < minDifference)
  720.  
  721. {
  722.  
  723. // Die besten Ergebnisse aktualisieren:
  724.  
  725. minDifference = sumDifference;
  726.  
  727. best = tarP;
  728.  
  729. }
  730.  
  731. }
  732.  
  733.  
  734.  
  735. // Ist dieser Abstand besser als ein Threshold (, der von k abhängt)?
  736.  
  737. if(minDifference < k*k*1024.0f)
  738.  
  739. {
  740.  
  741. // Dann diesen Match zu der Ergebnisliste hinzufügen
  742.  
  743. var m = new Match(refP, best);
  744.  
  745. matches.Add(m);
  746.  
  747. // Und einen Abstand zwischen den Punkten berechnen, um nachher Ausreißer rauszunehmen
  748.  
  749. distances.Add((m.p1.X-m.p2.X) * (m.p1.X-m.p2.X) + (m.p1.Y-m.p2.Y) * (m.p1.Y-m.p2.Y));
  750.  
  751. }
  752.  
  753. }
  754.  
  755.  
  756.  
  757. // Wir haben keine potenziellen Korrespondenzen gefunden
  758.  
  759. if(distances.Count == 0)
  760.  
  761. return new Match[0];
  762.  
  763.  
  764.  
  765. // Medianberechnung
  766.  
  767. List<float> sortedDistances = new List<float>(distances);
  768.  
  769. sortedDistances.Sort();
  770.  
  771. float distanceMedian = sortedDistances[distances.Count/2];
  772.  
  773.  
  774.  
  775. // Matches, die eine Entfernung haben, die um den Median liegt,
  776.  
  777. // (in Abhängigkeit von searchRange) zusammensuchen
  778.  
  779. List<Match> finalmatches = new List<Match>();
  780.  
  781. for(int i = 0; i < matches.Count; i++)
  782.  
  783. if(Math.Abs(distances[i]-distanceMedian) < searchRange * 64.0f)
  784.  
  785. finalmatches.Add(matches[i]);
  786.  
  787.  
  788.  
  789. // Endgültige Funde zurückgeben
  790.  
  791. return finalmatches.ToArray();
  792.  
  793. }
  794.  
  795. }
  796.  
  797. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement