轮廓提取

  • 前言
  • 提取傅里叶变换的高频信息
  • 通过蚁群算法进行图片轮廓提取
    • Canny边缘检测
  • 使用cuda加速提取轮廓

前言

  常用的轮廓提取算法有:Canny、阈值分割、提取傅里叶变换的高频信息,还有别具一格的蚁群算法,当然比较常见的作法是使用阈值分割+边缘查找,在OpenCV里是threshold和findContours两个函数的组合使用,和Canny。
  轮廓提取的算法很多,而其目的都是为了找到图像中灰阶差比较大的位置。而所谓亚像素提取,则是使用了插值算法,以找出灰阶差最大的位置。

提取傅里叶变换的高频信息

##############
#图像中的轮廓提取
#时间:2019/1/3
#作者:cclplus
#仅供学习交流使用
#如有疑问或者需求,可以联系作者的邮箱
#如果你有什么好的建议或者指导,我将不胜感激import cv2
import numpy as np
from matplotlib import pyplot as plt
import copy
img = cv2.imread('liuyifei.jpg',0)
f = np.fft.fft2(img)
fshift = np.fft.fftshift(f)rows,cols = img.shape
crow,ccol = int(rows/2) , int(cols/2)
for i in range(crow-30,crow+30):for j in range(ccol-30,ccol+30):fshift[i][j]=0.0
f_ishift = np.fft.ifftshift(fshift)
img_back = np.fft.ifft2(f_ishift)#进行高通滤波
# 取绝对值
img_back = np.abs(img_back)
plt.subplot(121),plt.imshow(img,cmap = 'gray')#因图像格式问题,暂已灰度输出
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
#先对灰度图像进行伽马变换,以提升暗部细节
rows,cols = img_back.shape
gamma=copy.deepcopy(img_back)
rows=img.shape[0]
cols=img.shape[1]
for i in range(rows):for j in range(cols):gamma[i][j]=5.0*pow(gamma[i][j],0.34)#0.34这个参数是我手动调出来的,根据不同的图片,可以选择不同的数值
#对灰度图像进行反转for i in range(rows):for j in range(cols):gamma[i][j]=255-gamma[i][j]
plt.subplot(122),plt.imshow(gamma,cmap = 'gray')
plt.title('Result in HPF'), plt.xticks([]), plt.yticks([])
plt.show()

原图

输出结果

通过蚁群算法进行图片轮廓提取

相关代码我上传到了我的github上
https://github.com/YuruTu/Ant_colony


  效果不够理想,这也算得上蚁群算法的一大特点,对参数要求较高,需要调参。相关内容,笔者会持续更新

Canny边缘检测

  

import cv2
import numpy as np
from matplotlib import pyplot as pltimg = cv2.imread('liuyifei.jpg',0)
edges = cv2.Canny(img,100,200)plt.subplot(121),plt.imshow(img,cmap='gray')
plt.title('original'),plt.xticks([]),plt.yticks([])
plt.subplot(122),plt.imshow(edges,cmap='gray')
plt.title('edge'),plt.xticks([]),plt.yticks([])plt.show()

使用cuda加速提取轮廓

#include <iostream>
#include <cuda.h>
#include <cstdlib>
#include <stdio.h>
#include <cuda_runtime.h>
#include <string>
#include <assert.h>
#include <cuda_runtime.h>
#include <cuda_runtime_api.h>
#include <device_launch_parameters.h>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>using namespace cv;
using namespace std;
// GPU constant memory to hold our kernels (extremely fast access time)
__constant__ float convolutionKernelStore[256];/*** Convolution funcion para cuda.  Destino tendra el mismo width/height como la fuente,** @param source      Source imagen host* @param width       Source imagen width* @param height      Source imagen height* @param paddingX    source imagen padding x* @param paddingY    source imagen padding y* @param kOffset     offset en kernel almacenamiento de memoria constante* @param kWidth      kernel width* @param kHeight     kernel height* @param destination Imagen de destino*/
__global__ void convolve(unsigned char *source, int width, int height, int paddingX, int paddingY, unsigned int kOffset, int kWidth, int kHeight, unsigned char *destination)
{// Calculate our pixel's locationint x = (blockIdx.x * blockDim.x) + threadIdx.x;int y = (blockIdx.y * blockDim.y) + threadIdx.y;float sum = 0.0;int   pWidth = kWidth / 2;int   pHeight = kHeight / 2;//Solo ejecuta validos pixelesif (x >= pWidth + paddingX && y >= pHeight + paddingY && x < (blockDim.x * gridDim.x) - pWidth - paddingX &&y < (blockDim.y * gridDim.y) - pHeight - paddingY){for (int j = -pHeight; j <= pHeight; j++){for (int i = -pWidth; i <= pWidth; i++){// Sample the weight for this locationint ki = (i + pWidth);int kj = (j + pHeight);float w = convolutionKernelStore[(kj * kWidth) + ki + kOffset];sum += w * float(source[((y + j) * width) + (x + i)]);}}}// Promedio sumdestination[(y * width) + x] = (unsigned char)sum;
}__global__ void pythagoras(unsigned char *a, unsigned char *b, unsigned char *c)
{int idx = (blockIdx.x * blockDim.x) + threadIdx.x;float af = float(a[idx]);float bf = float(b[idx]);c[idx] = (unsigned char)sqrtf(af*af + bf * bf);
}// crea imagen buffer
unsigned char* createImageBuffer(unsigned int bytes, unsigned char **devicePtr)
{unsigned char *ptr = NULL;cudaSetDeviceFlags(cudaDeviceMapHost);cudaHostAlloc(&ptr, bytes, cudaHostAllocMapped);cudaHostGetDevicePointer(devicePtr, ptr, 0);return ptr;
}int main(int argc, char** argv) {// Abre la camarawebcv::VideoCapture camera(0);cv::Mat          frame;if (!camera.isOpened())return -1;// capture windowscv::namedWindow("Source");cv::namedWindow("Greyscale");cv::namedWindow("Blurred");cv::namedWindow("Sobel");// Funciones para obtener el tiempo de ejecucion cudaEvent_t start, stop;cudaEventCreate(&start);cudaEventCreate(&stop);// Crea kernel gaussian(sum = 159)const float gaussianKernel5x5[25] ={2.f / 159.f,  4.f / 159.f,  5.f / 159.f,  4.f / 159.f, 2.f / 159.f,4.f / 159.f,  9.f / 159.f, 12.f / 159.f,  9.f / 159.f, 4.f / 159.f,5.f / 159.f, 12.f / 159.f, 15.f / 159.f, 12.f / 159.f, 5.f / 159.f,4.f / 159.f,  9.f / 159.f, 12.f / 159.f,  9.f / 159.f, 4.f / 159.f,2.f / 159.f,  4.f / 159.f,  5.f / 159.f,  4.f / 159.f, 2.f / 159.f,};cudaMemcpyToSymbol(convolutionKernelStore, gaussianKernel5x5, sizeof(gaussianKernel5x5), 0);const unsigned int gaussianKernel5x5Offset = 0;// Sobel gradient kernelsconst float sobelGradientX[9] ={-1.f, 0.f, 1.f,-2.f, 0.f, 2.f,-1.f, 0.f, 1.f,};const float sobelGradientY[9] ={1.f, 2.f, 1.f,0.f, 0.f, 0.f,-1.f, -2.f, -1.f,};cudaMemcpyToSymbol(convolutionKernelStore, sobelGradientX, sizeof(sobelGradientX), sizeof(gaussianKernel5x5));cudaMemcpyToSymbol(convolutionKernelStore, sobelGradientY, sizeof(sobelGradientY), sizeof(gaussianKernel5x5) + sizeof(sobelGradientX));const unsigned int sobelGradientXOffset = sizeof(gaussianKernel5x5) / sizeof(float);const unsigned int sobelGradientYOffset = sizeof(sobelGradientX) / sizeof(float) + sobelGradientXOffset;// Crea CPU/GPU imagenes compartidoscamera >> frame;unsigned char *sourceDataDevice, *blurredDataDevice, *edgesDataDevice;cv::Mat source(frame.size(), CV_8U, createImageBuffer(frame.size().width * frame.size().height, &sourceDataDevice));cv::Mat blurred(frame.size(), CV_8U, createImageBuffer(frame.size().width * frame.size().height, &blurredDataDevice));cv::Mat edges(frame.size(), CV_8U, createImageBuffer(frame.size().width * frame.size().height, &edgesDataDevice));// Crea 2 imagenes temporales (sobel gradients)unsigned char *deviceGradientX, *deviceGradientY;cudaMalloc(&deviceGradientX, frame.size().width * frame.size().height);cudaMalloc(&deviceGradientY, frame.size().width * frame.size().height);// Loop while captura imageneswhile (1){// Captura la imagen en escala de grisescamera >> frame;cvtColor(frame, source, COLOR_RGB2GRAY);_sleep(1);// Graba el tiempo que demora el procesocudaEventRecord(start);{// convolution kernel  parametrosdim3 cblocks(frame.size().width / 16, frame.size().height / 16);dim3 cthreads(16, 16);// pythagoran kernel parametrosdim3 pblocks(frame.size().width * frame.size().height / 256);dim3 pthreads(256, 1);//  gaussian blur (first kernel in store @ 0)convolve <<<cblocks, cthreads >> > (sourceDataDevice, frame.size().width, frame.size().height, 0, 0, gaussianKernel5x5Offset, 5, 5, blurredDataDevice);// sobel gradient convolutions (x&y padding is now 2 because there is a border of 2 around a 5x5 gaussian filtered image)convolve << <cblocks, cthreads >> > (blurredDataDevice, frame.size().width, frame.size().height, 2, 2, sobelGradientXOffset, 3, 3, deviceGradientX);convolve << <cblocks, cthreads >> > (blurredDataDevice, frame.size().width, frame.size().height, 2, 2, sobelGradientYOffset, 3, 3, deviceGradientY);pythagoras << <pblocks, pthreads >> > (deviceGradientX, deviceGradientY, edgesDataDevice);cudaThreadSynchronize();}cudaEventRecord(stop);// Muestra tiempo de ejecucionfloat ms = 0.0f;cudaEventSynchronize(stop);cudaEventElapsedTime(&ms, start, stop);std::cout << "Elapsed GPU time: " << ms << " milliseconds" << std::endl;// Muestra resultadosimshow("Source", frame);imshow("Greyscale", source);imshow("Blurred", blurred);imshow("Sobel", edges);// Spinif (cv::waitKey(1) == 27) break;}// ExitcudaFreeHost(source.data);cudaFreeHost(blurred.data);cudaFreeHost(edges.data);cudaFree(deviceGradientX);cudaFree(deviceGradientY);return 0;
}

  很多时候加上Cuda是有必要的,如果你要使用hough变换之类的时间复杂度比较高的代码,Gpu编程会给你带来多个数量级的加速。

OpenCV图像轮廓提取相关推荐

  1. 《OpenCv视觉之眼》Python图像处理十二 :Opencv图像轮廓提取之基于一阶导数的Roberts算法、Prewitt算法及Sobel算法

    本专栏主要介绍如果通过OpenCv-Python进行图像处理,通过原理理解OpenCv-Python的函数处理原型,在具体情况中,针对不同的图像进行不同等级的.不同方法的处理,以达到对图像进行去噪.锐 ...

  2. 《OpenCv视觉之眼》Python图像处理十四 :Opencv图像轮廓提取之Scharr算法和Canny算法

    本专栏主要介绍如果通过OpenCv-Python进行图像处理,通过原理理解OpenCv-Python的函数处理原型,在具体情况中,针对不同的图像进行不同等级的.不同方法的处理,以达到对图像进行去噪.锐 ...

  3. 图像轮廓提取算法(Opencv基于C++实现)

    Opencv图像轮廓提取 0. 实现结果如下: 1. 打开图像代码 2. 轮廓提取函数 3. 代码实现 本文主要实现了图像的轮廓提取,首先先给出直观的轮廓实现结果: 0. 实现结果如下: 1. 打开图 ...

  4. 一种新型鱼眼图像轮廓提取算法

    from: http://www.scimao.com/read/2307651     摘 要:提取鱼眼图像轮廓是利用鱼眼图像的前提.传统提取鱼眼图像轮廓的扫描线逼近法对噪点抑制能力不强,精度差.本 ...

  5. 发现你的身形——OpenCV图像轮廓

    文章目录 写在最前 轮廓发现算法 边缘检测 写在最后 写在最前 我的意思不是说你长得很胖,emmmm,而是你的轮廓很大. --五星上将詹姆斯下士如是说 果然有图没图,理解是不一样的,这就体现了计算机视 ...

  6. OpenCV 图像轮廓检测

    本文是OpenCV图像视觉入门之路的第15篇文章,本文详细的介绍了图像轮廓检测的各种操作,例如:轮廓检索模式.轮廓逼近算子等操作. 图像轮廓是具有相同颜色或灰度的连续点的曲线,轮廓在形状分析和物体的检 ...

  7. 我的Qt作品(7)使用Qt+OpenCV实现图像轮廓提取,再用三阶贝塞尔曲线拟合成光滑线条/多边形拟合

    一.贝塞尔曲线基础知识 给一系列顶点,如果只是用直线将其中的各个点依次连接起来,最终形成一个折线图,这种很容易实现.但是现实中事物的变化往往具有连续的特性,即使是给定了一系列离散的点,基于以往的生活经 ...

  8. OPENCV图像轮廓检测

    前面在图像转换的时候学到canny算子,可以检测出图像的轮廓信息,但是,该算子检测到的轮廓信息还需要我们手动的用眼睛去识别,而实际工程应用中,我们需要得到轮廓的具体数学信息,这就涉及到今天的主题,图像 ...

  9. pythonopencv提取轮廓区域_Python + Opencv 实现轮廓提取,轮廓区域面积计算

    Python + Opencv2 实现轮廓提取,轮廓区域面积计算: 对图像处理时,会遇到这样一个场景:找到图像主体轮廓,这是其一,可能为了凸显轮廓,需要用指定的颜色进行标记:轮廓标记完可能任务还没有结 ...

  10. 8.opencv——图像轮廓,霍夫变换

    图像轮廓,霍夫变换 图像轮廓 查找轮廓 绘制轮廓 轮廓特征 1.轮廓的矩 2.轮廓面积 3.轮廓的长度 4. 轮廓的近似多边形 5.轮廓的凸包 6.轮廓的直边界矩形 7.轮廓的旋转矩形 8.轮廓的最小 ...

最新文章

  1. 综述 | Google-斯坦福发布深度学习统计力学
  2. ## *将以下学生成绩数据,存放在Hdfs上,使用Spark读取完成下面分析**
  3. java实现鼠标宏编程_我應該如何編程高級java遊戲中的鼠標/鍵輸入?
  4. linux下设置mysql不区分大小写
  5. ios 刷新头像_iOS上传图片到网上,并更新到服务器,常用在设置头像
  6. android 视频 截图,java – android获取当前视频的截图
  7. Hibernate之N+1问题
  8. Windows虚拟地址转物理地址(原理+源码实现,附简单小工具)
  9. html选项卡_适用于Mac的最佳HTML文本编辑器,编码开发必备
  10. php 自动寻路算法,PHP树-不需要递归的实现方法
  11. android 下拉刷新实现方式,Android RecyclerView设置下拉刷新的实现方法
  12. android显示当前时间
  13. 你干的是高档活儿还是Low逼活儿,就在一念之间
  14. 程序员的自我修养(雾)
  15. android自定义素材拼图,众望所归 美图秀秀Android拼图隆重上线
  16. Java笔试面试-JVM
  17. SAP Enhancement 分类
  18. 解决办法在idea中搭建spark环境:Unable to fetch table student. Invalid method name: ‘get_table_req‘;
  19. HttpClient 调用耗时长服务问题记录和处理方案
  20. Java设计模式之《装饰器模式》及应用场景

热门文章

  1. 中国大学MOOC-陈越、何钦铭-数据结构-习题解答-02 线性结构
  2. java 合并多个文件_java中如何将两个文件合并到另一个文件
  3. NSGA-II中“支配”的概念
  4. x光安检图像检测数据集(7500多张图像,VOC标签)
  5. Android 如何屏蔽返回键和Home键
  6. 79g道闸雷达_79G雷达安装注意事项
  7. 海信65E7G Pro评测
  8. QQ 聊天机器人API
  9. Ubuntu系统中文输入法配置安装(谷歌拼音)
  10. PCB logo 制作 视频教程 内含 PCBLogoCreator软件