diff --git a/MMVII/Doc/Doc2007.tex b/MMVII/Doc/Doc2007.tex index b1b6871a10..19028d9f75 100755 --- a/MMVII/Doc/Doc2007.tex +++ b/MMVII/Doc/Doc2007.tex @@ -1,5 +1,7 @@ \usepackage[english]{babel} %\usepackage[english,french]{babel} +\usepackage{algpseudocode} +\usepackage{algorithm} \usepackage{amsmath} \usepackage{amsfonts} \usepackage{mathtools} @@ -129,6 +131,9 @@ %\newcommand{\DeuxExtre}{\ensuremath{\nabla}} \newcommand{\RefFantome}{{\bf ?2Def?}} \newcommand{\TOMODIF}[1]{\textcolor{red}{\textbf{#1}}} + +\newcommand{\TOIMPROVE}[1]{\textcolor{red}{\textbf{Possible improvement (??)} : [#1]}} + \newcommand{\PourLecteurAverti}{{\Large \bf \emph{Ce paragraphe peut facilement \^etre omis en premi\`ere lecture.}}} @@ -151,6 +156,8 @@ \newcommand{\doxy}{\emph{doxygen}} \newcommand{\MMNONE}{NONE} +%\includeonly{Methods/Line-Detection-Theory} + %--------------------------------------------- \begin{document} \selectlanguage{english} @@ -188,6 +195,7 @@ \part{Methodologies} \include{Methods/PushBroomSensor-Theory} \include{Methods/PoseEstimation} \include{Methods/CodedTarget-Theory} +\include{Methods/Line-Detection-Theory} \include{Methods/AimeDesc} \include{Methods/TiePoints} \include{Methods/Vrac} diff --git a/MMVII/Doc/Methods/ImagesFils/Crop-Hough.jpg b/MMVII/Doc/Methods/ImagesFils/Crop-Hough.jpg new file mode 100644 index 0000000000..125e859351 Binary files /dev/null and b/MMVII/Doc/Methods/ImagesFils/Crop-Hough.jpg differ diff --git a/MMVII/Doc/Methods/ImagesFils/FullHough.jpg b/MMVII/Doc/Methods/ImagesFils/FullHough.jpg new file mode 100644 index 0000000000..9fe23c0901 Binary files /dev/null and b/MMVII/Doc/Methods/ImagesFils/FullHough.jpg differ diff --git a/MMVII/Doc/Methods/ImagesFils/GradInit.jpg b/MMVII/Doc/Methods/ImagesFils/GradInit.jpg new file mode 100644 index 0000000000..ba497c7d85 Binary files /dev/null and b/MMVII/Doc/Methods/ImagesFils/GradInit.jpg differ diff --git a/MMVII/Doc/Methods/ImagesFils/GradNonLinear.jpg b/MMVII/Doc/Methods/ImagesFils/GradNonLinear.jpg new file mode 100644 index 0000000000..f37f960831 Binary files /dev/null and b/MMVII/Doc/Methods/ImagesFils/GradNonLinear.jpg differ diff --git a/MMVII/Doc/Methods/ImagesFils/ImagOri.jpg b/MMVII/Doc/Methods/ImagesFils/ImagOri.jpg new file mode 100644 index 0000000000..1b0649aebd Binary files /dev/null and b/MMVII/Doc/Methods/ImagesFils/ImagOri.jpg differ diff --git a/MMVII/Doc/Methods/ImagesFils/MaxLocGrad.jpg b/MMVII/Doc/Methods/ImagesFils/MaxLocGrad.jpg new file mode 100644 index 0000000000..88ee4bc18c Binary files /dev/null and b/MMVII/Doc/Methods/ImagesFils/MaxLocGrad.jpg differ diff --git a/MMVII/Doc/Methods/ImagesFils/NeighMaxLoc.jpg b/MMVII/Doc/Methods/ImagesFils/NeighMaxLoc.jpg new file mode 100644 index 0000000000..5ec5d1a2ed Binary files /dev/null and b/MMVII/Doc/Methods/ImagesFils/NeighMaxLoc.jpg differ diff --git a/MMVII/Doc/Methods/ImagesFils/ProfilNotOk.jpg b/MMVII/Doc/Methods/ImagesFils/ProfilNotOk.jpg new file mode 100644 index 0000000000..8f07095fcc Binary files /dev/null and b/MMVII/Doc/Methods/ImagesFils/ProfilNotOk.jpg differ diff --git a/MMVII/Doc/Methods/ImagesFils/ProfilOK.jpg b/MMVII/Doc/Methods/ImagesFils/ProfilOK.jpg new file mode 100644 index 0000000000..f2e6d50e3f Binary files /dev/null and b/MMVII/Doc/Methods/ImagesFils/ProfilOK.jpg differ diff --git a/MMVII/Doc/Methods/Line-Detection-Theory.tex b/MMVII/Doc/Methods/Line-Detection-Theory.tex new file mode 100644 index 0000000000..f434693975 --- /dev/null +++ b/MMVII/Doc/Methods/Line-Detection-Theory.tex @@ -0,0 +1,514 @@ +\chapter{Lines detection} +\label{Chap:LineDetect} + + +%----------------------------------------------------------------------- +%----------------------------------------------------------------------- +%----------------------------------------------------------------------- + +\section{Introduction} + +%----------------------------------------------------------------------- + +\subsection{Context and scope} + +This chapter describes the method developped for line detection in \PPP. +These methods have been developed in the case of wire detection in the context +of collaboration with CERN for magnet alignment. + +At the time being, the method and describtion is higly oriented to this use case, +in fact it is more or less limited to this case for now but the different elementary +part could be easily re-used to process more general cases. This chapter mixes +algorithmic specification, developer and end-user . + + +%----------------------------------------------------------------------- +\subsection{Overview of the method} + +The main steps of line detection are the following : + +\begin{itemize} + \item {\bf Image Space computation} + \begin{itemize} + \item compute de gradient of image + \item extract the local maxima of gradient, in the direction of gradient + \item refine de position (transform integer pixel coordinate in refined real coordinates); + \end{itemize} + \item {\bf Hough space computaion} + \begin{itemize} + \item compute the hough transform (at this step, each wire generate two \emph{anti parallel} lines); + \item extract the local maxima in hough space , in integer coordinates, and refined them, + \end{itemize} + + \item {\bf Back to image space computation} + \begin{itemize} + \item \emph{back transformate} hough point in line, and refine their coordinate using the initial gradient point; + \item match the lines for computing pairs of antiparallel lines, and extract caracteristic of central lines + including quality criteria (parallelism, width, radiometric homogeneity); + \item select the \emph{best} lines by comparison of quality criteria. + \end{itemize} +\end{itemize} + +In the process we have to take into account image distorsion, because this in \emph{un-distorded} coordinates, +that the wire is a straight line. As we dont want to make an image ressampling +\footnote{because it cannot be used with fish-eye and limit the image quality} , all along the process we +will have to make frequent back and forth between initial and undistorted coorinates. For the sake +of simplicity, we will no longer mention them here. + +%----------------------------------------------------------------------- +\subsection{Localisation of the code} + +The command for extracting line is {\tt ExtractLine}, the code of the command is +located in {\tt cAppliExtractLine.cpp}. The algorithms used in this command, +can mainly be found in the folder {\tt src/ImagesInfoExtract} : + +\begin{itemize} + \item {\tt cImGradWithN.cpp} code for computing gradient (sobel, deriche), + the method calls some code of file {\tt include/MMVII\_TplGradImFilter.h}; + + + \item {\tt cHoughTransform.cpp} code for computing the hough transform + + \item {\tt cAppliExtractLine.cpp}, code for extracting lines, calling previous + facilities (gradient, hough ); +\end{itemize} + +%----------------------------------------------------------------------- +%----------------------------------------------------------------------- +%----------------------------------------------------------------------- + +\section{Computation in image space} + +\subsection{Compute de gradient of image} +There is $2$ option for computing the gradient : + +\begin{itemize} + \item the \emph{Deriche} gradient + \item the \emph{Sobel} gradient +\end{itemize} + +The sobel method is significantly faster, and apparently gives equivalent result for our purpose +\footnote{consquently, for now the deriche variant is no longer completely supported}. +The method for computing \emph{Sobel} filter can be found in {\tt MMVII\_TplGradImFilter.h}. + +Also basic implementation of \emph{Sobel} is quite obvious, some precaution have been +taken for making it as effeciency as possible. The low level optimized services are offered +in class {\tt cTabulateGrad} : + +\begin{itemize} + \item for fast computing of norm of gradient, its value is computed + for any possible value of $G_x,G_y$ and stored in a table + (for example a $256 \times 256$ table if we know that $G_x, G_y \in [128,-128[^2 $); + + \item {\tt cTabulateGrad(int aVMax);} is the constructor, the $aVMax$ value + is used to limit the number of possible values in tabulation; + + \item the method {\tt ComputeSobel} store in $2$ image $G_x$ and $G_y$ the value + of gradient in $x$ and $y$; it takes a parameter {\tt Div} for dynamic of + storing, all values of $G_x,G_y$ are divied by {\tt Div}. + +\end{itemize} + +Figure~\ref{Fig:Line:Grad} present the result of \emph{Sobel} gradient on a small image +containing a wire. + + +\begin{figure} +\centering + \includegraphics[width=4cm]{Methods/ImagesFils/ImagOri.jpg } + \includegraphics[width=4cm]{Methods/ImagesFils/GradInit.jpg } + \includegraphics[width=4cm]{Methods/ImagesFils/GradNonLinear.jpg } + \caption{An extract of image, the sobel gradient, non linear radiometric enhance of gradient} +\label{Fig:Line:Grad} +\end{figure} + +%----------------------------------------------------------------------- + +\subsection{Selection of local maxima of gradient} + +\label{Line:LocMaxGrad} + +Extracting the local maxima of the gradient in the direction of gradient is a very classical pre-processing +in computation of edge detection. But there is some precuation to tack into account here because : + +\begin{itemize} + \item on want side we want to select point that are maxima on sufficiently large neighboorhoud + to have an efficient pre-filter (the point selected will be used for hough transform which + can be relatively long); + + \item on the other side, we must tacke into account that for each line we want to detect, there exist + a close anti-parallel line, that will have a module of gradient comparable; due to noise, + sampling, background, this anti parallel line may eliminate the point. +\end{itemize} + +To avoid elimination of many points by their macthing anti parallel line, we proceed this way, +illustrated by left schema of figure~\ref{Fig:Line:MaxLocGrad} : + +\begin{itemize} + \item let $P$ be a pixel of image were gradient is $\vec G$ + + \item $\rho_0$ and $\rho_1$ be two thresholds, low and hig, of size of + and $\theta$ be angular threshold + (value $\rho_0=1$, $\rho_1=8$ and $\theta=\frac{\pi}{2}$ are used in {\tt ExtractLine}) + + \item we define $N_1$ the neighbourhoud defined by point $Q$ with $\vec u = \overrightarrow{PQ}$ such + that $ < \rho_1$ and $ | \widehat{\vec G \vec{u}} | < \frac{\theta}{2} $ + + \item idem for $N_0$ + + + \item we define also $N_1^+$ by $N_1^+ = \{Q \in N_1 / \vec u . \vec G >0\} $ + (and similarly $<0$ for $N_1^-$). + \footnote{if the the wire is black, instide of white, the definition + of $N_1 ^-$ and $N_1 ^+$ are swapped} + +\end{itemize} + +Now, to select a point as a local maxima of the gradient we require that the following condition are +all satisfied : + +\begin{itemize} + \item $|\vec G(P)| \gg |\vec G(Q)| \forall Q \in N_0$ , in the \emph{small} neighbourhood , there is no risk + to meet the anti-parallel line; + + \item $|\vec G(P)| \gg |\vec G(Q)| \forall Q \in N_1 ^-$ in the direction opoposed to gradient, there is + no risk to meet the anti-parallel line + + + \item $ (|\vec G(P)| \gg |\vec G(Q)|) or (\vec G(P) . \vec G(Q) <0 ) \forall Q \in N_1 ^+$ + +\end{itemize} + +Where the relation $ |\vec G(P)| \gg |\vec G(Q)| $ stands for : + +\begin{itemize} + \item $|\vec G(P)| > |\vec G(Q)| $ + \item or $(|\vec G(P)| == |\vec G(Q)|) and (x_P >x_Q) $ + \item or $(|\vec G(P)| = |\vec G(Q)|) and (x_P =x_Q) and (y_P=y_Q)$ +\end{itemize} + +This definition is made so, for mathematicall consitency, $\gg$ is a strict order relation. +The result of this selection is illustrated on left image of figure~\ref{Fig:Line:MaxLocGrad}. + +\begin{figure} +\centering + \includegraphics[width=6cm]{Methods/ImagesFils/NeighMaxLoc.jpg} + \includegraphics[width=6cm]{Methods/ImagesFils/MaxLocGrad.jpg} + \caption{An extract of image, the sobel gradient, non linear radiometric enhance of gradient} +\label{Fig:Line:MaxLocGrad} +\end{figure} + +On the tested images, with the used parameters, the proportion of pixels selected by this +procedure is less than $2\%$ of the initial set of pixels. + + +%----------------------------------------------------------------------- + +\subsection{Refinement of local maxima} +\label{Ref:Grad:Loc:Max} + +The previous selection return a set of pixels with integer coordinates, to have a better accuracy +their coordinates are then refined. The method used is the following , let: + +\begin{itemize} + \item $\vec g = \frac{\vec G}{|G|} $ be the unitary vector in direction of gradient; + + \item $G_{1} = |\vec G(P+\vec g)|$ the norm of gradient at $1$ pixel distance in direction of gradient; + \item $G_{-1} = |\vec G(P-\vec g)|$ and $G_0 = |\vec G(P)|$ +\end{itemize} + +Consider now the parabol $\mathcal {P}$ such that : $\mathcal {P}(-1) = G_{-1} $ , $\mathcal {P}(0) = G_{0} $ , +$\mathcal {P}(1) = G_{1} $. Except in degenerate case, this $\mathcal {P}$ has a single extremum , note it $a$, we +then set $P_r = P+ a\vec g $ , where $P_r$ is the refined point. On this simple bases, there is several +modification : + +\begin{itemize} + \item if we are in the degenerate case, or if the extremum is a minima we dont do anything; + \item if $|a| > 1 $ we dont do anything; + \item we make several iteration of this process ($2$ in the current code). +\end{itemize} + +The corresponding code can be found in the method {\tt cImGradWithN::RefinePos}. + + +\TOIMPROVE{ maybe try a metho that dont use gradient and its interpolation, like method computing directly + the gradent in the interpolation, with a derivale interpolator like sinc of cubic } + +%----------------------------------------------------------------------- +%----------------------------------------------------------------------- +%----------------------------------------------------------------------- + +\section{Computation in hough space} + +%----------------------------------------------------------------------- + +\subsection{Notation and reminder of the hough transform} + +Let $x,y$ be the set of pixel of the image. Let $D$ be a line parametrized +by $\rho, \theta$, such that : + +\begin{equation} + x,y \in D(\rho,\theta) \Leftrightarrow x \cos (\theta) + y \sin (\theta) = \rho \label{Line:EqRadon} +\end{equation} + +The hough-transform is used here for counting for each lines +the number of points through which it passes . It's very similar to radon +transform, it's somewhat just an inversion of the process : + +\begin{itemize} + \item in radon transform, we parse all the possible line,i we sample the line + and count the sum of the image (function ) on the line; + + \item in hough transform we parse all the pixel, and for each pixel we + sum the function in accumulator $H$ on all the lines that pass trough the point , + for this we use equation \ref{Line:EqRadon} to make the incrementation + of equation \ref{Line:EqHough}. + +\end{itemize} + +\begin{algorithm} +\caption{Hough incrementation of $H$ for $x,y$ with value $I$}\label{alg:hough} +\begin{algorithmic} +\State $K_t \gets NbT$ \Comment{$ NbT$ : number of sampling in teta} +\While{$K_t \neq 0$} + \State $\theta \gets \frac{2 K_t \pi}{NbT} $ + \State $\rho \gets x \cos(\theta) + y \sin(\theta) $ \Comment{This is a comment} + \State $H(\theta,\rho) += I$ +\EndWhile +\end{algorithmic} +\end{algorithm} + +%----------------------------------------------------------------------- + +\subsection{Oriented hough transform} + +In general case, hough transform is equivalent to radon transform and relatively +slow, if the image has a size $N \times N$ , the computation time is in $N^3$. +What make hough very popular is that it can be very efficient in the case where +for the majority of point we have $I(x,y) =0 $, because in this case we dont +need to enter in the loop~\ref{alg:hough}. +This is more a less what we do with the computation of local maxmima in \ref{Line:LocMaxGrad}, +instead of executing \ref{alg:hough} for all pixel, we do it for only the few \% of pixel +that were selected as local maxima. + +By the way, there is more to do, because when we compute the gradient we have not only +its module but also its direction $\alpha$, so it is coherent to suppose that if +a straight line of Hough/Radon-coordinate $\rho,\theta$ pass by this point we have $\theta \approx \alpha$. +Note, that doing that we consider \emph{oriented} line where direction are defined $\equiv 2 \pi$ +and not single line defined $\equiv \pi$; which is rather an advantage if we want to +extract both each line and its antiparallel homologous they become distant in Hough/Radon space. + +So for each point, in algoritm~\ref{alg:hough}, +due to estimation $\alpha$ of $\theta$, we can reduce the initial +interval $[0,2\pi]$ to $[\alpha-\delta,\alpha+\delta]$ where $\delta$ is +the estimation of incertitude . In command {\tt ExtractLine}, the default value +is empirically set to $\delta=0.1$, which seem safe enough and lead to an acceleration +of factor $30$ of hough transform. + + +The code corresponding to algorithm~\ref{alg:hough} can be found in method : +\begin{itemize} + \item {\tt void cHoughTransform::Quick\_AccumulatePtAndDir(const cPt2dr \& aPt,tREAL8 aTetaC,tREAL8 aWeight); } + \item {\tt void cHoughTransform::Accurate\_AccumulatePtAndDir(const cPt2dr \& aPt,tREAL8 aTetaC,tREAL8 aWeight); } + + \item once we have sampled the different value of $\theta$ the formula~\ref{Line:EqRadon} gives a real value + of $\rho$, which does not fit exactly on a pixel of acccumularor, in quick version we simply round + to nearest integer, while in second we split it using linear interpolation; by default {\tt ExtractLine} + uses quick version. +\end{itemize} + +The figure~\ref{Fig:Line:Hough} represent the typical result we obtain at this step. + +\begin{figure} +\centering + \includegraphics[width=8cm]{Methods/ImagesFils/FullHough.jpg} + \includegraphics[width=8cm]{Methods/ImagesFils/Crop-Hough.jpg} + \caption{Example of hough transform, and a crop on it} +\label{Fig:Line:Hough} +\end{figure} + + +%----------------------------------------------------------------------- + +\subsection{Extraction of maxima of hough transform} + +\label{Hough:Max:Loc} + +The next step is to extract the "best" line candidate in hough space, +this is done in following steps : + +\begin{itemize} + \item compute a threshold $Th$ taking into accound average $Avg_H$ and max $Max_H$ of hough accumulator, + in current version $Th = \max (10*Avg, \frac{Max_H}{10})$ +% std::vector aVMaxLoc = mExtrL->Hough().ExtractLocalMax(10,5.0,10.0,0.1); +% std::vector cHoughTransform::ExtractLocalMax(size_t aNbMax,tREAL8 aDist,tREAL8 aThrAvg,tREAL8 aThrMax) const + \item extract all the point for which $H(\rho,\theta) > Th$ and that are a local maxima in a + given neighbourhood, in current version of size of the neighbourhoud is $5$; + + \item once we have selected the local maxima, we select if necessary the $K$ highest point, here $K=10$; + + \item finaly we refine the position to transform the integer value in real value, classically we compute + the value in a neigboorhoud (here the $8$ neighboors), fit a quadratic functions on these values + and extract the maximal value. +\end{itemize} + +This extraction is done in the method : + +\begin{itemize} + \item {\tt std::vector cHoughTransform::ExtractLocalMax(size\_t aNbMax,tREAL8 aDist,tREAL8 aThrAvg,tREAL8 aThrMax) const;} + +\end{itemize} + + +%----------------------------------------------------------------------- +%----------------------------------------------------------------------- +%----------------------------------------------------------------------- + +\section{Back to image space computation} + +%----------------------------------------------------------------------- + +\subsection{Refine line in image space} + +\label{Line:Ref:ImSpace} + +It is more accurate to use the initial pixel coordinates for extracting +the fine coordinate of each line. This is done using a weighted least-square fitting. +For each hough-point $\rho, \theta$ selected +(by~\ref{Hough:Max:Loc}) , we compute the line $D$ and do the following process : + +\begin{itemize} + \item extract the local maxima of gradient (as in~\ref{Ref:Grad:Loc:Max}) that are + at distance of $D$ inferior to a given threshold (here $2.0$ pixel), + and whose gradient is close enouh to normal of $D$ (here we use $\frac{\pi}{10}$); + + \item for each point $p_k$ at distance $d_k$ compute a weight $W_k=\frac{1}{1+\frac{d_k}{\sigma}^2}$, + where $\sigma$ is the a priori estimation of variance (here we use $\sigma=0.2$); + + \item estimate by least squate the $D'$ minimizind $\sum_k W_k d(D',p_k)^2 $ + + \item eventually iterate. +\end{itemize} + + +This refinement is done in the method : + +\begin{itemize} + \item {\tt void cExtractLines::RefineLineInSpace(cHoughPS \& aHPS)} ; + +\end{itemize} + + +%----------------------------------------------------------------------- + +\subsection{Match anti parallele line} + +Now we have a set of line that may be potentially the contour of a wire, to reconstruct +the wire we must match the two anti parallel line that constitute the wire. We say +that two oriented line match if : + +\begin{itemize} + \item their anti parallel , with a given threshold (here we use $6e-4 rad$ ) + \item the distance of the middle of each line to the other is in a given interval + (here we use $[2.0,7.0]$, maybe dangerous to have a too strict value) + + \item their relative position (left/right, taking into account orientation) + is compatible with the fact that the line is white or black; +\end{itemize} + +This match method is implemented in : + +\begin{itemize} + \item {\tt bool cHoughPS::Match(const cHoughPS \& aPS2,bool IsLight,tREAL8 aMaxTeta,tREAL8 aDMin,tREAL8 aDMax) const;} +\end{itemize} + +Now for each detected line, we compute, if any, its matched line : the line that comply with previous rule and +and is the closest. Finnaly we select as valid pair the line for which we have a reciprocal match. +This is implemented in : + +\begin{itemize} + \item {\tt std::vector> cHoughPS::GetMatches(const std::vector\& aVPS,bool IsLight,tREAL8 aMaxTeta,tREAL8 aDMin,tREAL8 aDMax);} +\end{itemize} + + + +%----------------------------------------------------------------------- + +\subsection{Central wire extraction and Quality evaluation} + +For selected lines, we compute then the central wire, that contain a line (middle of two matched lines) and a whidth. +Different quantities are computed to evaluate the quality of the wire, in the case where the line would contain +several lines that can generate false positive : + + +\begin{itemize} + \item sum of weight $SW$ of match gradient point (as defined in \ref{Line:Ref:ImSpace}) + \item parallelism of the two anti paralel line line; + \item homogeneity of radiometry measured in the center of the wire. +\end{itemize} + +\TOIMPROVE{ Add the sigma from least square fitting, as additionnal criteria} + +The criteria on homogeneity is based on the folowing expectation : + +\begin{itemize} + \item the "real" wire being on the foreground is never interupted, and it radiometry should be + smooth (the radiometric variation are due to the vignettage and flash) + \item the "false" wire are object, put on the magnet that are often interupted which create + radiomatetric discontinuities (see the typical two profiles on figure~\ref{Fig:Line:RadiomProfil}.) +\end{itemize} + +\begin{figure} +\centering + \includegraphics[width=14cm]{Methods/ImagesFils/ProfilOK.jpg} + \includegraphics[width=14cm]{Methods/ImagesFils/ProfilNotOk.jpg} + \caption{Two radiometric profil, one of a "true" wire, the other for a false positive} +\label{Fig:Line:RadiomProfil} +\end{figure} + +The radiometric homogeneity if computed this way : + +\begin{itemize} + \item a median filter is applied (size $3$) is applied, + this low frequency filter aims at supressing the random noise, + \item then the total difference between the signal and convolution by a gaussian filter $\sum |I-I \circledast G| $ is computed + to estimate the variations; + \item the homogeneity is the total variation divide by the average (to have a measure + invariant to global lighting). +\end{itemize} + +This estimation of radiometric homogeneity can be found in : + +\begin{itemize} + \item {\tt void cParalLine::ComputeRadiomHomog(const cDataGenUnTypedIm<2> \& anIm,cPerspCamIntrCalib * aCalib,const std::string \& aNameFile)}; + +\end{itemize} + +%----------------------------------------------------------------------- + +\subsection{Selection of \emph{best} lines} + +We must now make a decision, ideally detect $1$ and only $1$ wire, but handle the case +were there is no wire, or multiple wire. + + +Firt if the list of candidate is not empty we consider the wire that maximize the sum of weight $SW$ +defined bellow ( $\pm$ the one fitted by the maximum of points). All the other can be rejected if +the best one is signficantly better, to see the rule used, examine the method + +\begin{itemize} + \item {\tt bool cParalLine::RejectByComparison(const cParalLine \& aBetterOne) const;} +\end{itemize} + + + +% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +% \subsubsection{AAA} + + + + + + + + diff --git a/MMVII/MMVII-TestDir/Input/TestParseFile1.txt b/MMVII/MMVII-TestDir/Input/TestParseFile1.txt index afea6a0f7a..777605c5d8 100644 --- a/MMVII/MMVII-TestDir/Input/TestParseFile1.txt +++ b/MMVII/MMVII-TestDir/Input/TestParseFile1.txt @@ -16,6 +16,8 @@ this one also # blabla # blabla again +# here we add a line identic to already done, will ne skiped +0 Zero 3.14 0.0 0.0 1.0 2.0 3.0 0.0 0 # some comment after line 2 Two 3.14 2.0 2.0 1.0 2.0 3.0 2.0 2 diff --git a/MMVII/include/MMVII_DeclareAllCmd.h b/MMVII/include/MMVII_DeclareAllCmd.h index 6270fc92bf..5c028823c5 100755 --- a/MMVII/include/MMVII_DeclareAllCmd.h +++ b/MMVII/include/MMVII_DeclareAllCmd.h @@ -111,6 +111,8 @@ extern cSpecMMVII_Appli TheSpec_ExportUndistMesIm; extern cSpecMMVII_Appli TheSpecAppliExtractLine; extern cSpecMMVII_Appli TheSpec_CERN_ImportClino; extern cSpecMMVII_Appli TheSpec_MMV2_MesIm_2_MMV1; + +extern cSpecMMVII_Appli TheSpec_MergeMesImGCP; }; #endif // _MMVII_DeclareAllCmd_H_ diff --git a/MMVII/include/MMVII_ExtractLines.h b/MMVII/include/MMVII_ExtractLines.h index ec2743cbac..649abf8ec2 100755 --- a/MMVII/include/MMVII_ExtractLines.h +++ b/MMVII/include/MMVII_ExtractLines.h @@ -43,7 +43,7 @@ class cHoughPS // : public cMemCheck void Test(const cHoughPS & ) const; bool Match(const cHoughPS &,bool IsDark,tREAL8 aMaxTeta,tREAL8 aDMin,tREAL8 aDMax) const; - static std::vector GetMatches(std::vector& mVPS,bool IsLight,tREAL8 aMaxTeta,tREAL8 aDMin,tREAL8 aDMax); + static std::vector> GetMatches(const std::vector& mVPS,bool IsLight,tREAL8 aMaxTeta,tREAL8 aDMin,tREAL8 aDMax); void UpdateSegImage(const tSeg & aNewSeg,tREAL8 aNewCumul); @@ -210,6 +210,20 @@ template void ComputeDericheAndNorm(cImGradWithN & aResGrad,co /** Class for extracting line using gradient & hough transform*/ +enum class eIsWhite +{ + Yes, + No +}; + +enum class eIsQuick +{ + Yes, + No +}; + +template bool IsYes(const Type & aVal) {return aVal==Type::Yes;} + template class cExtractLines { public : @@ -221,8 +235,12 @@ template class cExtractLines cExtractLines(tIm anIm); ///< constructor , memorize image ~cExtractLines(); + // isWhite is necessary for oriented test on max loc + /// initialize the gradient - void SetDericheGradAndMasq(tREAL8 aAlphaDerich,tREAL8 aRayMaxLoc,int aBorder,bool Show=false); + void SetSobelAndMasq(eIsWhite,tREAL8 aRayMaxLoc,int aBorder,bool Show=false); + void SetDericheAndMasq(eIsWhite,tREAL8 aAlphaDerich,tREAL8 aRayMaxLoc,int aBorder,bool Show=false); + /// Initialize the hough transform void SetHough(const cPt2dr & aMulTetaRho,tREAL8 aSigmTeta,cPerspCamIntrCalib *,bool Accurate,bool Show=false); @@ -241,6 +259,8 @@ template class cExtractLines private : + void SetGradAndMasq(eIsQuick Quick,eIsWhite isWhite,tREAL8 aRayMaxLoc,int aBorder,bool Show=false); + cPt2di mSz; ///< Size of the image tIm mIm; ///< Memorize the image cIm2D mImMasqCont; ///< Masq of point selected as contour diff --git a/MMVII/include/MMVII_MeasuresIm.h b/MMVII/include/MMVII_MeasuresIm.h index 14a7107b2f..8a2e7ed656 100755 --- a/MMVII/include/MMVII_MeasuresIm.h +++ b/MMVII/include/MMVII_MeasuresIm.h @@ -98,6 +98,9 @@ class cSetMesPtOf1Im : public cMemCheck void AddMeasure(const cMesIm1Pt &); void AddData(const cAuxAr2007 & anAux); + + void AddSetMeasure(const cSetMesPtOf1Im &,bool SuprNone,bool OkDupl); + void ToFile(const std::string & aNameFile) const; static std::string StdNameFileOfIm(const std::string &); std::string StdNameFile() const; diff --git a/MMVII/include/MMVII_ReadFileStruct.h b/MMVII/include/MMVII_ReadFileStruct.h index 55af321347..65cdaea220 100644 --- a/MMVII/include/MMVII_ReadFileStruct.h +++ b/MMVII/include/MMVII_ReadFileStruct.h @@ -29,17 +29,19 @@ class cNRFS_ParamRead { public : cNRFS_ParamRead(); - cNRFS_ParamRead(int aL0,int aLast,char aComment); + cNRFS_ParamRead(int aL0,int aLast,char aComment,bool noDupL=false); int L0() const; int LLast() const; char Comment() const; + bool NoDupLine() const; void AddArgOpt(cCollecSpecArg2007 &); private : int mL0; int mLLast; char mComment; + bool mNoDupLine; }; diff --git a/MMVII/include/MMVII_Sensor.h b/MMVII/include/MMVII_Sensor.h index b6e4b3c992..a171c8ff9c 100755 --- a/MMVII/include/MMVII_Sensor.h +++ b/MMVII/include/MMVII_Sensor.h @@ -549,6 +549,9 @@ class cPhotogrammetricProject void SaveMeasureIm(const cSetMesPtOf1Im & aSetM) const; /// Does the measure exist bool HasMeasureIm(const std::string & aNameIm,bool InDir=true) const; + /// Does it exist for a specific folder + bool HasMeasureImFolder(const std::string & aFolder,const std::string & aNameIma) const; + /// return from Std Dir, can be out in case of reload cSetMesPtOf1Im LoadMeasureIm(const std::string &,bool InDir=true) const; diff --git a/MMVII/src/Appli/cSpecMMVII_Appli.cpp b/MMVII/src/Appli/cSpecMMVII_Appli.cpp index cea21a2be3..e3c66f3689 100755 --- a/MMVII/src/Appli/cSpecMMVII_Appli.cpp +++ b/MMVII/src/Appli/cSpecMMVII_Appli.cpp @@ -258,6 +258,7 @@ std::vector & cSpecMMVII_Appli::InternVecAll() TheVecAll.push_back(&TheSpec_CERN_ImportClino); TheVecAll.push_back(&TheSpec_MMV2_MesIm_2_MMV1); + TheVecAll.push_back(&TheSpec_MergeMesImGCP); std::sort(TheVecAll.begin(),TheVecAll.end(),CmpCmd); } diff --git a/MMVII/src/CodedTarget/cCircTargetExtract.cpp b/MMVII/src/CodedTarget/cCircTargetExtract.cpp index 50d6803135..78142e1a4e 100755 --- a/MMVII/src/CodedTarget/cCircTargetExtract.cpp +++ b/MMVII/src/CodedTarget/cCircTargetExtract.cpp @@ -809,7 +809,8 @@ cCollecSpecArg2007 & cAppliExtractCircTarget::ArgObl(cCollecSpecArg2007 & anArgO // Standard use, we put args of cAppliParseBoxIm first return APBI_ArgObl(anArgObl) - << Arg2007(mNameSpec,"Xml/Json name for bit encoding struct",{{eTA2007::XmlOfTopTag,cFullSpecifTarget::TheMainTag}}) + // << Arg2007(mNameSpec,"Xml/Json name for bit encoding struct",{{eTA2007::XmlOfTopTag,cFullSpecifTarget::TheMainTag}}) + << Arg2007(mNameSpec,"Xml/Json name for bit encoding struct",{{eTA2007::FileAny}}) ; } diff --git a/MMVII/src/CodedTarget/cGenerateEncoding.cpp b/MMVII/src/CodedTarget/cGenerateEncoding.cpp index 1b99f1ac0a..98681c6fa9 100755 --- a/MMVII/src/CodedTarget/cGenerateEncoding.cpp +++ b/MMVII/src/CodedTarget/cGenerateEncoding.cpp @@ -270,6 +270,7 @@ cCollecSpecArg2007 & cAppliGenerateEncoding::ArgOpt(cCollecSpecArg2007 & anArgOp << AOpt2007(mSpec.mUseHammingCode,"UHC","Use Hamming code") << AOpt2007(mSpec.mPrefix,"Prefix","Prefix for output files") << AOpt2007(mMiror,"Mir","Unify mirro codes") + << AOpt2007(mNameOut,"Out","Name for output file") ; } @@ -381,7 +382,8 @@ int cAppliGenerateEncoding::Exe() + "_Hamm" + ToStr(mSpec.mMinHammingD) + "_Run" + ToStr(mSpec.mMaxRunL.x()) + "_" + ToStr(mSpec.mMaxRunL.y()); } - mNameOut = mSpec.mPrefix + "_SpecEncoding." + TaggedNameDefSerial(); + if (! IsInit(&mNameOut)) + mNameOut = mSpec.mPrefix + "_SpecEncoding." + TaggedNameDefSerial(); // calls method in cMMVII_Appli, to show current value of params, as many transformation have been made ShowAllParams(); diff --git a/MMVII/src/CodedTarget/cGenerateTarget.cpp b/MMVII/src/CodedTarget/cGenerateTarget.cpp index 94b1e71a9a..ac5cde34ba 100755 --- a/MMVII/src/CodedTarget/cGenerateTarget.cpp +++ b/MMVII/src/CodedTarget/cGenerateTarget.cpp @@ -1050,6 +1050,7 @@ class cAppliGenCodedTarget : public cMMVII_Appli bool mDoMarkC; std::string mPatternDoImage; int mNbPixBin; + std::string mNameOut; }; eTyCodeTarget cAppliGenCodedTarget::Type() {return mBE.Specs().mType ;} @@ -1068,7 +1069,8 @@ cCollecSpecArg2007 & cAppliGenCodedTarget::ArgObl(cCollecSpecArg2007 & anArgObl) { return anArgObl - << Arg2007(mNameBE,"Xml/Json name for bit encoding struct",{{eTA2007::XmlOfTopTag,cBitEncoding::TheMainTag}}) + // << Arg2007(mNameBE,"Xml/Json name for bit encoding struct",{{eTA2007::XmlOfTopTag,cBitEncoding::TheMainTag}}) + << Arg2007(mNameBE,"Xml/Json/Dmp name for bit encoding struct",{{eTA2007::FileAny}}) ; } @@ -1093,6 +1095,7 @@ cCollecSpecArg2007 & cAppliGenCodedTarget::ArgOpt(cCollecSpecArg2007 & anArgOpt) << AOpt2007(mDoMarkC,"MarkC","Mark center of bits, just for verif ",{eTA2007::HDV,eTA2007::Tuning}) << AOpt2007(mZoomShow,"ZoomShow","Zoom to generate a high resolution check images",{eTA2007::Tuning}) << AOpt2007(mNbPixBin,"NbPixBin","Size of binary image when printing",{eTA2007::HDV}) + << AOpt2007(mNameOut,"Out","Name for out file") ; } @@ -1136,19 +1139,21 @@ int cAppliGenCodedTarget::Exe() } } - std::string aName = aFullSpec.Prefix()+"_FullSpecif."+TaggedNameDefSerial(); - SaveInFile(aFullSpec, aName); + // std::string aName = aFullSpec.Prefix()+"_FullSpecif."+TaggedNameDefSerial(); + if (! IsInit(&mNameOut)) + mNameOut = aFullSpec.Prefix()+"_FullSpecif."+ LastPostfix(mNameBE); + SaveInFile(aFullSpec, mNameOut); if (0) // test reload { - auto aPtr = cFullSpecifTarget::CreateFromFile(aName); - StdOut() << "NNN=" << aName << std::endl; + auto aPtr = cFullSpecifTarget::CreateFromFile(mNameOut); + StdOut() << "NNN=" << mNameOut << std::endl; delete aPtr; } if (IsInit(&mZoomShow)) { - TestReloadAndShow_cFullSpecifTarget(aDirVisu,aName,mZoomShow); + TestReloadAndShow_cFullSpecifTarget(aDirVisu,mNameOut,mZoomShow); } diff --git a/MMVII/src/ImagesInfoExtract/cAppliExtractLine.cpp b/MMVII/src/ImagesInfoExtract/cAppliExtractLine.cpp index 76877c6d62..0a2f479e5e 100755 --- a/MMVII/src/ImagesInfoExtract/cAppliExtractLine.cpp +++ b/MMVII/src/ImagesInfoExtract/cAppliExtractLine.cpp @@ -283,7 +283,8 @@ void cAppliExtractLine::DoOneImage(const std::string & aNameIm) // Compute Gradient and extract max-loc in gradient direction cAutoTimerSegm anATSDerAndMasq (mTimeSeg,"DericheAndMasq"); - mExtrL->SetDericheGradAndMasq(2.0,10.0,12,mShow); // aAlphaDerich,aRayMaxLoc,aBorder + mExtrL->SetSobelAndMasq(eIsWhite::Yes,10.0,12,mShow); // aRayMaxLoc,aBorder + // mExtrL->SetDericheGradAndMasq(2.0,10.0,12,mShow); // aAlphaDerich,aRayMaxLoc,aBorder // Compute Hough-Transform cAutoTimerSegm anATSHough (mTimeSeg,"Hough"); @@ -330,11 +331,11 @@ void cAppliExtractLine::DoOneImage(const std::string & aNameIm) { cAutoTimerSegm anATSMatchHough (mTimeSeg,"MatchLocHough"); - std::vector aVMatches = cHoughPS::GetMatches(mVPS,mLineIsWhite,mParamMatch.at(0),mParamMatch.at(1),mParamMatch.at(2)); + std::vector> aVMatches = cHoughPS::GetMatches(mVPS,mLineIsWhite,mParamMatch.at(0),mParamMatch.at(1),mParamMatch.at(2)); - for (const auto aPair : aVMatches) + for (const auto & [aK1,aK2] : aVMatches) { - mParalLines.push_back(cParalLine(mVPS.at(aPair.x()),mVPS.at(aPair.y()))); + mParalLines.push_back(cParalLine(mVPS.at(aK1),mVPS.at(aK2))); } StdOut() << " # NBMatched " << aVMatches.size() << "\n"; SortOnCriteria diff --git a/MMVII/src/ImagesInfoExtract/cExtractLines.cpp b/MMVII/src/ImagesInfoExtract/cExtractLines.cpp index 102a548a60..6e15aebf24 100755 --- a/MMVII/src/ImagesInfoExtract/cExtractLines.cpp +++ b/MMVII/src/ImagesInfoExtract/cExtractLines.cpp @@ -122,11 +122,37 @@ template void cExtractLines::SetHough } } -template void cExtractLines::SetDericheGradAndMasq(tREAL8 aAlpha,tREAL8 aRay,int aBorder,bool Show) +template void cExtractLines::SetSobelAndMasq(eIsWhite isWhite,tREAL8 aRayMaxLoc,int aBorder,bool Show) { // Create the data for storing gradient & init gradient mGrad = new cImGradWithN(mIm.DIm().Sz()); + mTabG->TabulateNeighMaxLocGrad(63,1.7,aRayMaxLoc); // 1.7=> maintain 8-neighboor, 63 number of direction + mGrad->SetQuickSobel(mIm.DIm(),*mTabG,2); + + SetGradAndMasq(eIsQuick::Yes,isWhite, aRayMaxLoc,aBorder,Show); +} + +/* The behaviour is not coherent with "SetSobelAndMasq" , to modify later probably, for now comment + +template void cExtractLines::SetDericheAndMasq(eIsWhite isWhite,tREAL8 aAlphaDerich,tREAL8 aRayMaxLoc,int aBorder,bool Show) +{ + // Create the data for storing gradient & init gradient + mGrad = new cImGradWithN(mIm.DIm().Sz()); + + mGrad->SetDeriche(mIm.DIm(),aAlphaDerich); + + SetGradAndMasq(eIsQuick::No,isWhite, aRayMaxLoc,aBorder,Show); +} +*/ + +template void cExtractLines::SetGradAndMasq(eIsQuick isQuick,eIsWhite isWhite,tREAL8 aRayMaxLoc,int aBorder,bool Show) + +{ + // Create the data for storing gradient & init gradient +/* + mGrad = new cImGradWithN(mIm.DIm().Sz()); + bool Quick = true; bool IsWhite = true; @@ -139,9 +165,10 @@ template void cExtractLines::SetDericheGradAndMasq(tREAL8 aAl { mGrad->SetDeriche(mIm.DIm(),aAlpha); } +*/ cRect2 aRect(mImMasqCont.DIm().Dilate(-aBorder)); // rect interior - std::vector aVecNeigh = cImGradWithN::NeighborsForMaxLoc(aRay); // neigbours for compute max + std::vector aVecNeigh = cImGradWithN::NeighborsForMaxLoc(aRayMaxLoc); // neigbours for compute max // count pts & pts of contour for stat mNbPtsCont = 0; @@ -150,8 +177,8 @@ template void cExtractLines::SetDericheGradAndMasq(tREAL8 aAl for (const auto & aPix : aRect) { aNbPt++; - bool IsMaxLoc = Quick ? - mGrad->TabIsMaxLocDirGrad(aPix,*mTabG,IsWhite) : + bool IsMaxLoc = IsYes(isQuick) ? + mGrad->TabIsMaxLocDirGrad(aPix,*mTabG,IsYes(isWhite)) : mGrad->IsMaxLocDirGrad(aPix,aVecNeigh,1.0) ; if (IsMaxLoc) { @@ -164,6 +191,8 @@ template void cExtractLines::SetDericheGradAndMasq(tREAL8 aAl if (Show) StdOut()<< " Prop Contour = " << mNbPtsCont / double(aNbPt) << "\n"; } +/* +*/ /* Generate a RGB-image : * - background is initial image diff --git a/MMVII/src/ImagesInfoExtract/cHoughTransform.cpp b/MMVII/src/ImagesInfoExtract/cHoughTransform.cpp index d9574c9804..de29919d0f 100755 --- a/MMVII/src/ImagesInfoExtract/cHoughTransform.cpp +++ b/MMVII/src/ImagesInfoExtract/cHoughTransform.cpp @@ -307,9 +307,9 @@ bool cHoughPS::Match(const cHoughPS & aPS2,bool IsLight,tREAL8 aMaxTeta,tREAL8 a } -std::vector cHoughPS::GetMatches(std::vector& aVPS,bool IsLight,tREAL8 aMaxTeta,tREAL8 aDMin,tREAL8 aDMax) +std::vector> cHoughPS::GetMatches(const std::vector& aVPS,bool IsLight,tREAL8 aMaxTeta,tREAL8 aDMin,tREAL8 aDMax) { - std::vector aVMatches; + std::vector> aVMatches; std::vector aIndM( aVPS.size(),-1); std::vector aCostM( aVPS.size(),1e30); @@ -341,7 +341,7 @@ std::vector cHoughPS::GetMatches(std::vector& aVPS,bool IsLig // test to get only one way && reciprocity if ((aIndM[aK] > aK) && (aIndM[aIndM[aK]] == aK)) { - aVMatches.push_back(cPt2di(aK,aIndM[aK])); + aVMatches.push_back(std::pair(aK,aIndM[aK])); } } return aVMatches; diff --git a/MMVII/src/ImagesInfoExtract/cImGradWithN.cpp b/MMVII/src/ImagesInfoExtract/cImGradWithN.cpp index 8920d2c52c..77acd74312 100755 --- a/MMVII/src/ImagesInfoExtract/cImGradWithN.cpp +++ b/MMVII/src/ImagesInfoExtract/cImGradWithN.cpp @@ -204,6 +204,7 @@ template bool cImGradWithN::TabIsMaxLocDirGrad(const cPt2di& cPt2di aGrad(this->mDGx->GetV(aPix),this->mDGy->GetV(aPix)); auto [aPtrVecNeigh,aIndR0] = aTabul.TabNeighMaxLocGrad(aGrad); + // first check in "small" neighboorhood if it's max lox for (int anInd=0 ; anInd bool cImGradWithN::TabIsMaxLocDirGrad(const cPt2di& } } + // first check in "big" half neighboorhood (opposed to posible matching seg of the wire) if it's max lox for (size_t anInd=aIndR0 ; anIndsize() ; anInd++) { cPt2di aNeigh = aPtrVecNeigh->at(anInd) * aSignGlob; @@ -229,6 +231,8 @@ template bool cImGradWithN::TabIsMaxLocDirGrad(const cPt2di& return false; } + // then check in other big half neighboorhood , if its max loc, but only with point in same direction + // to avoid elimination by anti-paralell seg for (size_t anInd=aIndR0 ; anIndsize() ; anInd++) { cPt2di aNeigh = aPtrVecNeigh->at(anInd) * (-aSignGlob); diff --git a/MMVII/src/ImportFormat/ImportLines.cpp b/MMVII/src/ImportFormat/ImportLines.cpp index 5d383a4b7c..a75f3d7dd7 100644 --- a/MMVII/src/ImportFormat/ImportLines.cpp +++ b/MMVII/src/ImportFormat/ImportLines.cpp @@ -54,6 +54,7 @@ class cAppli_ImportLines : public cMMVII_Appli std::string mNameWidth; std::string mSpecFormatMand; std::string mSpecFormatTot; + cPt2dr mOffset; }; @@ -68,7 +69,8 @@ cAppli_ImportLines::cAppli_ImportLines(const std::vector & aVArgs,c mNameSigma ("Sigma"), mNameWidth ("Width"), mSpecFormatMand (mNameIm+mNameX1+mNameY1+mNameX2+mNameY2), - mSpecFormatTot (mSpecFormatMand+"/"+mNameSigma +mNameWidth) + mSpecFormatTot (mSpecFormatMand+"/"+mNameSigma +mNameWidth), + mOffset (0,0) { // std::map aMap{{"2",2}}; } @@ -89,6 +91,8 @@ cCollecSpecArg2007 & cAppli_ImportLines::ArgOpt(cCollecSpecArg2007 & anArgOpt) return anArgOpt << mPhProj.ArgSysCo() + << AOpt2007(mOffset,"Offset","Offset to add to pixels",{eTA2007::HDV}) + ; } @@ -116,8 +120,8 @@ int cAppli_ImportLines::Exe() // Add a new line cOneLineAntiParal aLine; - cPt2dr aP1=aNRFS.GetPt2dr(aK,mNameX1,mNameY1); - cPt2dr aP2=aNRFS.GetPt2dr(aK,mNameX2,mNameY2); + cPt2dr aP1=aNRFS.GetPt2dr(aK,mNameX1,mNameY1) + mOffset; + cPt2dr aP2=aNRFS.GetPt2dr(aK,mNameX2,mNameY2) + mOffset; if (aP1.x() > -100) // CERN CONVENTION FOR FALSE SEG { diff --git a/MMVII/src/ImportFormat/ImportTiepMul.cpp b/MMVII/src/ImportFormat/ImportTiepMul.cpp index 71de57fa79..21619ad48e 100644 --- a/MMVII/src/ImportFormat/ImportTiepMul.cpp +++ b/MMVII/src/ImportFormat/ImportTiepMul.cpp @@ -54,6 +54,7 @@ class cAppli_ImportTiePMul : public cMMVII_Appli tNameSet mSetFilterIm; bool mWithImFilter; cMMVII_Ofs * mFiltFile; + cPt2dr mOffset; }; @@ -68,7 +69,8 @@ cAppli_ImportTiePMul::cAppli_ImportTiePMul(const std::vector & aVAr mFileSelIm ("ImagesWithTieP"), mNumByConseq (false), mWithImFilter (false), - mFiltFile (nullptr) + mFiltFile (nullptr), + mOffset (0,0) { } @@ -97,6 +99,7 @@ cCollecSpecArg2007 & cAppli_ImportTiePMul::ArgOpt(cCollecSpecArg2007 & anArgObl) << AOpt2007(mPatIm,"PatIm","Pattern for transforming name [Pat,Replace]",{{eTA2007::ISizeV,"[2,2]"}}) << AOpt2007(mPatPt,"PatPt","Pattern for transforming/select pt [Pat,Replace] ",{{eTA2007::ISizeV,"[2,2]"}}) << AOpt2007(mImFilter,"ImFilter","File/Pattern for selecting images") + << AOpt2007(mOffset,"Offset","Offset to add to image measures",{eTA2007::HDV}) ; if (mModeTieP) return aRes @@ -162,7 +165,7 @@ int cAppli_ImportTiePMul::Exe() aNameI = ReplacePattern(mPatIm.at(0),mPatIm.at(1),aNameI); - cPt2dr aP2 = Proj(aVXYZ.at(aK)); + cPt2dr aP2 = Proj(aVXYZ.at(aK)) + mOffset; bool ImIsSel = true; if (mWithImFilter) ImIsSel = mSetFilterIm.Match(aNameI); diff --git a/MMVII/src/ImportFormat/MergeMesImGCP.cpp b/MMVII/src/ImportFormat/MergeMesImGCP.cpp new file mode 100644 index 0000000000..b54c84473e --- /dev/null +++ b/MMVII/src/ImportFormat/MergeMesImGCP.cpp @@ -0,0 +1,126 @@ +#include "MMVII_PCSens.h" +#include "MMVII_MMV1Compat.h" +#include "MMVII_DeclareCste.h" +#include "MMVII_BundleAdj.h" +#include "MMVII_2Include_Serial_Tpl.h" + + +/** + \file cConvCalib.cpp testgit + + \brief file for conversion between calibration (change format, change model) and tests +*/ + + +namespace MMVII +{ + + /* ********************************************************** */ + /* */ + /* cAppli_MergeMesImGCP */ + /* */ + /* ********************************************************** */ + +class cAppli_MergeMesImGCP : public cMMVII_Appli +{ + public : + cAppli_MergeMesImGCP(const std::vector & aVArgs,const cSpecMMVII_Appli & aSpec); + int Exe() override; + cCollecSpecArg2007 & ArgObl(cCollecSpecArg2007 & anArgObl) override ; + cCollecSpecArg2007 & ArgOpt(cCollecSpecArg2007 & anArgOpt) override ; + + // std::vector Samples() const override; + private : + + cPhotogrammetricProject mPhProj; + std::string mSpecImIn; + std::string mFolder2; + + // Mandatory Arg + +}; + +cAppli_MergeMesImGCP::cAppli_MergeMesImGCP(const std::vector & aVArgs,const cSpecMMVII_Appli & aSpec) : + cMMVII_Appli (aVArgs,aSpec), + mPhProj (*this) +{ +} + +cCollecSpecArg2007 & cAppli_MergeMesImGCP::ArgObl(cCollecSpecArg2007 & anArgObl) +{ + return anArgObl + << Arg2007(mSpecImIn,"Pattern/file for images",{{eTA2007::MPatFile,"0"},{eTA2007::FileDirProj}}) + << mPhProj.DPPointsMeasures().ArgDirInMand() + << mPhProj.DPPointsMeasures().ArgDirInMand("Folder second set",&mFolder2) + << mPhProj.DPPointsMeasures().ArgDirOutMand() + ; + +} + +cCollecSpecArg2007 & cAppli_MergeMesImGCP::ArgOpt(cCollecSpecArg2007 & anArgOpt) +{ + return anArgOpt +/* + << AOpt2007(mL0,"NumL0","Num of first line to read",{eTA2007::HDV}) + << AOpt2007(mLLast,"NumLast","Num of last line to read (-1 if at end of file)",{eTA2007::HDV}) + << AOpt2007(mPatIm,"PatIm","Pattern for transforming name [Pat,Replace]",{{eTA2007::ISizeV,"[2,2]"}}) + << AOpt2007(mPatPt,"PatPt","Pattern for transforming/select pt [Pat,Replace] ",{{eTA2007::ISizeV,"[2,2]"}}) + << AOpt2007(mImFilter,"ImFilter","File/Pattern for selecting images") + << AOpt2007(mOffset,"Offset","Offset to add to image measures",{eTA2007::HDV}) +*/ + ; +} + +int cAppli_MergeMesImGCP::Exe() +{ + mPhProj.FinishInit(); + std::vector aSetNames = VectMainSet(0); + + for (const auto & aNameIma : aSetNames) + { + cSetMesPtOf1Im aSet(aNameIma); + if (mPhProj.HasMeasureIm(aNameIma)) + aSet.AddSetMeasure(mPhProj.LoadMeasureIm(aNameIma),true,true); + + if (mPhProj.HasMeasureImFolder(mFolder2,aNameIma)) + aSet.AddSetMeasure(mPhProj.LoadMeasureImFromFolder(mFolder2,aNameIma),true,true); + + mPhProj.SaveMeasureIm(aSet); + } + + // cSetMesPtOf1Im LoadMeasureIm(const std::string &,bool InDir=true) const; + // cSetMesPtOf1Im LoadMeasureImFromFolder(const std::string & aFolder,const std::string &) const; + + + return EXIT_SUCCESS; +} + + +// std::vector cAppli_MergeMesImGCP::Samples() const { return {}; } + +/*********************************************************************/ +/* */ +/* ImportTiePMul */ +/* */ +/*********************************************************************/ + +tMMVII_UnikPApli Alloc_MergeMesImGCP(const std::vector & aVArgs,const cSpecMMVII_Appli & aSpec) +{ + return tMMVII_UnikPApli(new cAppli_MergeMesImGCP(aVArgs,aSpec)); +} + +cSpecMMVII_Appli TheSpec_MergeMesImGCP +( + "MergeMesImGCP", + Alloc_MergeMesImGCP, + "Merge different files of image measur of GCP", + {eApF::GCP}, + {eApDT::GCP}, + {eApDT::GCP}, + __FILE__ +); + + + +}; // MMVII + diff --git a/MMVII/src/Instrumental/cBlockCamInit.cpp b/MMVII/src/Instrumental/cBlockCamInit.cpp index d7aea30443..87c7d8d508 100644 --- a/MMVII/src/Instrumental/cBlockCamInit.cpp +++ b/MMVII/src/Instrumental/cBlockCamInit.cpp @@ -732,6 +732,8 @@ int cAppli_BlockCamInit::Exe() { mPhProj.FinishInit(); // the final construction of photogrammetric project manager can only be done now + SetReportSubDir(mPhProj.DPRigBloc().DirOut()); + // creat the bloc, for now no cam,just the info to insert them cBlocOfCamera aBloc(mPattern,mNumSub.x(),mNumSub.y(),mComputeInv,mNameBloc); diff --git a/MMVII/src/Instrumental/cWire3DInit.cpp b/MMVII/src/Instrumental/cWire3DInit.cpp index 055bf9c942..7414baa0e2 100644 --- a/MMVII/src/Instrumental/cWire3DInit.cpp +++ b/MMVII/src/Instrumental/cWire3DInit.cpp @@ -43,8 +43,8 @@ class cAppli_Wire3DInit : public cMMVII_Appli private : void MakeOneBloc(const std::vector &); - void TestWire3D(const std::vector & aVCam); - void TestPoint3D(const std::vector & aVCam); + void TestWire3D(const std::string& anIdSync,const std::vector & aVCam); + void TestPoint3D(const std::string& anIdSync,const std::vector & aVCam); cPhotogrammetricProject mPhProj; @@ -52,6 +52,9 @@ class cAppli_Wire3DInit : public cMMVII_Appli std::list mListBloc; cBlocOfCamera * mTheBloc; + std::string mRepW; + std::string mRepPt; + std::string mPatNameGCP; }; /* @@ -70,7 +73,10 @@ cAppli_Wire3DInit::cAppli_Wire3DInit const cSpecMMVII_Appli & aSpec ) : cMMVII_Appli (aVArgs,aSpec), - mPhProj (*this) + mPhProj (*this), + mRepW ("Wire"), + mRepPt ("Pt"), + mPatNameGCP (".*") { } @@ -89,21 +95,14 @@ cCollecSpecArg2007 & cAppli_Wire3DInit::ArgObl(cCollecSpecArg2007 & anArgObl) cCollecSpecArg2007 & cAppli_Wire3DInit::ArgOpt(cCollecSpecArg2007 & anArgOpt) { - return anArgOpt - /* - << AOpt2007(mNameBloc,"NameBloc","Set the name of the bloc ",{{eTA2007::HDV}}) - << AOpt2007(mMaster,"Master","Set the name of the master bloc, is user wants to enforce it ") - << AOpt2007(mShowByBloc,"ShowByBloc","Show matricial organization by bloc ",{{eTA2007::HDV}}) - << AOpt2007(mShowBySync,"ShowBySync","Show matricial organization by sync ",{{eTA2007::HDV}}) - << AOpt2007(mTestRW,"TestRW","Call test en Read-Write ",{{eTA2007::HDV}}) - << AOpt2007(mTestNoDel,"TestNoDel","Force a memory leak error ",{{eTA2007::HDV}}) - */ + return anArgOpt + << AOpt2007(mPatNameGCP,"PatFiltGCP","Pattern to filter name of GCP",{{eTA2007::HDV}}) ; } -void cAppli_Wire3DInit::TestWire3D(const std::vector & aVCam) +void cAppli_Wire3DInit::TestWire3D(const std::string & anIdSync,const std::vector & aVCam) { std::vector aVPlane; @@ -130,7 +129,9 @@ void cAppli_Wire3DInit::TestWire3D(const std::vector & aVCam) } } + int aNbPl = aVPlane.size(); + if (aNbPl>=3) { cSegmentCompiled aWire = cPlane3D::InterPlane(aVPlane); @@ -151,52 +152,66 @@ void cAppli_Wire3DInit::TestWire3D(const std::vector & aVCam) } } tREAL8 aRatio = aNbPl /(aNbPl-2.0); + tREAL8 aDist3D = aWGr.Average() * aRatio; + tREAL8 aDistPix = aWPix.Average() * aRatio; - StdOut() << " DIST3=" << aWGr.Average() * aRatio - << " DDDDd= " << aWPix.Average() * aRatio + AddOneReportCSV(mRepW,{anIdSync,ToStr(aNbPl),ToStr(aDist3D),ToStr(aDistPix)}); + +/* + StdOut() << " DIST3=" << aDist3D + << " DDDDd= " << aDistPix << " NBPL = " << aNbPl << " RATIO=" << aRatio << "\n"; +*/ } } typedef std::pair tPairCamPt; -void cAppli_Wire3DInit::TestPoint3D(const std::vector & aVCam) +void cAppli_Wire3DInit::TestPoint3D(const std::string & anIdSync,const std::vector & aVCam) { std::map> aMapMatch; for (const auto & aCam : aVCam) { - cSetMesPtOf1Im aSet = mPhProj.LoadMeasureIm(aCam->NameImage()); + if (mPhProj.HasMeasureIm(aCam->NameImage())) + { + cSetMesPtOf1Im aSet = mPhProj.LoadMeasureIm(aCam->NameImage()); - for (const auto & aMes : aSet.Measures()) - { - if (!starts_with( aMes.mNamePt,MMVII_NONE)) - { - aMapMatch[aMes.mNamePt].push_back(tPairCamPt(aCam,aMes)); - } + for (const auto & aMes : aSet.Measures()) + { + if ((!starts_with( aMes.mNamePt,MMVII_NONE)) && MatchRegex(aMes.mNamePt,mPatNameGCP)) + { + aMapMatch[aMes.mNamePt].push_back(tPairCamPt(aCam,aMes)); + } + } } // StdOut() << "IM=" << aSet.NameIm() << " Nb=" << aSet.Measures().size() << "\n"; } for (const auto & [aStr,aList] : aMapMatch ) { - if (aList.size() > 2) + int aNbPt = aList.size(); + if (aNbPt > 2) { - StdOut() << " NAME=" << aStr << " " << aList.size() << "\n"; + // StdOut() << " NAME=" << aStr << " " << aList.size() << "\n"; std::vector aVSeg; for (const auto & [aCam,aMes] : aList) { aVSeg.push_back(aCam->Image2Bundle(aMes.mPt)); } cPt3dr aPG = BundleInters(aVSeg); + cWeightAv aWPix; for (const auto & [aCam,aMes] : aList) { cPt2dr aPProj = aCam->Ground2Image(aPG); - StdOut() << " DDDD = " << Norm2(aMes.mPt-aPProj) << "\n"; + aWPix.Add(1.0,Norm2(aMes.mPt-aPProj)); + // StdOut() << " DDDD = " << Norm2(aMes.mPt-aPProj) << "\n"; } + tREAL8 aDistPix = aWPix.Average() * (aNbPt*2.0) / (aNbPt*2.0 -3.0); + AddOneReportCSV(mRepPt,{anIdSync,ToStr(aNbPt),ToStr(aDistPix)}); } } @@ -205,14 +220,26 @@ void cAppli_Wire3DInit::TestPoint3D(const std::vector & aVCam) void cAppli_Wire3DInit::MakeOneBloc(const std::vector & aVCam) { - TestWire3D(aVCam); - // TestPoint3D(aVCam); + std::string anIdSync = mTheBloc->IdSync(aVCam.at(0)->NameImage()); + TestWire3D(anIdSync,aVCam); + TestPoint3D(anIdSync,aVCam); } int cAppli_Wire3DInit::Exe() { mPhProj.FinishInit(); // the final construction of photogrammetric project manager can only be done now + std::string aDirRep = mPhProj.DPOrient().DirIn() + + "-" + mPhProj.DPPointsMeasures().DirIn() + + "-" + mPhProj.DPRigBloc().DirIn() ; + SetReportSubDir(aDirRep); + + InitReport(mRepW,"csv",true); + InitReport(mRepPt,"csv",true); + AddOneReportCSV(mRepW,{"TimeBloc","NbPlane","Dist Ground","Dist Pix"}); + AddOneReportCSV(mRepPt,{"TimeBloc","NbPt","Dist Pix"}); + + mListBloc = mPhProj.ReadBlocCams(); MMVII_INTERNAL_ASSERT_tiny(mListBloc.size()==1,"Number of bloc ="+ ToStr(mListBloc.size())); diff --git a/MMVII/src/OrientReport/SegImCmp.cpp b/MMVII/src/OrientReport/SegImCmp.cpp index f209fa6c89..7469f866a4 100644 --- a/MMVII/src/OrientReport/SegImCmp.cpp +++ b/MMVII/src/OrientReport/SegImCmp.cpp @@ -31,7 +31,6 @@ class cAppli_SegImReport : public cMMVII_Appli std::string mFolder2; std::string mPrefixCSV; - std::string mPrefixCSVIma; void AddOneImage(const std::string & aNameIma); }; @@ -76,84 +75,41 @@ void cAppli_SegImReport::AddOneImage(const std::string & aNameIma) bool hasL1 = mPhProj.HasFileLines(aNameIma); bool hasL2 = mPhProj.HasFileLinesFolder(mFolder2,aNameIma); - if (hasL1 && hasL2) + std::string aStrDist = "XXXXXXX"; + int aNb1 = 0; + int aNb2 = 0; + cLinesAntiParal1Im aVL1 ; + cLinesAntiParal1Im aVL2 ; + if (hasL1) { - cLinesAntiParal1Im aVL1 = mPhProj.ReadLines(aNameIma); - cLinesAntiParal1Im aVL2 = mPhProj.ReadLinesFolder(mFolder2,aNameIma); - - - if ((aVL1.mLines.size()==1) && (aVL2.mLines.size()==1)) - { - cOneLineAntiParal aL1 = aVL1.mLines.at(0); - tSegComp2dr aSeg1 (aL1.mSeg.P1(),aL1.mSeg.P2()); + aVL1 = mPhProj.ReadLines(aNameIma); + aNb1 = aVL1.mLines.size(); + } + if (hasL2) + { + aVL2 = mPhProj.ReadLinesFolder(mFolder2,aNameIma); + aNb2 = aVL2.mLines.size(); + } - cOneLineAntiParal aL2 = aVL2.mLines.at(0); - tSegComp2dr aSeg2 (aL2.mSeg.P1(),aL2.mSeg.P2()); - if (1) - aSeg1 = tSegComp2dr(aCal->Undist(aSeg1.P1()), aCal->Undist(aSeg1.P2())); - if (1) - aSeg2 = tSegComp2dr(aCal->Undist(aSeg2.P1()), aCal->Undist(aSeg2.P2())); + if ((aNb1==1) && (aNb2==1)) + { + cOneLineAntiParal aL1 = aVL1.mLines.at(0); + cSegment2DCompiled aSeg1 (aL1.mSeg.P1(),aL1.mSeg.P2()); + cOneLineAntiParal aL2 = aVL2.mLines.at(0); + cSegment2DCompiled aSeg2 (aL2.mSeg.P1(),aL2.mSeg.P2()); - StdOut() << " DDDD=" << aNameIma << " " << aSeg1.Dist(aSeg2.P1()) << " " << aSeg1.Dist(aSeg2.P2()) << "\n"; + if (1) + aSeg1 = tSegComp2dr(aCal->Undist(aSeg1.P1()), aCal->Undist(aSeg1.P2())); + if (1) + aSeg2 = tSegComp2dr(aCal->Undist(aSeg2.P1()), aCal->Undist(aSeg2.P2())); - } - } - else - { - StdOut() << "MISSING " << aNameIma << " Im1=" << hasL1 << " Im2=" << hasL2 << " F=" << aCal->F() << "\n"; + tREAL8 aDist = (aSeg1.Dist(aSeg2.P1()) + aSeg1.Dist(aSeg2.P2())) / 2.0; + aStrDist = ToStr(aDist); } -// cLinesAntiParal1Im ReadLines(const std::string & aNameIm) const; - - /* - InitReport(mPrefixCSVIma,"csv",false); - AddStdHeaderStatCSV(mPrefixCSVIma,"Image",mPropStat,{"AvgX","AvgY"}); - - for (size_t aKImGlob = 0 ; aKImGlobVSensors().at(aKImGlob); - // a litle check on indexe for these complexe structures - MMVII_INTERNAL_ASSERT_tiny(mSetNames[aKImGlob]==aSensI->NameImage(),"Chek names in Appli_TiePReport::MakeStatByImage"); - - cWeightAv aAvg2d; - cStdStatRes aStat; - - for (const auto& aPairKC : aLInd) - { - size_t aKImLoc = aPairKC.first; - // Unused in mode release - [[maybe_unused]] const auto & aConfig = aPairKC.second->first; - // a litle check on indexe for these complexe structures - MMVII_INTERNAL_ASSERT_tiny(aKImGlob==(size_t)aConfig.at(aKImLoc),"Check nums in cAppli_SegImReport::MakeStatByImage"); - - const auto & aVal = aPairKC.second->second; - size_t aNbP = NbPtsMul(*aPairKC.second); - // size_t aMult = Multiplicity(*aPairKC.second); - - for (size_t aKp=0 ; aKpGround2Image(aPGr) - aPt; - - aStat.Add(Norm2(aResidu)); - aAvg2d.Add(1.0,aResidu); - } - - } - - AddStdStatCSV - ( - mPrefixCSVIma,mSetNames[aKImGlob],aStat,mPropStat, - {ToStr(aAvg2d.Average().x()),ToStr(aAvg2d.Average().y())} - ); - StdOut() << mSetNames[aKImGlob] << " Avg=" << aStat.Avg() << std::endl; - } - */ + AddOneReportCSV(mPrefixCSV,{aNameIma,ToStr(aNb1),ToStr(aNb2),aStrDist}); } @@ -162,11 +118,13 @@ int cAppli_SegImReport::Exe() mPhProj.FinishInit(); mSetNames = VectMainSet(0); - /* + std::string aN1 = mPhProj.DPPointsMeasures().DirIn() ; + std::string aN2 = mFolder2; + + mPrefixCSV = "CmpLines_"+ aN1 + "_" + aN2 ; + InitReport(mPrefixCSV,"csv",false); + AddHeaderReportCSV(mPrefixCSV,{"Image","Nb " + aN1 ,"Nb " + aN2,"Dist"}); - mPrefixCSV = "_Ori-"+ mPhProj.DPOrient().DirIn() + "_Mes-"+ mPhProj.DPMulTieP().DirIn() ; - mPrefixCSVIma = "ByImages" + mPrefixCSV; - */ for (const auto & aNameIma : mSetNames) AddOneImage(aNameIma); diff --git a/MMVII/src/Sensors/Measures.cpp b/MMVII/src/Sensors/Measures.cpp index 711323629c..fbe1447d35 100644 --- a/MMVII/src/Sensors/Measures.cpp +++ b/MMVII/src/Sensors/Measures.cpp @@ -447,6 +447,30 @@ void cSetMesPtOf1Im::AddMeasure(const cMesIm1Pt & aMeasure) mMeasures.push_back(aMeasure); } + +void cSetMesPtOf1Im::AddSetMeasure(const cSetMesPtOf1Im & aSet,bool SuprNone,bool OkDupl) +{ + MMVII_INTERNAL_ASSERT_tiny(mNameIm==aSet.mNameIm,"Mix different images in AddSetMeasure " + mNameIm + "!=" + aSet.mNameIm); + + for (const auto & aMes : aSet.mMeasures) + { + if (NameHasMeasure(aMes.mNamePt)) + { + if (! OkDupl) + { + MMVII_INTERNAL_ERROR("Non autorize duplicate name for Im=" + mNameIm + " Pt=" +aMes.mNamePt); + } + } + else + { + if ((!SuprNone) || (!starts_with(aMes.mNamePt,MMVII_NONE)) ) + mMeasures.push_back(aMes); + } + } +} + + + cMesIm1Pt * cSetMesPtOf1Im::NearestMeasure(const cPt2dr & aPt) { return WhitchMinVect diff --git a/MMVII/src/Sensors/cPhotogrammetricProject.cpp b/MMVII/src/Sensors/cPhotogrammetricProject.cpp index cc5c6f6585..a3e8aa8c93 100644 --- a/MMVII/src/Sensors/cPhotogrammetricProject.cpp +++ b/MMVII/src/Sensors/cPhotogrammetricProject.cpp @@ -748,6 +748,12 @@ bool cPhotogrammetricProject::HasMeasureIm(const std::string & aNameIm,bool InDi return ExistFile(NameMeasureGCPIm(aNameIm,InDir)); } +bool cPhotogrammetricProject::HasMeasureImFolder(const std::string & aFolder,const std::string & aNameIm) const +{ + cAutoChgRestoreDefFolder aCRDF(aFolder,DPPointsMeasures()); // Chg Folder and restore at destruction + return HasMeasureIm(aNameIm,true); +} + cSetMesPtOf1Im cPhotogrammetricProject::LoadMeasureIm(const std::string & aNameIm,bool isIn) const { diff --git a/MMVII/src/Utils/cNewReadFilesStruct.cpp b/MMVII/src/Utils/cNewReadFilesStruct.cpp index 1f7a242ada..74d0f284fc 100644 --- a/MMVII/src/Utils/cNewReadFilesStruct.cpp +++ b/MMVII/src/Utils/cNewReadFilesStruct.cpp @@ -1,6 +1,7 @@ #include "cMMVII_Appli.h" #include "MMVII_ReadFileStruct.h" #include "MMVII_Bench.h" +#include "MMVII_2Include_Serial_Tpl.h" @@ -34,10 +35,11 @@ namespace MMVII /* */ /* ************************************************************************************ */ -cNRFS_ParamRead::cNRFS_ParamRead(int aL0,int aLast,char aComment) : - mL0 (aL0), - mLLast (aLast), - mComment (aComment) +cNRFS_ParamRead::cNRFS_ParamRead(int aL0,int aLast,char aComment,bool noDupL) : + mL0 (aL0), + mLLast (aLast), + mComment (aComment), + mNoDupLine (noDupL) { } @@ -46,9 +48,10 @@ cNRFS_ParamRead::cNRFS_ParamRead() : { } -int cNRFS_ParamRead::L0() const {return mL0;} -int cNRFS_ParamRead::LLast() const {return mLLast;} -char cNRFS_ParamRead::Comment() const {return mComment;} +int cNRFS_ParamRead::L0() const {return mL0;} +int cNRFS_ParamRead::LLast() const {return mLLast;} +char cNRFS_ParamRead::Comment() const {return mComment;} +bool cNRFS_ParamRead::NoDupLine() const {return mNoDupLine;} void cNRFS_ParamRead::AddArgOpt(cCollecSpecArg2007 & anArgOpt) { @@ -56,6 +59,7 @@ void cNRFS_ParamRead::AddArgOpt(cCollecSpecArg2007 & anArgOpt) << AOpt2007(mL0,"NumL0","Num of first line to read",{eTA2007::HDV}) << AOpt2007(mLLast,"NumLast","Num of last line to read (-1 if at end of file)",{eTA2007::HDV}) << AOpt2007(mComment,"Comment","Carac for comment") + << AOpt2007(mNoDupLine,"NoDupL","Supress duplicated lines") ; } @@ -260,6 +264,7 @@ void cNewReadFilesStruct::ParseFormat(bool isSpec,const std::string & aFormat,s void cNewReadFilesStruct::ReadFile(const std::string & aNameFile,const cNRFS_ParamRead & aParam) { + std::set aSetHCode; // std::map > mMapInt; // std::map > mMapFloat; // std::map > mMapString; @@ -282,7 +287,12 @@ void cNewReadFilesStruct::ReadFile(const std::string & aNameFile,const cNRFS_Pa while (std::getline(infile, line)) { - if ((aNumL>=aParam.L0()) && (aNumL< aParam.LLast())) + size_t aHashCode = HashValue(line,true); + // must we skeep the line for duplicata reason + bool toSkip4Dupl = aParam.NoDupLine() && MapBoolFind(aSetHCode,aHashCode); + + + if ((aNumL>=aParam.L0()) && (aNumL< aParam.LLast()) && (!toSkip4Dupl) ) { const char * aC = line.c_str(); bool GoOn = true; @@ -311,6 +321,7 @@ void cNewReadFilesStruct::ReadFile(const std::string & aNameFile,const cNRFS_Pa aC++; std::string aToken(aC0,size_t(aC-aC0)); + std::string aNameField = mNameFields.at(aNbToken); switch (mTypes.at(aNbToken)) { @@ -340,6 +351,8 @@ void cNewReadFilesStruct::ReadFile(const std::string & aNameFile,const cNRFS_Pa } if (aNbToken>0) mNbLineRead++; + + aSetHCode.insert(aHashCode); } if (mDebug) StdOut() << "-------------------------------------------\n"; @@ -420,7 +433,7 @@ class cBenchcNewReadFilesStruct cBenchcNewReadFilesStruct::cBenchcNewReadFilesStruct(int aNum) : mNameFile (cMMVII_Appli::InputDirTestMMVII() + "TestParseFile"+ToStr(aNum) + ".txt"), - mParamRead (2,21,'#') + mParamRead (2,21,'#',true) { }