OpenCV图像轮廓提取的实现

目录
  • 前言
  • 提取傅里叶变换的高频信息
  • 通过蚁群算法进行图片轮廓提取
  • 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 plt

img = 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 location
	int 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 pixeles
	if (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 location
				int ki = (i + pWidth);
				int kj = (j + pHeight);
				float w = convolutionKernelStore[(kj * kWidth) + ki + kOffset];

				sum += w * float(source[((y + j) * width) + (x + i)]);
			}
		}
	}

	// Promedio sum
	destination[(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 camaraweb
	cv::VideoCapture camera(0);
	cv::Mat          frame;
	if (!camera.isOpened())
		return -1;

	// capture windows
	cv::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 kernels
	const 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 compartidos
	camera >> 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 imagenes
	while (1)
	{
		// Captura la imagen en escala de grises
		camera >> frame;
		cvtColor(frame, source, COLOR_RGB2GRAY);
		_sleep(1);
		// Graba el tiempo que demora el proceso
		cudaEventRecord(start);
		{
			// convolution kernel  parametros
			dim3 cblocks(frame.size().width / 16, frame.size().height / 16);
			dim3 cthreads(16, 16);

			// pythagoran kernel parametros
			dim3 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 ejecucion
		float ms = 0.0f;
		cudaEventSynchronize(stop);
		cudaEventElapsedTime(&ms, start, stop);
		std::cout << "Elapsed GPU time: " << ms << " milliseconds" << std::endl;

		// Muestra resultados
		imshow("Source", frame);
		imshow("Greyscale", source);
		imshow("Blurred", blurred);
		imshow("Sobel", edges);

		// Spin
		if (cv::waitKey(1) == 27) break;
	}

	// Exit
	cudaFreeHost(source.data);
	cudaFreeHost(blurred.data);
	cudaFreeHost(edges.data);
	cudaFree(deviceGradientX);
	cudaFree(deviceGradientY);

	return 0;
}

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

到此这篇关于OpenCV图像轮廓提取的实现的文章就介绍到这了,更多相关OpenCV图像轮廓提取内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Opencv实现轮廓提取功能

    轮廓:一个轮廓代表一系列的点(像素),这一系列的点构成一个有序的点集,所以可以把一个轮廓理解为一个有序的点集. 在opencv中,提供了一个函数返回一个有序的点集或者有序的点集的集合(指多个有序的点集),函数findContour是从二值图像中来计算轮廓的,一般使用Canny()函数处理后的图像,因为这样的图像含有边缘像素. 寻找轮廓的API函数: findContours(image,vector<vector<Point>> contours,vector<Vec4i&g

  • Opencv处理图像之轮廓提取

    本文实例为大家分享了Opencv处理图像之轮廓提取,使用cvfindContours对图像进行轮廓检测,供大家参考,具体内容如下 #include<iostream> #include<opencv2/core/core.hpp> #include<opencv2/highgui/highgui.hpp> int main() { IplImage* img = cvLoadImage("E:\\test.bmp",0); IplImage* img

  • OpenCV图像轮廓提取的实现

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

  • opencv 图像轮廓的实现示例

    图像轮廓 Contours:轮廓 轮廓是将没有连着一起的边缘连着一起. 边缘检测检测出边缘,边缘有些未连接在一起. 注意问题 1.对象为二值图像,首先进行阈值分割或者边缘检测. 2.查找轮廓需要更改原始图像,通常使用原始图像的一份进行拷贝. 3.在opencv里,是从黑色背景里找白色.因此对象必须是白色,背景为黑色. 方法 cv2.findContours() cv2.drawContours() 通过cv2.findContours() 查找轮廓在哪里,再通过 cv2.drawContours

  • OpenCV图像轮廓的绘制方法

    本文实例为大家分享了检测几何图形轮廓和检测花朵图形轮廓,供大家参考,具体内容如下 OpenCV绘制图像轮廓 绘制轮廓的一般步骤: 1.读取图像 image = cv2.imread('image_path') 2.将原图转化为灰度图像 image_gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY) 3.将灰度图像进行二值化阈值处理 # 这里将阈值设置为127为例,最大阈值为255 t, binary = cv.threshold(image_gray, 12

  • Python基于opencv的简单图像轮廓形状识别(全网最简单最少代码)

    可以直接跳到最后整体代码看一看是不是很少的代码!!!! 思路: 1. 数据的整合 2. 图片的灰度转化 3. 图片的二值转化 4. 图片的轮廓识别 5. 得到图片的顶点数 6. 依据顶点数判断图像形状 一.原数据的展示 图片文件共36个文件夹,每个文件夹有100张图片,共3600张图片. 每一个文件夹里都有形同此类的图形 二.数据的整合 对于多个文件夹,分析起来很不方便,所有决定将其都放在一个文件夹下进行分析,在python中具体实现如下: 本次需要的包 import cv2 import os

  • OpenCV半小时掌握基本操作之图像轮廓

    目录 概述 图像轮廓 绘制轮廓 轮廓特征 轮廓近似 边界矩形 外接圆 [OpenCV]⚠️高手勿入! 半小时学会基本操作 ⚠️ 图像轮廓 概述 OpenCV 是一个跨平台的计算机视觉库, 支持多语言, 功能强大. 今天小白就带大家一起携手走进 OpenCV 的世界. 图像轮廓 cv2.findContours可以帮助我们查找轮廓. 格式: cv2.findContours(image, mode, method, contours=None, hierarchy=None, offset=Non

  • Python+OpenCV之图像轮廓详解

    目录 1. 图像轮廓 1.1 findContours介绍 1.2 绘制轮廓 1.3 轮廓特征 2. 轮廓近似 2.1 轮廓 2.2 边界矩形 2.3 外界多边形及面积 1. 图像轮廓 1.1 findContours介绍 cv2.findContours(img, mode, method) mode:轮廓检索模式 RETR_EXTERNAL :只检索最外面的轮廓: RETR_LIST:检索所有的轮廓,并将其保存到一条链表当中: RETR_CCOMP:检索所有的轮廓,并将他们组织为两层:顶层是

  • python 基于opencv 绘制图像轮廓

    图像轮廓概念 轮廓是一系列相连的点组成的曲线,代表了物体的基本外形. 谈起轮廓不免想到边缘,它们确实很像.简单的说,轮廓是连续的,边缘并不全都连续(下图).其实边缘主要是作为图像的特征使用,比如可以用边缘特征可以区分脸和手:而轮廓主要用来分析物体的形态,比如物体的周长和面积等,可以说边缘包括轮廓. 寻找轮廓的操作一般用于二值图像,所以通常会使用阈值分割或Canny边缘检测先得到二值图. 注意:寻找轮廓是针对白色物体的,一定要保证物体是白色,而背景是黑色,不然很多人在寻找轮廓时会找到图片最外面的一

  • Python OpenCV 图像区域轮廓标记(框选各种小纸条)

    学在前面 上篇 OpenCV 博客原计划完成一个 识别银行卡号的项目,但是写的过程中发现,技术储备不足,我无法在下述图片中,提取出卡号区域,也就无法进行后续的识别了,再次意识到了自己技术还不达标,继续学习.完不成,就实现其它学习项目. 轮廓识别实战 先看一下最终实现的效果,针对一张图片(该图片前景色和背景色差异较大),进行轮廓标记. 图片基本处理 import cv2 as cv src = cv.imread("./demo.jpg") gray = cv.cvtColor(src,

随机推荐