안녕하세요
"코딩연습생"입니다ㅎㅎ
저번 포스팅에서 컨투어(윤곽선) 검출을 포스팅 했었는데요
https://codingman.tistory.com/65
컨투어 찾기 기능을 통해 모형이나 특정 조건에 의한 영역의 윤곽선을 찾기를 해볼려고 합니다
그래서 이번 포스팅에서 준비한것은 이미지의 사각, 원형에 대한 특정 조건을 검출하여 윤곽선을
표기 해보도록 하겠습니다
역시나 연속적인 OpenCvSharp 포스팅을 진행하고 있기에 기초 설정이라든지 공통 디자인 부분같은 경우
생략하고 진행하고 있습니다
불편하시더라도 기존 포스팅을 찾아 확인하신 다음에 이어 준비한 내용을 보시길 바랍니다
https://codingman.tistory.com/49
그럼 시작해보도록 하겠습니다
언제나 처럼 사각,원형을 검출하기 위한 버튼이 있어야 겠죠?
[디자인]
- 메뉴에서 다음과 같이 버튼 메뉴를 생성합니다
- 생성된 버튼에 클릭 했을때 동작 할 수 있도록 클릭 이벤트를 설정해 줍니다
- 여기까지는 그렇게 어렵지 않습니다
[Source Code]
private void 사각형찾기ToolStripMenuItem_Click(object sender, EventArgs e)
{
if (src == null) return;
using (Squre Sq = new Squre())
using (IplImage temp = Sq.Squares(src))
{
result = temp.Clone();
}
pictureBoxIpl2.ImageIpl = result;
}
private void 원형찾기ToolStripMenuItem_Click(object sender, EventArgs e)
{
if (src == null) return;
using (Squre Sq = new Squre())
using (IplImage temp = Sq.HoughCircles(src))
{
result = temp.Clone();
}
pictureBoxIpl2.ImageIpl = result;
}
- 다음에 사각과 원형을 검출해주고 그 위에 윤곽선을 그려줄 기능을 할 클래스를 작성 할께요
- 생성된 클래스 파일에 아래와 같이 소스코드를 생성해 줍니다
using System;
using System.Collections.Generic;
using System.Text;
using OpenCvSharp;
namespace OpenCV_V1
{
class Squre : IDisposable
{
const int Thresh = 50;
IplImage squr;
static double Angle(CvPoint pt1, CvPoint pt2, CvPoint pt0)
{
double dx1 = pt1.X - pt0.X;
double dy1 = pt1.Y - pt0.Y;
double dx2 = pt2.X - pt0.X;
double dy2 = pt2.Y - pt0.Y;
return (dx1 * dx2 + dy1 * dy2) / Math.Sqrt((dx1 * dx1 + dy1 * dy1) * (dx2 * dx2 + dy2 * dy2) + 1e-10);
}
public IplImage Squares(IplImage src)
{
CvMemStorage storage = new CvMemStorage(0);
DrawSquares(src, FindSquares4(src, storage));
return squr;
}
//원형 검출
public IplImage HoughCircles(IplImage src)
{
squr = new IplImage(src.Size, BitDepth.U8, 3);
IplImage gray = new IplImage(src.Size, BitDepth.U8, 1);
Cv.Copy(src, squr);
Cv.CvtColor(src, gray, ColorConversion.BgrToGray);
Cv.Smooth(gray, gray, SmoothType.Gaussian, 9);
CvMemStorage Storage = new CvMemStorage();
CvSeq<CvCircleSegment> circles = Cv.HoughCircles(gray, Storage, HoughCirclesMethod.Gradient, 1, 100, 150, 50, 0, 0);
foreach (CvCircleSegment item in circles)
{
Cv.Circle(squr, item.Center, (int)item.Radius, CvColor.Blue, 3);
}
return squr;
}
//사각 검출
static CvPoint[] FindSquares4(IplImage img, CvMemStorage storage)
{
const int N = 11;
CvSize sz = new CvSize(img.Width & -2, img.Height & -2);
IplImage timg = img.Clone(); // make a copy of input image
IplImage gray = new IplImage(sz, BitDepth.U8, 1);
IplImage pyr = new IplImage(sz.Width / 2, sz.Height / 2, BitDepth.U8, 3);
// create empty sequence that will contain points -
// 4 points per square (the square's vertices)
CvSeq<CvPoint> squares = new CvSeq<CvPoint>(SeqType.Zero, CvSeq.SizeOf, storage);
// select the maximum ROI in the image
// with the width and height divisible by 2
timg.ROI = new CvRect(0, 0, sz.Width, sz.Height);
// down-scale and upscale the image to filter out the noise
Cv.PyrDown(timg, pyr, CvFilter.Gaussian5x5);
Cv.PyrUp(pyr, timg, CvFilter.Gaussian5x5);
IplImage tgray = new IplImage(sz, BitDepth.U8, 1);
// find squares in every color plane of the image
for (int c = 0; c < 3; c++)
{
// extract the c-th color plane
timg.COI = c + 1;
Cv.Copy(timg, tgray, null);
// try several threshold levels
for (int l = 0; l < N; l++)
{
// hack: use Canny instead of zero threshold level.
// Canny helps to catch squares with gradient shading
if (l == 0)
{
// apply Canny. Take the upper threshold from slider
// and set the lower to 0 (which forces edges merging)
Cv.Canny(tgray, gray, 0, Thresh, ApertureSize.Size5);
// dilate canny output to remove potential
// holes between edge segments
Cv.Dilate(gray, gray, null, 1);
}
else
{
// apply threshold if l!=0:
// tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0
Cv.Threshold(tgray, gray, (l + 1) * 255.0 / N, 255, ThresholdType.Binary);
}
// find contours and store them all as a list
CvSeq<CvPoint> contours;
Cv.FindContours(gray, storage, out contours, CvContour.SizeOf, ContourRetrieval.List, ContourChain.ApproxSimple, new CvPoint(0, 0));
// test each contour
while (contours != null)
{
// approximate contour with accuracy proportional
// to the contour perimeter
CvSeq<CvPoint> result = Cv.ApproxPoly(contours, CvContour.SizeOf, storage, ApproxPolyMethod.DP, contours.ContourPerimeter() * 0.02, false);
// square contours should have 4 vertices after approximation
// relatively large area (to filter out noisy contours)
// and be convex.
// Note: absolute value of an area is used because
// area may be positive or negative - in accordance with the
// contour orientation
if (result.Total == 4 && Math.Abs(result.ContourArea(CvSlice.WholeSeq)) > 1000 && result.CheckContourConvexity())
{
double s = 0;
for (int i = 0; i < 5; i++)
{
// find minimum Angle between joint
// edges (maximum of cosine)
if (i >= 2)
{
double t = Math.Abs(Angle(result[i].Value, result[i - 2].Value, result[i - 1].Value));
s = s > t ? s : t;
}
}
// if cosines of all angles are small
// (all angles are ~90 degree) then write quandrange
// vertices to resultant sequence
if (s < 0.3)
{
for (int i = 0; i < 4; i++)
{
//Console.WriteLine(result[i]);
squares.Push(result[i].Value);
}
}
}
// take the next contour
contours = contours.HNext;
}
}
}
// release all the temporary images
gray.Dispose();
pyr.Dispose();
tgray.Dispose();
timg.Dispose();
return squares.ToArray();
}
/// <summary>
/// the function draws all the squares in the image
/// </summary>
/// <param name="img"></param>
/// <param name="squares"></param>
private void DrawSquares(IplImage img, CvPoint[] squares)
{
using (IplImage cpy = img.Clone())
{
// read 4 sequence elements at a time (all vertices of a square)
for (int i = 0; i < squares.Length; i += 4)
{
CvPoint[] pt = new CvPoint[4];
// read 4 vertices
pt[0] = squares[i + 0];
pt[1] = squares[i + 1];
pt[2] = squares[i + 2];
pt[3] = squares[i + 3];
// draw the square as a closed polyline
Cv.PolyLine(cpy, new CvPoint[][] { pt }, true, CvColor.Green, 3, LineType.AntiAlias, 0);
}
// show the resultant image
squr = cpy.Clone();
}
}
public void Dispose()
{
if (squr != null) Cv.ReleaseImage(squr);
}
}
}
- 사실 이렇게 많은 소스코드 중에 핵심 함수는 두가지 밖에 없습니다
(제가 아는 범위에서 말씀드리는것이기 때문에 사실과 다를수 있습니다);;ㅎㅎ
*원형을 찾기 위한 함수 HoughCircles
*사각을 찾기 위한 함수 ApproxPoly
[결과 창]
*원형 검출 (검출 인식율 : 70%)
*사각 검출(인식율 : 90%)
이 두가지 함수를 공부 하시고 응용하시면 5각형, 6각형의 다각형 모형도 검출이 가능하니
공부해서 응용해보시면 좋을거 같아요
혹시 공부하시고 터득하신 정보가 있으시다면 공유 해주시기 바랍니다
감사합니다
'코딩정보 > OpenCv' 카테고리의 다른 글
[C#] OpenCvSharp 이미지 컨벡스헐 그리기 (0) | 2020.01.10 |
---|---|
[C#] OpenCvSharp 포인트를 찾아 영역 지정 하기 (0) | 2020.01.09 |
[C#] OpenCvSharp 이미지 컨투어(윤곽선) 찾기 (0) | 2020.01.08 |
[C#] OpenCvSharp 사용자 지정 마스크 사용하기 (0) | 2020.01.08 |
[C#] OpenCvSharp 이미지 모폴로지 연산 처리하기 (0) | 2020.01.07 |