The Canny edge detector (Deteksi tepi) is an edge detection operator that uses a multi-stage algorithm to detect a wide range of edges in images. It was developed by John F. Canny in 1986. Canny also produced a computational theory of edge detection explaining why the technique works.. Berikut adalah contoh code canny detection pada opencv menggunakan c++
#include <opencv2/opencv.hpp>
#include <iostream>
#include <wx/wxprec.h>
#include <wx/wx.h>
#include <wx/textctrl.h>
#include <wx/bookctrl.h>
#include <wx/toplevel.h>
#include <wx/dcclient.h>
#include <iostream>
#include <wx/dcmemory.h>
#include <wx/log.h>
#include <wx/graphics.h>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#ifdef __WXMAC__
#include <ApplicationServices/ApplicationServices.h>
ProcessSerialNumber PSN;
//GetCurrentProcess(&PSN);
//TransformProcessType(&PSN,kProcessTransformToForegroundApplication);
#endif
//#define FormUtama_STYLE wxCAPTION | wxRESIZE_BORDER | wxSYSTEM_MENU | wxRESIZE_BORDER | wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxCLOSE_BOX
//#define FormUtama_STYLE wxCAPTION | wxRESIZE_BORDER | wxSYSTEM_MENU | wxRESIZE_BORDER | wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxCLOSE_BOX
// Declare some IDs. These are arbitrary.
const int BOOKCTRL = 100;
const int BUTTON1 = 101;
const int BUTTON2 = 102;
const int LISTBOX1 = 103;
const int TEXTBOX1 = 104;
const int FILE_QUIT = wxID_EXIT;
const int HELP_ABOUT = wxID_ABOUT;
const int ID_WXPANEL3 = 1003;
const int ID_WXPANEL2 = 1002;
const int ID_WXPANEL1 = 1001;
const wxEventTypeTag<wxThreadEvent> wxEVT_THREAD_UPDATE(wxNewEventType());
using namespace cv;
using namespace std;
class MyFrame : public wxFrame , public wxThreadHelper {
public:
wxPanel* WxPanel1;
wxBoxSizer *sizer;
wxStaticBitmap *image;
MyFrame(const wxString& title,const wxSize& size); //,long style);
void OnButton1( wxCommandEvent& event );
void OnButton2( wxCommandEvent& event );
void OnQuit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
void OnThreadUpdate(wxThreadEvent& evt);
void DoStartALongTask();
void CannyDetect();
protected:
wxCriticalSection m_cs_image;
wxImage m_video_image;
cv::Mat captured_image;
virtual wxThread::ExitCode Entry();
void Render(wxDC &dc);
void OnIdle(wxIdleEvent& event);
void OnClose(wxCloseEvent& event);
void OnMenuExit(wxCommandEvent& event);
//void OnCameraControls(wxCommandEvent& event);
void OnUpdateUI(wxUpdateUIEvent& event);
DECLARE_EVENT_TABLE();
};
// Attach the event handlers. Put this after MyFrame declaration.
BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_BUTTON(BUTTON1, MyFrame::OnButton1)
EVT_BUTTON(BUTTON2, MyFrame::OnButton2)
EVT_MENU(FILE_QUIT, MyFrame::OnQuit)
EVT_MENU(HELP_ABOUT, MyFrame::OnAbout)
EVT_CLOSE(MyFrame::OnClose)
END_EVENT_TABLE();
MyFrame::MyFrame(const wxString& title,const wxSize& size):
wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, size)
{
//SetIcon(wxICON(sample));
SetBackgroundColour(wxColour(77,77,77));
//top menubar
wxMenu *fileMenu = new wxMenu;
wxMenu *helpMenu = new wxMenu;
helpMenu->Append(HELP_ABOUT, _T("&About...\tF1"), _T("Show about dialog"));
fileMenu->Append(FILE_QUIT, _T("E&xit\tAlt-X"), _T("Quit this program"));
wxMenuBar* menuBar = new wxMenuBar();
menuBar->Append(fileMenu, _T("&File"));
menuBar->Append(helpMenu, _T("&Help"));
//bottom statusbar 2 column
CreateStatusBar(2);
SetStatusText(_T("So far so good."), 0);
SetStatusText(_T("Welcome."), 1);
sizer = new wxBoxSizer(wxHORIZONTAL);
WxPanel1 = new wxPanel(this, ID_WXPANEL1, wxPoint(20, 20), wxSize(280, 281));
WxPanel1->SetBackgroundColour(wxColour(55,55,55));
WxPanel1->SetSizer(sizer);
//load initial image to wxbitmap panel
image = new wxStaticBitmap(WxPanel1, wxID_ANY,
wxBitmap("mobil.jpg", wxBITMAP_TYPE_JPEG), wxPoint(0,0), WxPanel1->GetSize()); //wxSize(100, 500));
//autosize image
sizer->Add(image, 1, wxEXPAND);
sizer->SetSizeHints(this);
wxPanel *WxPanel2 = new wxPanel(this, ID_WXPANEL2, wxPoint(400, 20), wxSize(180, 136));
WxPanel2->SetBackgroundColour(wxColour(44,44,44));
wxButton *m_btn_2 = new wxButton(WxPanel2, 0, wxT("Panel 2") , wxPoint(10,10), wxSize(100,30));
//wxPoint(int x, int y)
//wxSize(int width, int height)
wxPanel *WxPanel3 = new wxPanel(this, ID_WXPANEL3, wxPoint(400, 165), wxSize(180, 136));
WxPanel3->SetBackgroundColour(wxColour(99,99,99));
wxButton *m_btn_3 = new wxButton(WxPanel3, 0, wxT("Panel 3") , wxPoint(10,10), wxSize(100,30));
wxPanel *WxPanel4 = new wxPanel(this, ID_WXPANEL3, wxPoint(0, 400), wxSize(600, 50));
WxPanel4->SetBackgroundColour(wxColour(99,99,00));
//add button to panel 4
wxButton *button1=new wxButton(WxPanel4, BUTTON1, _T("Start &1"), wxPoint(10,10), wxSize(100,30) );
wxButton *button2=new wxButton(WxPanel4, BUTTON2, _T("Stop &2"), wxPoint(130,10), wxSize(100,30) );
//SetIcon(wxNullIcon);
SetSize(8,8,600,490);
Center();
SetMenuBar(menuBar);
// It is also possible to use event tables, but dynamic binding is simpler.
Bind(wxEVT_THREAD_UPDATE, &MyFrame::OnThreadUpdate, this);
}
void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
{
Close(true);
}
void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
{
wxString msg;
msg.Printf( _T("About.\n")
_T("Selamat Datang di %s"), wxVERSION_STRING);
wxMessageBox(msg, _T("About My Program"), wxOK | wxICON_INFORMATION, this);
}
void MyFrame::OnButton1(wxCommandEvent& WXUNUSED(event))
{
wxMessageBox("Click1", "Click", wxOK | wxICON_INFORMATION, this);
};
void MyFrame::OnButton2(wxCommandEvent& WXUNUSED(event))
{
wxMessageBox("Click2", "Click", wxOK | wxICON_INFORMATION, this);
}
/*thead handle*/
void MyFrame::OnThreadUpdate(wxThreadEvent& evt)
{
wxClientDC dc(this);
this->Render(dc);
}
void MyFrame::Render(wxDC &dc)
{
if (!dc.IsOk() || !m_video_image.IsOk()) return;
//cout << captured_image;
int win_width = this->GetClientSize().GetX();
int win_height = this->GetClientSize().GetY();
wxCriticalSectionLocker lock(m_cs_image);
int img_width = m_video_image.GetSize().GetX();
int img_height = m_video_image.GetSize().GetY();
int new_img_width = (img_width * win_height) / img_height;
int new_img_height = (img_height * win_width) / img_width;
int width_diff = (win_width - new_img_width) / 2;
int height_diff = (win_height - new_img_height) / 2;
//test image
//cv::imshow("preview", captured_image);
//cout << " width : " << img_width << endl;
//cout << " height : " << img_height << endl;
//draw opencvBitmap to image panel
if (height_diff >= 0)
{
image->SetBitmap(wxBitmap(m_video_image.Scale(win_width, new_img_height)));
//dc.DrawBitmap(wxBitmap(m_video_image.Scale(win_width, new_img_height)), 0, height_diff);
} else{
image->SetBitmap(wxBitmap(m_video_image.Scale(new_img_width, win_height)));
//dc.DrawBitmap(wxBitmap(m_video_image.Scale(new_img_width, win_height)), width_diff, 0);
}
//draw text
//dc.DrawText(strbuffer, 0, 0);
}
/*
void MyFrame::OnCameraControls(wxCommandEvent& event)
{
int id = event.GetId();
switch (id)
{
case ID_MENU_START_CAMERA:
case ID_TB_START_CAMERA:
{
if (CreateThread(wxTHREAD_DETACHED) != wxTHREAD_NO_ERROR)
{
wxLogMessage("Error: Could not create the worker thread!");
return;
}
if (GetThread()->Run() != wxTHREAD_NO_ERROR)
{
wxLogMessage("Error: Could not run the worker thread!");
return;
}
break;
}
case ID_MENU_PAUSE_RESUME:
case ID_TB_PAUSE_RESUME:
{
if (!this->GetThread()) return;
if (!this->GetThread()->IsPaused())
{
this->GetThread()->Pause();
wxLogMessage("Camera paused");
} else
{
this->GetThread()->Resume();
wxLogMessage("Camera running");
}
break;
}
default: break;
}
}
*/
std::string time_in_HH_MM_SS_MMM()
{
using namespace std::chrono;
// get current time
auto now = system_clock::now();
// get number of milliseconds for the current second
// (remainder after division into seconds)
auto ms = duration_cast<milliseconds>(now.time_since_epoch()) % 1000;
// convert to std::time_t in order to convert to std::tm (broken time)
auto timer = system_clock::to_time_t(now);
// convert to broken time
std::tm bt = *std::localtime(&timer);
std::ostringstream oss;
oss << std::put_time(&bt, "%H:%M:%S"); // HH:MM:SS
oss << '.' << std::setfill('0') << std::setw(3) << ms.count();
return oss.str();
}
/***this is function for canny detection***/
void MyFrame::CannyDetect(){
Mat dst, src_gray, detected_edges;
int lowThreshold = 0;
int ratio = 3;
int kernel_size = 3;
int max_lowThreshold = 200;
cvtColor( captured_image, src_gray, COLOR_BGR2GRAY );
blur( src_gray, detected_edges, cv::Size(3,3) );
Canny( detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size );
dst = Scalar::all(0);
captured_image.copyTo( dst, detected_edges);
captured_image = dst;
}
wxThread::ExitCode MyFrame::Entry()
{
// VERY IMPORTANT: this function gets executed in the secondary thread context!
// Do not call any GUI function inside this function; rather use wxQueueEvent():
wxLogMessage("Initializing camera...");
// Initialize the OpenCV capture device. This has to be done in the secondary thread context!
cv::VideoCapture capture_device(0);
if (!capture_device.isOpened())
{
wxLogMessage("Error: Couldn't initialize camera!");
return (wxThread::ExitCode)0;
}
// here we do our long task, periodically calling TestDestroy():
while (!this->GetThread()->TestDestroy())
{
capture_device >> captured_image;
if (captured_image.empty()) return static_cast<wxThread::ExitCode>(0);
//apply canny detection
this->CannyDetect();
//draw text image info
cv::Size sz = captured_image.size();
int img_height = sz.height;
int img_width = sz.width;
char strbuffer[100];
int retVal=sprintf(strbuffer, "size: %d x %d Time : %s", img_width, img_height, time_in_HH_MM_SS_MMM().c_str());
cv::putText(captured_image,
strbuffer,
cv::Point(40,40), // Coordinates
cv::FONT_HERSHEY_COMPLEX_SMALL, // Font
1.0, // Scale. 2.0 = 2x bigger
cv::Scalar(0,0,0), // BGR Color
2, // Line Thickness (Optional)
LINE_AA); // Anti-alias (Optional)
//change color
cv::cvtColor(captured_image, captured_image, COLOR_BGR2RGB);
{
wxCriticalSectionLocker lock(m_cs_image);
m_video_image = wxImage(captured_image.cols, captured_image.rows, captured_image.data, true);
}
wxQueueEvent(this, new wxThreadEvent(wxEVT_THREAD_UPDATE));
this->GetThread()->Sleep(10);
}
return static_cast<wxThread::ExitCode>(0);
}
void MyFrame::OnClose(wxCloseEvent& event)
{
// important: before terminating, we _must_ wait for our joinable
// thread to end, if it's running; in fact it uses variables of this
// instance and posts events to *this event handler
if (!this->GetThread())
{
this->Destroy();
return;
}
if (this->GetThread()->Delete() != wxTHREAD_NO_ERROR)
wxLogMessage("Error: Can't delete the camera thread!");
while (true)
{
if (!this->GetThread()) break;
this->GetThread()->Sleep(15);
}
this->Destroy();
event.Skip();
/*
if (GetThread() && // DoStartALongTask() may have not been called
GetThread()->IsRunning())
GetThread()->Wait();
Destroy();
*/
}
void MyFrame::OnUpdateUI(wxUpdateUIEvent& event)
{
event.Skip();
}
void MyFrame::OnIdle(wxIdleEvent& event)
{
event.Skip();
}
void MyFrame::OnMenuExit(wxCommandEvent& event)
{
this->Close();
}
void MyFrame::DoStartALongTask()
{
// we want to start a long task, but we don't want our GUI to block
// while it's executed, so we use a thread to do it.
if (CreateThread(wxTHREAD_JOINABLE) != wxTHREAD_NO_ERROR)
{
wxLogError("Could not create the worker thread!");
return;
}
// go!
if (GetThread()->Run() != wxTHREAD_NO_ERROR)
{
wxLogError("Could not run the worker thread!");
return;
}
}
/*app part*/
class MyApp : public wxApp {
public: bool OnInit(){
//wxImage::AddHandler(new wxPNGHandler());
wxImage::AddHandler(new wxJPEGHandler());
MyFrame *frame = new MyFrame(_T("OpenCV Camera CannyDetection"),wxSize(600, 400));
frame->Center(wxBOTH);
frame->Show();
SetTopWindow(frame);
//start camera in wxthread
frame->DoStartALongTask();
return true;
}
};
IMPLEMENT_APP(MyApp);
/*
##compile command:
g++ -ggdb -std=c++11 \
`pkg-config --cflags opencv4` \
frame.cpp -o frame \
`pkg-config --cflags --libs opencv4` \
`wx-config --cxxflags --libs`
*/
Result: