Imam Ferianto Blogs

sekedar catatan kecil saja

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: