OpenCVSharp을 이용한 LD Bar 의 NFP 알고리즘

Software/C#|2022. 12. 28. 22:38
반응형

고출력 레이저 다이오드 모듈에 사용되는  LD Bar의 NFP(Near Field Pattern) 특성을 측정하기 위하여 OpenCVSharp 을 이용한 클래스를 만들었습니다. 

 

1. public class EmitterInform : LD Bar 에 있는 한개의 Emitter 정보를 저장하기위한 클래스 입니다.

2. public class NFP_Process : Emitter 정보를 찾고 처리하기위한 클래스 입니다. 

  (1) LD Bar 에 있는 각각의 Emitter 정보를 찾는다. 

  (2) 각 Emitter 의 위치 좌표값 정보

  (3) 각 Emitter 의 Intensity (밝기) 정보 

3. Emitter 정보 Search 알고리즘 : 아래 이미지는 테스트 과정에서 임의로 화면에 표시한것입니다. 실제 알고리즘 적용 단계에서는 아래 이미지가 보이지는 않습니다. 

  (1) 카메라로 LD Bar의 광출력 영상을 촬영한다. 흑백 카메라를 사용하는 것이 좋으며 칼라 카메라를 사용했을 경우 영상을 흑백으로 변환하여야 합니다.

LD Bar를 카메라로 찍은 모습

  (2) Cv2.FindContours 함수를 사용하여 각각의 Emitter의 외곽 정보를 찾는다.  

  (3) 이렇게 찾은 개별 Emitter에 대해 Cv2.Moments 함수를 이용하여 Emitter의 중심점 좌표를 찾는다. 

빨간색 점이 각 Emitter의 중심좌표이다.

  (4) Contour 정보와 중심점 좌표를 이용하여 각 Emitter의 Mask 이미지를 생성한다. 

  (5) 생성된 Mask 이미지와 소스 이미지를 이용하여 각각의 Emitter에 대해 평균값을 계산하여 Intensity를 구한다. 

  (6) 저장된 List는 Emitter의 순서가 뒤죽박죽이기 때문에 위치의 X값을 기준으로 Sort를 해야 한다.

저장된 NFP 정보

4. 구현된 클래스

public class EmitterInform     // Emitter 정보를 저장하기 위한 클래스 
{
    public System.Drawing.Point Position { get; set; }     // Emitter 의 위치 좌표
    public double Intensity { get; set; }               // Emitter 의 밝기 

    public EmitterInform(System.Drawing.Point Position, double Intensity)
    {
        this.Position = Position;
        this.Intensity = Intensity;
    }
}
//----------------------------------------------------------------------------------------------------
public class NFP_Process
{
    private Mat sourceImage;
    private int ThresholdRangeMin = 10;
    private int ThresholdRangeMax = 255;

    //----------------------------------------------------------------------------------------------------
    public NFP_Process()
    {
        sourceImage = new Mat();
    }
    //----------------------------------------------------------------------------------------------------
    public void imageLoad(string fileName)
    {
        sourceImage = Cv2.ImRead(fileName, ImreadModes.Grayscale);
    }
    //----------------------------------------------------------------------------------------------------
    public void imageLoad(string fileName, PictureBox pictureBox)
    {
        sourceImage = Cv2.ImRead(fileName, ImreadModes.Grayscale);
        pictureBox.Image = BitmapConverter.ToBitmap(sourceImage);
    }
    //----------------------------------------------------------------------------------------------------
    public void setThresholdRange(int Min, int Max)
    {
        if (Max > Min)
        {
            if (Min > 0 || Min < 256) ThresholdRangeMin = Min;
            if (Max > 0 || Max < 256) ThresholdRangeMax = Max;
        }
    }
    //----------------------------------------------------------------------------------------------------
    public int SearchEmitters(List<EmitterInform> emittersInforms)
    {
        int Count = 0;  // emitter 갯수  
        Mat binaryImage = new Mat();

        Cv2.Threshold(sourceImage, binaryImage, ThresholdRangeMin, ThresholdRangeMax, ThresholdTypes.Binary);   // Source Image를 이진 이미지로 변환한다. 
        Cv2.FindContours(binaryImage, out OpenCvSharp.Point[][] contour, out HierarchyIndex[] hierarchy, RetrievalModes.Tree, ContourApproximationModes.ApproxSimple);  // Emitters 의 외형을 추출한다. 

        emittersInforms.Clear();

        if (contour.Length > 0)     // emitter를 정상적으로 찾았을 경우 
        {
            foreach (var emitter in contour)
            {
                Moments mmt = Cv2.Moments(emitter);     // emitter의 모멘트 정보를 추출

                int cx = (int)(mmt.M10 / mmt.M00);   // emitter 중심점의 X 좌표
                int cy = (int)(mmt.M01 / mmt.M00);   // emitter 중심점의 Y 좌표

                if (cx > 0 && cy > 0)
                {
                    Mat mask = Mat.Zeros(sourceImage.Height, sourceImage.Width, MatType.CV_8UC1);  // 마스크 이미지를 초기화 한다. 
                    Cv2.DrawContours(mask, contour, Count, Scalar.White, 2, LineTypes.Link4, hierarchy);  // Emitter 의 외형을 그린다. 
                    Cv2.FloodFill(mask, new OpenCvSharp.Point(cx, cy), Scalar.White);  // mask 이미지에서 Emitter 외형의 내부를 채운다. 

                    Scalar value = Cv2.Mean(sourceImage, mask);  // 소스 이미지에서 마스킹된 emitter의 Pixel 값들의 평균을 구한다. 

                    emittersInforms.Add(new EmitterInform(new System.Drawing.Point(cx, cy), Math.Round(value.Val0, 3)));  // emitter 정보를 저장한다. 
                    Count++;
                }
            }

            emittersInforms.Sort((InformA, InformB) => InformA.Position.X.CompareTo(InformB.Position.X));     // searching 된 emitter들을 X 좌표값을 기준으로 sorting한다. 
        }

        return Count;
    }
    //----------------------------------------------------------------------------------------------------
}

5. 사용방법 

 

private void button2_Click(object sender, EventArgs e)
{

    NFP_Process nfp_Process = new NFP_Process();    //class 객체 생성

    List<EmitterInform> emitterInforms= new List<EmitterInform>();  // Emitter 의 정보가 저장될 List
    nFP_Process.imageLoad("nfp_image.png");  // 저장된 이미지 loading

    int count = nfp_Process .SearchEmitters(emitterInforms);  // NFP search
    dataGridView1.DataSource = emitterInforms;  // 써칭된 정보를 dataGridView 에 출력 
}

 

6. 기타 

  (1)저장된 영상의 상태에 따라 ThresholdRangeMin, ThresholdRangeMax 값을 조정하면 더 정확한 정보를 추출할 수 있다,

반응형

댓글()