الگوریتم Canny در سی پلاس پلاس قسمت 4
مرحله 4: سرکوب لبه های غیر حداکثر
آخرین مرحله، پیدا کردن لبه های ضعیف که موازی با لبه های قوی هستند و از بین بردن آنهاست. این عمل، با بررسی پیکسل های عمود بر یک پیکسل لبه خاص و حذف لبه های غیر حداکثرانجام شده است. کد مورد استفاده بسیار مشابه کد ردیابی لبه است.
</pre> <pre>#include "stdafx.h" #include "tripod.h" #include "tripodDlg.h" #include "LVServerDefs.h" #include "math.h" #include <fstream> #include <string> #include <iostream> #include <stdlib.h> #include <stdio.h> #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif using namespace std; ///////////////////////////////////////////////////////////////////////////// // CAboutDlg dialog used for App About class CAboutDlg : public CDialog { public: CAboutDlg(); // Dialog Data //{{AFX_DATA(CAboutDlg) enum { IDD = IDD_ABOUTBOX }; //}}AFX_DATA // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CAboutDlg) protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //}}AFX_VIRTUAL // Implementation protected: //{{AFX_MSG(CAboutDlg) //}}AFX_MSG DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) { //{{AFX_DATA_INIT(CAboutDlg) //}}AFX_DATA_INIT } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CAboutDlg) //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) //{{AFX_MSG_MAP(CAboutDlg) // No message handlers //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CTripodDlg dialog CTripodDlg::CTripodDlg(CWnd* pParent /*=NULL*/) : CDialog(CTripodDlg::IDD, pParent) { //{{AFX_DATA_INIT(CTripodDlg) // NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT // Note that LoadIcon does not require a subsequent DestroyIcon in Win32 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); //////////////// Set destination BMP to NULL first m_destinationBitmapInfoHeader = NULL; } ////////////////////// Additional generic functions static unsigned PixelBytes(int w, int bpp) { return (w * bpp + 7) / 8; } static unsigned DibRowSize(int w, int bpp) { return (w * bpp + 31) / 32 * 4; } static unsigned DibRowSize(LPBITMAPINFOHEADER pbi) { return DibRowSize(pbi->biWidth, pbi->biBitCount); } static unsigned DibRowPadding(int w, int bpp) { return DibRowSize(w, bpp) - PixelBytes(w, bpp); } static unsigned DibRowPadding(LPBITMAPINFOHEADER pbi) { return DibRowPadding(pbi->biWidth, pbi->biBitCount); } static unsigned DibImageSize(int w, int h, int bpp) { return h * DibRowSize(w, bpp); } static size_t DibSize(int w, int h, int bpp) { return sizeof (BITMAPINFOHEADER) + DibImageSize(w, h, bpp); } /////////////////////// end of generic functions void CTripodDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CTripodDlg) DDX_Control(pDX, IDC_PROCESSEDVIEW, m_cVideoProcessedView); DDX_Control(pDX, IDC_UNPROCESSEDVIEW, m_cVideoUnprocessedView); //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CTripodDlg, CDialog) //{{AFX_MSG_MAP(CTripodDlg) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDEXIT, OnExit) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CTripodDlg message handlers BOOL CTripodDlg::OnInitDialog() { CDialog::OnInitDialog(); // Add "About..." menu item to system menu. // IDM_ABOUTBOX must be in the system command range. ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { CString strAboutMenu; strAboutMenu.LoadString(IDS_ABOUTBOX); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here // For Unprocessed view videoportal (top one) char sRegUnprocessedView[] = "HKEY_LOCAL_MACHINE\\Software\\UnprocessedView"; m_cVideoUnprocessedView.PrepareControl("UnprocessedView", sRegUnprocessedView, 0 ); m_cVideoUnprocessedView.EnableUIElements(UIELEMENT_STATUSBAR,0,TRUE); m_cVideoUnprocessedView.ConnectCamera2(); m_cVideoUnprocessedView.SetEnablePreview(TRUE); // For binary view videoportal (bottom one) char sRegProcessedView[] = "HKEY_LOCAL_MACHINE\\Software\\ProcessedView"; m_cVideoProcessedView.PrepareControl("ProcessedView", sRegProcessedView, 0 ); m_cVideoProcessedView.EnableUIElements(UIELEMENT_STATUSBAR,0,TRUE); m_cVideoProcessedView.ConnectCamera2(); m_cVideoProcessedView.SetEnablePreview(TRUE); // Initialize the size of binary videoportal m_cVideoProcessedView.SetPreviewMaxHeight(240); m_cVideoProcessedView.SetPreviewMaxWidth(320); // Uncomment if you wish to fix the live videoportal's size // m_cVideoUnprocessedView.SetPreviewMaxHeight(240); // m_cVideoUnprocessedView.SetPreviewMaxWidth(320); // Find the screen coodinates of the binary videoportal m_cVideoProcessedView.GetWindowRect(m_rectForProcessedView); ScreenToClient(m_rectForProcessedView); allocateDib(CSize(320, 240)); // Start grabbing frame data for Procssed videoportal (bottom one) m_cVideoProcessedView.StartVideoHook(0); return TRUE; // return TRUE unless you set the focus to a control } void CTripodDlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialog::OnSysCommand(nID, lParam); } } // If you add a minimize button to your dialog, you will need the code below // to draw the icon. For MFC applications using the document/view model, // this is automatically done for you by the framework. void CTripodDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // device context for painting SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0); // Center icon in client rectangle int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // Draw the icon dc.DrawIcon(x, y, m_hIcon); } else { CDialog::OnPaint(); } } // The system calls this to obtain the cursor to display while the user drags // the minimized window. HCURSOR CTripodDlg::OnQueryDragIcon() { return (HCURSOR) m_hIcon; } void CTripodDlg::OnExit() { // TODO: Add your control notification handler code here // Kill live view videoportal (top one) m_cVideoUnprocessedView.StopVideoHook(0); m_cVideoUnprocessedView.DisconnectCamera(); // Kill binary view videoportal (bottom one) m_cVideoProcessedView.StopVideoHook(0); m_cVideoProcessedView.DisconnectCamera(); // Kill program DestroyWindow(); } BEGIN_EVENTSINK_MAP(CTripodDlg, CDialog) //{{AFX_EVENTSINK_MAP(CTripodDlg) ON_EVENT(CTripodDlg, IDC_PROCESSEDVIEW, 1 /* PortalNotification */, OnPortalNotificationProcessedview, VTS_I4 VTS_I4 VTS_I4 VTS_I4) //}}AFX_EVENTSINK_MAP END_EVENTSINK_MAP() void CTripodDlg::OnPortalNotificationProcessedview(long lMsg, long lParam1, long lParam2, long lParam3) { // TODO: Add your control notification handler code here // This function is called at the camera's frame rate #define NOTIFICATIONMSG_VIDEOHOOK 10 // Declare some useful variables // QCSDKMFC.pdf (Quickcam MFC documentation) p. 103 explains the variables lParam1, lParam2, lParam3 too LPBITMAPINFOHEADER lpBitmapInfoHeader; // Frame's info header contains info like width and height LPBYTE lpBitmapPixelData; // This pointer-to-long will point to the start of the frame's pixel data unsigned long lTimeStamp; // Time when frame was grabbed switch(lMsg) { case NOTIFICATIONMSG_VIDEOHOOK: { lpBitmapInfoHeader = (LPBITMAPINFOHEADER) lParam1; lpBitmapPixelData = (LPBYTE) lParam2; lTimeStamp = (unsigned long) lParam3; grayScaleTheFrameData(lpBitmapInfoHeader, lpBitmapPixelData); doMyImageProcessing(lpBitmapInfoHeader); // Place where you'd add your image processing code displayMyResults(lpBitmapInfoHeader); } break; default: break; } } void CTripodDlg::allocateDib(CSize sz) { // Purpose: allocate information for a device independent bitmap (DIB) // Called from OnInitVideo if(m_destinationBitmapInfoHeader) { free(m_destinationBitmapInfoHeader); m_destinationBitmapInfoHeader = NULL; } if(sz.cx | sz.cy) { m_destinationBitmapInfoHeader = (LPBITMAPINFOHEADER)malloc(DibSize(sz.cx, sz.cy, 24)); ASSERT(m_destinationBitmapInfoHeader); m_destinationBitmapInfoHeader->biSize = sizeof(BITMAPINFOHEADER); m_destinationBitmapInfoHeader->biWidth = sz.cx; m_destinationBitmapInfoHeader->biHeight = sz.cy; m_destinationBitmapInfoHeader->biPlanes = 1; m_destinationBitmapInfoHeader->biBitCount = 24; m_destinationBitmapInfoHeader->biCompression = 0; m_destinationBitmapInfoHeader->biSizeImage = DibImageSize(sz.cx, sz.cy, 24); m_destinationBitmapInfoHeader->biXPelsPerMeter = 0; m_destinationBitmapInfoHeader->biYPelsPerMeter = 0; m_destinationBitmapInfoHeader->biClrImportant = 0; m_destinationBitmapInfoHeader->biClrUsed = 0; } } void CTripodDlg::displayMyResults(LPBITMAPINFOHEADER lpThisBitmapInfoHeader) { // displayMyResults: Displays results of doMyImageProcessing() in the videoport // Notes: StretchDIBits stretches a device-independent bitmap to the appropriate size CDC *pDC; // Device context to display bitmap data pDC = GetDC(); int nOldMode = SetStretchBltMode(pDC->GetSafeHdc(),COLORONCOLOR); StretchDIBits( pDC->GetSafeHdc(), m_rectForProcessedView.left, // videoportal left-most coordinate m_rectForProcessedView.top, // videoportal top-most coordinate m_rectForProcessedView.Width(), // videoportal width m_rectForProcessedView.Height(), // videoportal height 0, // Row position to display bitmap in videoportal 0, // Col position to display bitmap in videoportal lpThisBitmapInfoHeader->biWidth, // m_destinationBmp's number of columns lpThisBitmapInfoHeader->biHeight, // m_destinationBmp's number of rows m_destinationBmp, // The bitmap to display; use the one resulting from doMyImageProcessing (BITMAPINFO*)m_destinationBitmapInfoHeader, // The bitmap's header info e.g. width, height, number of bits etc DIB_RGB_COLORS, // Use default 24-bit color table SRCCOPY // Just display ); SetStretchBltMode(pDC->GetSafeHdc(),nOldMode); ReleaseDC(pDC); // Note: 04/24/02 - Added the following: // Christopher Wagner cwagner@fas.harvard.edu noticed that memory wasn't being freed // Recall OnPortalNotificationProcessedview, which gets called everytime // a frame of data arrives, performs 3 steps: // (1) grayScaleTheFrameData - which mallocs m_destinationBmp // (2) doMyImageProcesing // (3) displayMyResults - which we're in now // Since we're finished with the memory we malloc'ed for m_destinationBmp // we should free it: free(m_destinationBmp); // End of adds } void CTripodDlg::grayScaleTheFrameData(LPBITMAPINFOHEADER lpThisBitmapInfoHeader, LPBYTE lpThisBitmapPixelData) { // grayScaleTheFrameData: Called by CTripodDlg::OnPortalNotificationBinaryview // Task: Read current frame pixel data and computes a grayscale version unsigned int W, H; // Width and Height of current frame [pixels] BYTE *sourceBmp; // Pointer to current frame of data unsigned int row, col; unsigned long i; BYTE grayValue; BYTE redValue; BYTE greenValue; BYTE blueValue; W = lpThisBitmapInfoHeader->biWidth; // biWidth: number of columns H = lpThisBitmapInfoHeader->biHeight; // biHeight: number of rows // Store pixel data in row-column vector format // Recall that each pixel requires 3 bytes (red, blue and green bytes) // m_destinationBmp is a protected member and declared in binarizeDlg.h m_destinationBmp = (BYTE*)malloc(H*3*W*sizeof(BYTE)); // Point to the current frame's pixel data sourceBmp = lpThisBitmapPixelData; for (row = 0; row < H; row++) { for (col = 0; col < W; col++) { // Recall each pixel is composed of 3 bytes i = (unsigned long)(row*3*W + 3*col); // The source pixel has a blue, green andred value: blueValue = *(sourceBmp + i); greenValue = *(sourceBmp + i + 1); redValue = *(sourceBmp + i + 2); // A standard equation for computing a grayscale value based on RGB values grayValue = (BYTE)(0.299*redValue + 0.587*greenValue + 0.114*blueValue); // The destination BMP will be a grayscale version of the source BMP *(m_destinationBmp + i) = grayValue; *(m_destinationBmp + i + 1) = grayValue; *(m_destinationBmp + i + 2) = grayValue; } } } void CTripodDlg::doMyImageProcessing(LPBITMAPINFOHEADER lpThisBitmapInfoHeader) { // doMyImageProcessing: This is where you'd write your own image processing code // Task: Read a pixel's grayscale value and process accordingly unsigned int W, H; // Width and Height of current frame [pixels] unsigned int row, col; // Pixel's row and col positions unsigned long i; // Dummy variable for row-column vector int upperThreshold = 60; // Gradient strength nessicary to start edge int lowerThreshold = 30; // Minimum gradient strength to continue edge unsigned long iOffset; // Variable to offset row-column vector during sobel mask int rowOffset; // Row offset from the current pixel int colOffset; // Col offset from the current pixel int rowTotal = 0; // Row position of offset pixel int colTotal = 0; // Col position of offset pixel int Gx; // Sum of Sobel mask products values in the x direction int Gy; // Sum of Sobel mask products values in the y direction float thisAngle; // Gradient direction based on Gx and Gy int newAngle; // Approximation of the gradient direction bool edgeEnd; // Stores whether or not the edge is at the edge of the possible image int GxMask[3][3]; // Sobel mask in the x direction int GyMask[3][3]; // Sobel mask in the y direction int newPixel; // Sum pixel values for gaussian int gaussianMask[5][5]; // Gaussian mask W = lpThisBitmapInfoHeader->biWidth; // biWidth: number of columns H = lpThisBitmapInfoHeader->biHeight; // biHeight: number of rows for (row = 0; row < H; row++) { for (col = 0; col < W; col++) { edgeDir[row][col] = 0; } } /* Declare Sobel masks */ GxMask[0][0] = -1; GxMask[0][1] = 0; GxMask[0][2] = 1; GxMask[1][0] = -2; GxMask[1][1] = 0; GxMask[1][2] = 2; GxMask[2][0] = -1; GxMask[2][1] = 0; GxMask[2][2] = 1; GyMask[0][0] = 1; GyMask[0][1] = 2; GyMask[0][2] = 1; GyMask[1][0] = 0; GyMask[1][1] = 0; GyMask[1][2] = 0; GyMask[2][0] = -1; GyMask[2][1] = -2; GyMask[2][2] = -1; /* Declare Gaussian mask */ gaussianMask[0][0] = 2; gaussianMask[0][1] = 4; gaussianMask[0][2] = 5; gaussianMask[0][3] = 4; gaussianMask[0][4] = 2; gaussianMask[1][0] = 4; gaussianMask[1][1] = 9; gaussianMask[1][2] = 12; gaussianMask[1][3] = 9; gaussianMask[1][4] = 4; gaussianMask[2][0] = 5; gaussianMask[2][1] = 12; gaussianMask[2][2] = 15; gaussianMask[2][3] = 12; gaussianMask[2][4] = 2; gaussianMask[3][0] = 4; gaussianMask[3][1] = 9; gaussianMask[3][2] = 12; gaussianMask[3][3] = 9; gaussianMask[3][4] = 4; gaussianMask[4][0] = 2; gaussianMask[4][1] = 4; gaussianMask[4][2] = 5; gaussianMask[4][3] = 4; gaussianMask[4][4] = 2; /* Gaussian Blur */ for (row = 2; row < H-2; row++) { for (col = 2; col < W-2; col++) { newPixel = 0; for (rowOffset=-2; rowOffset<=2; rowOffset++) { for (colOffset=-2; colOffset<=2; colOffset++) { rowTotal = row + rowOffset; colTotal = col + colOffset; iOffset = (unsigned long)(rowTotal*3*W + colTotal*3); newPixel += (*(m_destinationBmp + iOffset)) * gaussianMask[2 + rowOffset][2 + colOffset]; } } i = (unsigned long)(row*3*W + col*3); *(m_destinationBmp + i) = newPixel / 159; } } /* Determine edge directions and gradient strengths */ for (row = 1; row < H-1; row++) { for (col = 1; col < W-1; col++) { i = (unsigned long)(row*3*W + 3*col); Gx = 0; Gy = 0; /* Calculate the sum of the Sobel mask times the nine surrounding pixels in the x and y direction */ for (rowOffset=-1; rowOffset<=1; rowOffset++) { for (colOffset=-1; colOffset<=1; colOffset++) { rowTotal = row + rowOffset; colTotal = col + colOffset; iOffset = (unsigned long)(rowTotal*3*W + colTotal*3); Gx = Gx + (*(m_destinationBmp + iOffset) * GxMask[rowOffset + 1][colOffset + 1]); Gy = Gy + (*(m_destinationBmp + iOffset) * GyMask[rowOffset + 1][colOffset + 1]); } } gradient[row][col] = sqrt(pow(Gx,2.0) + pow(Gy,2.0)); // Calculate gradient strength thisAngle = (atan2(Gx,Gy)/3.14159) * 180.0; // Calculate actual direction of edge /* Convert actual edge direction to approximate value */ if ( ( (thisAngle < 22.5) && (thisAngle > -22.5) ) || (thisAngle > 157.5) || (thisAngle < -157.5) ) newAngle = 0; if ( ( (thisAngle > 22.5) && (thisAngle < 67.5) ) || ( (thisAngle < -112.5) && (thisAngle > -157.5) ) ) newAngle = 45; if ( ( (thisAngle > 67.5) && (thisAngle < 112.5) ) || ( (thisAngle < -67.5) && (thisAngle > -112.5) ) ) newAngle = 90; if ( ( (thisAngle > 112.5) && (thisAngle < 157.5) ) || ( (thisAngle < -22.5) && (thisAngle > -67.5) ) ) newAngle = 135; edgeDir[row][col] = newAngle; // Store the approximate edge direction of each pixel in one array } } /* Trace along all the edges in the image */ for (row = 1; row < H - 1; row++) { for (col = 1; col < W - 1; col++) { edgeEnd = false; if (gradient[row][col] > upperThreshold) { // Check to see if current pixel has a high enough gradient strength to be part of an edge /* Switch based on current pixel's edge direction */ switch (edgeDir[row][col]){ case 0: findEdge(0, 1, row, col, 0, lowerThreshold); break; case 45: findEdge(1, 1, row, col, 45, lowerThreshold); break; case 90: findEdge(1, 0, row, col, 90, lowerThreshold); break; case 135: findEdge(1, -1, row, col, 135, lowerThreshold); break; default : i = (unsigned long)(row*3*W + 3*col); *(m_destinationBmp + i) = *(m_destinationBmp + i + 1) = *(m_destinationBmp + i + 2) = 0; break; } } else { i = (unsigned long)(row*3*W + 3*col); *(m_destinationBmp + i) = *(m_destinationBmp + i + 1) = *(m_destinationBmp + i + 2) = 0; } } } /* Suppress any pixels not changed by the edge tracing */ for (row = 0; row < H; row++) { for (col = 0; col < W; col++) { // Recall each pixel is composed of 3 bytes i = (unsigned long)(row*3*W + 3*col); // If a pixel's grayValue is not black or white make it black if( ((*(m_destinationBmp + i) != 255) && (*(m_destinationBmp + i) != 0)) || ((*(m_destinationBmp + i + 1) != 255) && (*(m_destinationBmp + i + 1) != 0)) || ((*(m_destinationBmp + i + 2) != 255) && (*(m_destinationBmp + i + 2) != 0)) ) *(m_destinationBmp + i) = *(m_destinationBmp + i + 1) = *(m_destinationBmp + i + 2) = 0; // Make pixel black } } /* Non-maximum Suppression */ for (row = 1; row < H - 1; row++) { for (col = 1; col < W - 1; col++) { i = (unsigned long)(row*3*W + 3*col); if (*(m_destinationBmp + i) == 255) { // Check to see if current pixel is an edge /* Switch based on current pixel's edge direction */ switch (edgeDir[row][col]) { case 0: suppressNonMax( 1, 0, row, col, 0, lowerThreshold); break; case 45: suppressNonMax( 1, -1, row, col, 45, lowerThreshold); break; case 90: suppressNonMax( 0, 1, row, col, 90, lowerThreshold); break; case 135: suppressNonMax( 1, 1, row, col, 135, lowerThreshold); break; default : break; } } } } } void CTripodDlg::findEdge(int rowShift, int colShift, int row, int col, int dir, int lowerThreshold) { int W = 320; int H = 240; int newRow; int newCol; unsigned long i; bool edgeEnd = false; /* Find the row and column values for the next possible pixel on the edge */ if (colShift < 0) { if (col > 0) newCol = col + colShift; else edgeEnd = true; } else if (col < W - 1) { newCol = col + colShift; } else edgeEnd = true; // If the next pixel would be off image, don't do the while loop if (rowShift < 0) { if (row > 0) newRow = row + rowShift; else edgeEnd = true; } else if (row < H - 1) { newRow = row + rowShift; } else edgeEnd = true; /* Determine edge directions and gradient strengths */ while ( (edgeDir[newRow][newCol]==dir) && !edgeEnd && (gradient[newRow][newCol] > lowerThreshold) ) { /* Set the new pixel as white to show it is an edge */ i = (unsigned long)(newRow*3*W + 3*newCol); *(m_destinationBmp + i) = *(m_destinationBmp + i + 1) = *(m_destinationBmp + i + 2) = 255; if (colShift < 0) { if (newCol > 0) newCol = newCol + colShift; else edgeEnd = true; } else if (newCol < W - 1) { newCol = newCol + colShift; } else edgeEnd = true; if (rowShift < 0) { if (newRow > 0) newRow = newRow + rowShift; else edgeEnd = true; } else if (newRow < H - 1) { newRow = newRow + rowShift; } else edgeEnd = true; } } void CTripodDlg::suppressNonMax(int rowShift, int colShift, int row, int col, int dir, int lowerThreshold) { int W = 320; int H = 240; int newRow = 0; int newCol = 0; unsigned long i; bool edgeEnd = false; float nonMax[320][3]; // Temporarily stores gradients and positions of pixels in parallel edges int pixelCount = 0; // Stores the number of pixels in parallel edges int count; // A for loop counter int max[3]; // Maximum point in a wide edge if (colShift < 0) { if (col > 0) newCol = col + colShift; else edgeEnd = true; } else if (col < W - 1) { newCol = col + colShift; } else edgeEnd = true; // If the next pixel would be off image, don't do the while loop if (rowShift < 0) { if (row > 0) newRow = row + rowShift; else edgeEnd = true; } else if (row < H - 1) { newRow = row + rowShift; } else edgeEnd = true; i = (unsigned long)(newRow*3*W + 3*newCol); /* Find non-maximum parallel edges tracing up */ while ((edgeDir[newRow][newCol] == dir) && !edgeEnd && (*(m_destinationBmp + i) == 255)) { if (colShift < 0) { if (newCol > 0) newCol = newCol + colShift; else edgeEnd = true; } else if (newCol < W - 1) { newCol = newCol + colShift; } else edgeEnd = true; if (rowShift < 0) { if (newRow > 0) newRow = newRow + rowShift; else edgeEnd = true; } else if (newRow < H - 1) { newRow = newRow + rowShift; } else edgeEnd = true; nonMax[pixelCount][0] = newRow; nonMax[pixelCount][1] = newCol; nonMax[pixelCount][2] = gradient[newRow][newCol]; pixelCount++; i = (unsigned long)(newRow*3*W + 3*newCol); } /* Find non-maximum parallel edges tracing down */ edgeEnd = false; colShift *= -1; rowShift *= -1; if (colShift < 0) { if (col > 0) newCol = col + colShift; else edgeEnd = true; } else if (col < W - 1) { newCol = col + colShift; } else edgeEnd = true; if (rowShift < 0) { if (row > 0) newRow = row + rowShift; else edgeEnd = true; } else if (row < H - 1) { newRow = row + rowShift; } else edgeEnd = true; i = (unsigned long)(newRow*3*W + 3*newCol); while ((edgeDir[newRow][newCol] == dir) && !edgeEnd && (*(m_destinationBmp + i) == 255)) { if (colShift < 0) { if (newCol > 0) newCol = newCol + colShift; else edgeEnd = true; } else if (newCol < W - 1) { newCol = newCol + colShift; } else edgeEnd = true; if (rowShift < 0) { if (newRow > 0) newRow = newRow + rowShift; else edgeEnd = true; } else if (newRow < H - 1) { newRow = newRow + rowShift; } else edgeEnd = true; nonMax[pixelCount][0] = newRow; nonMax[pixelCount][1] = newCol; nonMax[pixelCount][2] = gradient[newRow][newCol]; pixelCount++; i = (unsigned long)(newRow*3*W + 3*newCol); } /* Suppress non-maximum edges */ max[0] = 0; max[1] = 0; max[2] = 0; for (count = 0; count < pixelCount; count++) { if (nonMax[count][2] > max[2]) { max[0] = nonMax[count][0]; max[1] = nonMax[count][1]; max[2] = nonMax[count][2]; } } for (count = 0; count < pixelCount; count++) { i = (unsigned long)(nonMax[count][0]*3*W + 3*nonMax[count][1]); *(m_destinationBmp + i) = *(m_destinationBmp + i + 1) = *(m_destinationBmp + i + 2) = 0; } }
دانلود کد فوق از طریق لینک زیر:
Canny in C++ -No2
رمز فایل : behsanandish.com
الگوریتم Canny در سی پلاس پلاس قسمت 1
الگوریتم Canny در سی پلاس پلاس قسمت 2
الگوریتم Canny در سی پلاس پلاس قسمت 3
الگوریتم Canny در سی پلاس پلاس قسمت 4
دیدگاه خود را ثبت کنید
تمایل دارید در گفتگوها شرکت کنید؟در گفتگو ها شرکت کنید.