C# OpenCV4
직선 검출
직선에 대한 방정식은 우리가 흔히 알고 있는 기울기(a)와 y절편(b)로 표현되는 𝑦=a𝑥+b도 있지만 삼각함수에 의한 매개변수 방정식으로써는 r = 𝑥cos𝜃 + 𝑦sin𝜃 로도 표현됩니다.
선형 Hough 변환의 최종 결과는 누산기와 유사한 2차원 배열(행렬)입니다. 이 행렬의 한 차원은 양자화된 각도 θ 이고 다른 차원은 양자화된 거리 r 입니다. 행렬의 각 요소는 양자화된 매개변수( r , θ ) 로 표시되는 선에 위치한 점 또는 픽셀의 합과 같은 값을 갖습니다 . 따라서 값이 가장 높은 요소는 입력 이미지에서 가장 많이 표현되는 직선을 나타냅니다.
C#으로 구현한 Code를 확인해보겠습니다.
Houghline (허프변환)
왼쪽 - Hough line, 오른쪽 - Input Image
using OpenCvSharp;
using OpenCvSharp.Extensions;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace HoughlineTest
{
public partial class Form1 : Form
{
Mat Img;
public Form1()
{
InitializeComponent();
Img = BitmapConverter.ToMat(Properties.Resources.WAFER);
HoughLine(Img);
}
private void HoughLine(Mat Org)
{
LineSegmentPolar[] lines;
using (Mat InputImg = Org.Clone())
using (Mat img = Org.Clone())
using (Mat img_gray = new Mat())
{
Mat result = img;
// 검색에 용이하도록 그레이스케일로 변환
Cv2.CvtColor(img, img_gray, ColorConversionCodes.BGR2GRAY);
// 캐니엣지처리 먼저 해준다.
Cv2.Canny(img_gray, img_gray, 50, 200);
// 마지막 파라미터 Threshold 값 조정으로 임계설정을 해준다.
lines = Cv2.HoughLines(img_gray, 1, Math.PI / 180, 200);
if (lines.Length <= 0)
return;
// 찾은 라인들을 그려준다.
for (int i = 0; i < lines.Length; i++)
{
float rho = lines[i].Rho;
float theta = lines[i].Theta;
double a = Math.Cos(theta);
double b = Math.Sin(theta);
double x0 = a * rho;
double y0 = b * rho;
OpenCvSharp.Point p1 = new OpenCvSharp.Point(Math.Round(x0 + 1000 * (-b)), Math.Round(y0 + 1000 * a));
OpenCvSharp.Point p2 = new OpenCvSharp.Point(Math.Round(x0 - 1000 * (-b)), Math.Round(y0 - 1000 * a));
Cv2.Line(result, p1, p2, Scalar.Red, 1);
}
Cv2.ImShow("Input Image", InputImg);
Cv2.ImShow("Hough Transform", result);
result.Release();
result.Dispose();
}
}
}
}
HoughlineP (확률적 허프변환)
왼쪽 - Hough line, 오른쪽 - Input Image
using OpenCvSharp;
using OpenCvSharp.Extensions;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace HoughlineTest
{
public partial class Form1 : Form
{
Mat Img;
public Form1()
{
InitializeComponent();
Img = BitmapConverter.ToMat(Properties.Resources.WAFER);
HoughLineP(Img);
}
private void HoughLineP(Mat Org)
{
LineSegmentPoint[] linesP;
using (Mat InputImg = Org.Clone())
using (Mat img = Org.Clone())
using (Mat img_gray = new Mat())
{
Mat result = img;
Cv2.CvtColor(img, img_gray, ColorConversionCodes.BGR2GRAY);
// 허프변환 할라고 캐니엣지 처리 먼저해줌
Cv2.Canny(img_gray, img_gray, 50, 200);
// ===================================================================================================================
// 일반 허프변환(HoughLine)이 아니라 확률적 허프변환(HoughLinesP)임
// 속성 -> HoughLinesP(이미지, 픽셀 해상도 0~1(보통 1을씀), 선회전 각도(모든각도에서의 직선검출은 PI / 180), threshold, 직선 최소길이, 선위의 점들사이 거리)
// 일반 허프변환이랑 다른점이 <직선 최소길이>랑 <선위의 점들사이 거리>를 추가 계산하기때매 구하고자하는 선분을 두번 걸러줌 그렇기때매 연산속도도 더 빠름
// ===================================================================================================================
linesP = Cv2.HoughLinesP(img_gray, 1, Math.PI / 180, 100, 50, 100);
if (linesP.Length <= 0)
return;
for (int i = 0; i < linesP.Length; i++)
{
Cv2.Line(result, linesP[i].P1.X, linesP[i].P1.Y, linesP[i].P2.X, linesP[i].P2.Y, Scalar.Red, 1);
}
Cv2.ImShow("Input Image", InputImg);
Cv2.ImShow("Hough Transform", result);
result.Release();
result.Dispose();
}
}
}
}
확률적 허프변환이 허프변환보다 최소길이와 선 위의 점들 사이 거리라는 조건이 있어 연산속도가
더 빠른것을 확인 할 수 있습니다.
저는 여기에 가장 긴 직선을 뽑아서 그려주는 기능이 필요하여 추가해보았습니다.
Code
using OpenCvSharp;
using OpenCvSharp.Extensions;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace HoughlineTest
{
public partial class Form1 : Form
{
Mat Img;
public Form1()
{
InitializeComponent();
Img = BitmapConverter.ToMat(Properties.Resources.WAFER);
HoughLineP(Img);
}
private void HoughLineP(Mat Org)
{
List<LineSegmentPoint[]> LongestLine = new List<LineSegmentPoint[]>();
int Longest_idx = 0;
double Longest_max = 0;
LineSegmentPoint[] linesP;
using (Mat InputImg = Org.Clone())
using (Mat img = Org.Clone())
using (Mat img_gray = new Mat())
{
Mat result = img;
Cv2.CvtColor(img, img_gray, ColorConversionCodes.BGR2GRAY);
Cv2.Canny(img_gray, img_gray, 50, 200);
linesP = Cv2.HoughLinesP(img_gray, 1, Math.PI / 180, 100, 50, 100);
if (linesP.Length <= 0)
return;
for (int i = 0; i < linesP.Length; i++)
{
double EuclideanDistance;
// 유클리드 거리 공식으로 계산
// (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)
EuclideanDistance = ((linesP[i].P2.X - linesP[i].P1.X) * (linesP[i].P2.X - linesP[i].P1.X)) +
((linesP[i].P2.Y - linesP[i].P1.Y) * (linesP[i].P2.Y - linesP[i].P1.Y));
EuclideanDistance = Math.Sqrt(EuclideanDistance);
if (Longest_max < EuclideanDistance)
{
Longest_idx = i;
Longest_max = EuclideanDistance;
}
}
// 가장 긴 선만 그려줌
Cv2.Line(result, linesP[Longest_idx].P1.X, linesP[Longest_idx].P1.Y, linesP[Longest_idx].P2.X, linesP[Longest_idx].P2.Y, Scalar.Blue, 1);
int dx = linesP[Longest_idx].P2.X - linesP[Longest_idx].P1.X;
int dy = linesP[Longest_idx].P2.Y - linesP[Longest_idx].P1.Y;
double rad = Math.Atan2((double)dx, (double)dy);
double degree = Math.Abs(rad * 180) / Math.PI;
Cv2.ImShow("Input Image", InputImg);
Cv2.ImShow("Hough Transform", result);
result.Release();
result.Dispose();
}
}
}
}
OpenCV 직선 검출은 여러모로 유용하게 쓰이는 기능인 것 같습니다.
가장 긴 직선을 찾아 평탄 확인을 한다던지...
산업용 카메라 Vision으로 이미지를 받아올때 직선이 가장 많이 검출이 되는 위치가 포커싱 위치라던지 등등
여러모로 잘 쓰고있습니다.
도움이 많이 되셨으면 좋겠네요!
'🔥 Programming > OpenCV' 카테고리의 다른 글
[OpenCV4] C# OpenCV4 배경색 변경 (0) | 2022.11.10 |
---|---|
[OpenCV4] C# OpenCV4 Template Matching (0) | 2022.10.19 |
[OpenCV4] C# OpenCV4 WebCam 사용하기 (0) | 2022.09.23 |
[OpenCV4] C# OpenCV4 Gamma (0) | 2022.09.23 |
[OpenCV4] C# OpenCV4 두 이미지 합치기 (0) | 2022.08.17 |