본문 바로가기

Tech

코드 정리 #1

인터넷으로 소스를 검색하다보면 읽을 때 피로도가 증가하는 경우도 있는 것 같다.

마침 눈에 띄는 소스가 있어서 일부를 한번 수정해 보았다.

 

소스 출처 : https://github.com/bytefish/opencv/blob/master/eigenfaces/src/eigenfaces.cpp

 

 

Before

 

void Eigenfaces::compute(const vector<MAT>& src, const vector<INT>& labels) {
    if(src.size() == 0) {
        string error_message = format("Empty training data was given. You'll need more than one sample to learn a model.");
        CV_Error(CV_StsUnsupportedFormat, error_message);
    }
    // observations in row
    Mat data = asRowMatrix(src, CV_64FC1);
    // number of samples
    int n = data.rows;
    // dimensionality of data
    int d = data.cols;
    // assert there are as much samples as labels
    if(n != labels.size()) {
        string error_message = format("The number of samples (src) must equal the number of labels (labels). Was len(samples)=%d, len(labels)=%d.", n, labels.size());
        CV_Error(CV_StsBadArg, error_message);
    }
    // clip number of components to be valid
    if((_num_components <= 0) || (_num_components > n))
        _num_components = n;
    // perform the PCA
    PCA pca(data, Mat(), CV_PCA_DATA_AS_ROW, _num_components);
    // copy the PCA results
    _mean = pca.mean.reshape(1,1); // store the mean vector
    _eigenvalues = pca.eigenvalues.clone(); // eigenvalues by row
    _eigenvectors = transpose(pca.eigenvectors); // eigenvectors by column
    _labels = labels; // store labels for prediction
    // save projections
    for(int sampleIdx = 0; sampleIdx < data.rows; sampleIdx++) {
        Mat p = project(data.row(sampleIdx).clone());
        this->_projections.push_back(p);
    }
}


void Eigenfaces::predict(const Mat& src, int &minClass, double &minDist) {
    if(_projections.empty()) {
        // throw error if no data (or simply return -1?)
        string error_message = "This cv::Eigenfaces model is not computed yet. Did you call cv::Eigenfaces::train?";
        CV_Error(CV_StsError, error_message);
    } else if(_eigenvectors.rows != src.total()) {
        // check data alignment just for clearer exception messages
        string error_message = format("Wrong input image size. Reason: Training and Test images must be of equal size! Expected an image with %d elements, but got %d.", _eigenvectors.rows, src.total());
        CV_Error(CV_StsError, error_message);
    }
    // project into PCA subspace
    Mat q = project(src.reshape(1,1));
    // find 1-nearest neighbor
    minDist = DBL_MAX;
    minClass = -1;
    for(int sampleIdx = 0; sampleIdx < _projections.size(); sampleIdx++) {
        double dist = norm(_projections[sampleIdx], q, NORM_L2);
        if((dist < minDist) && (dist < _threshold)) {
            minDist = dist;
            minClass = _labels[sampleIdx];
        }
    }
}

int Eigenfaces::predict(const Mat& src) {
    int label;
    double dummy;
    predict(src, label, dummy);
    return label;
}

 

 

 

After

 

void Eigenfaces::compute(const vector<MAT> &src, const vector<INT> &labels)
{
    string strMsgError;
    Mat data;
    int n;
    int d;
	
    
    if(src.size() == 0) 
    {
        string strMsgError = format("Empty training data was given. You'll need more than one sample to learn a model.");
        CV_Error(CV_StsUnsupportedFormat, strMsgError);
    }
    
    data = asRowMatrix(src, CV_64FC1);		// observations in row
    n 	 = data.rows;		// number of samples
    d 	 = data.cols;		// dimensionality of data

    // assert there are as much samples as labels
    if(n != labels.size())
    {
        strMsgError = format("The number of samples (src) must equal the number of labels (labels). Was len(samples)=%d, len(labels)=%d.", n, labels.size());
        CV_Error(CV_StsBadArg, strMsgError);
    }
    
    // clip number of components to be valid
    if((_num_components <= 0) || (_num_components > n))	_num_components = n;
    
    // perform the PCA
    PCA pca(data, Mat(), CV_PCA_DATA_AS_ROW, _num_components);
    
    // copy the PCA results
    _mean 		= pca.mean.reshape(1,1); 		// store the mean vector
    _eigenvalues 	= pca.eigenvalues.clone(); 		// eigenvalues by row
    _eigenvectors 	= transpose(pca.eigenvectors); 		// eigenvectors by column
    _labels 		= labels; 				// store labels for prediction
    
    // save projections
    for(int sampleIdx = 0; sampleIdx < data.rows; sampleIdx++) 
    {
        Mat p = project( data.row(sampleIdx).clone() );
        this->_projections.push_back(p);
    }
}


void Eigenfaces::predict(const Mat &src, int &minClass, double &minDist)
{
    string strMsgError;
    Mat q;
	
	
    if(_projections.empty()) 
    {
        // throw error if no data (or simply return -1?)
        strMsgError = "This cv::Eigenfaces model is not computed yet. Did you call cv::Eigenfaces::train?";
        CV_Error(CV_StsError, strMsgError);
    }
    else if(_eigenvectors.rows != src.total()) 
    {
        // check data alignment just for clearer exception messages
        strMsgError = format("Wrong input image size. Reason: Training and Test images must be of equal size! Expected an image with %d elements, but got %d.", _eigenvectors.rows, src.total());
        CV_Error(CV_StsError, strMsgError);
    }
    
    // project into PCA subspace
    q = project(src.reshape(1,1));
    
    // find 1-nearest neighbor
    minDist  = DBL_MAX;
    minClass = -1;
    
    for(int sampleIdx = 0; sampleIdx < _projections.size(); sampleIdx++)
    {
        double dist = norm(_projections[sampleIdx], q, NORM_L2);
        
        if((dist < minDist) && (dist < _threshold)) 
        {
            minDist  = dist;
            minClass = _labels[sampleIdx];
        }
    }
}


int Eigenfaces::predict(const Mat &src)
{
    int 	label;
    double 	dummy;
    
    
    predict(src, label, dummy);
    
    return label;
}

 

 

해설

 

큰 수정 맥락은 다음과 같다.

 

1. const Mat& src ==> const Mat &src

==> 좌측 코드는 변수를 여러개 선언 할 경우 마치 &가 각 변수에 적용되는 것처럼 오인될 수 있다.

==> EX) const Mat& src, dest;

==>      const Mat &src, &dest;  // 이것이 더 명확하다.

 

2. 대입문에 대한 코멘트가 상단에 위치하기도 하고 우측에 위치하기도 한다.

==> 모두 우측에 정렬되어 위치하도록 수정해 보았다.

 

3. 코드 중간에 변수가 선언되어 있는 부분을 될 수 있는 한 최상단에 보이도록 모아 보았다.

==> 코드 분석 시 변수 선언 파악이 더 빨라진다.

 

4. 함수와 함수, 메쏘드와 메쏘드 간의 간격은 2줄이다.

==> 직관적으로 함수의 끝을 더 쉽게 찾을 수 있다.

 

5. Allman style로 변경하였다.

==> 괄호 쌍의 짝이 안맞는 상황을 쉽게 감지 할 수 있다. 탭 역시 가장 이상적인 스타일로 생각된다.

 

6. 대입문이 반복될 시 이퀄은 같은 컬럼 위치에 존재하여 가독성을 높인다.

 

7.  로직의 흐름이 끊어지는 곳은 한 줄을 띄운다.

 

 

이처럼 수정하면 코드 가독성이 증가하여 에러를 찾거나 보수가 쉬워지지 않을까 싶다.

 

PS. 발행해서 보니 소스에서 코멘트의 탭이 맞지 않는데 이것은 의도된 바가 아니다.

'Tech' 카테고리의 다른 글

코드 정리 #3  (0) 2018.07.27
코드 정리 #2  (0) 2018.07.26
[기초] C/C++ 에서 에러를 최소화하는 코딩 법  (0) 2018.07.25
기본 Base64의 urlsafe 한 변형 버전이 있다.  (0) 2018.07.25
One Time Coding  (0) 2018.07.24