拉普拉斯算子(Laplacian)可应用到图像边缘检测中。在OpenCV中当kernel大小为3*3时,支持两种kernel算子,分别为:

在OpenCV中默认的计算方式如下,假设有一个5*5的小图像,原始值依次为1,2,…25,如下图红色部分,首先将5*5映射到(5+3-1)*(5+3-1)大小,然后和3*3的kernel做累积和,因为计算结果有可能超出有效值[0, 255]范围,因此在最终还需要判断使其在有效范围内,即小于0为0,大于255为255:
如坐标为(0,0)的计算过程为:12 = 7*0+6*1+7*0+2*1+1*(-4)+2*1+7*0+6*1+7*0.
以下code分别采用两种方式实现,一种是从OpenCV中提取的code(Laplacian_函数),一种是按照上面说明的方式实现的(Laplacian函数),两种方式实现的结果完全一致,但第二种方式更容易理解:
laplacian.cpp:
#include "funset.hpp"
#include <limits.h>
#include <chrono>
#include <vector>
#include <algorithm>
#include <memory>
#include "common.hpp"namespace {typedef struct Rect {int x, y, width, height;
} Rect;
typedef struct Size {int width, height;
} Size;
typedef struct Point {int x, y;
} Point;int borderInterpolate(int p, int len)
{if ((unsigned)p < (unsigned)len) {;} else {if (len == 1) return 0;int delta = 1;do {if (p < 0) p = -p - 1 + delta;else p = len - 1 - (p - len) - delta;} while ((unsigned)p >= (unsigned)len);}return p;
}inline unsigned char saturate_cast(float v)
{int iv = (int)(v + (v >= 0 ? 0.5f : -0.5f));return (unsigned char)((unsigned)v <= UCHAR_MAX ? v : v > 0 ? UCHAR_MAX : 0);
}void filter2D(const unsigned char** src, unsigned char* dst, int dststep, int count, int width, int ksize)
{std::vector<Point> coords;std::vector<float> coeffs;if (ksize == 1) {coords = { { 1, 0 }, { 0, 1 }, { 1, 1 }, { 2, 1 }, { 1, 2 } }; // kernel non zero position: (x, y)coeffs = { 1.f, 1.f, -4.f, 1.f, 1.f }; // kernel non zero value: 1, 1, -4, 1, 1} else {coords = { { 0, 0 }, { 2, 0 }, { 1, 1 }, { 0, 2 }, { 2, 2 } }; // kernel non zero position: (x, y)coeffs = { 2.f, 2.f, -8.f, 2.f, 2.f }; // kernel non zero value: 2, 2, -8, 2, 2}std::vector<unsigned char*> ptrs(coords.size());float _delta{ 0.f };const Point* pt = &coords[0];const float* kf = (const float*)&coeffs[0];const unsigned char** kp = (const unsigned char**)&ptrs[0];int nz = (int)coords.size();for (; count > 0; count--, dst += dststep, src++) {unsigned char* D = (unsigned char*)dst;for (int k = 0; k < nz; k++)kp[k] = (const unsigned char*)src[pt[k].y] + pt[k].x;for (int i = 0; i < width; i++) {float s0 = _delta;for (int k = 0; k < nz; k++)s0 += kf[k] * kp[k][i];D[i] = saturate_cast(s0);}}
}int Laplacian_(const unsigned char* src_, unsigned char* dst_, int width_, int height_, int ksize_)
{const unsigned char* src = src_;unsigned char* dst = dst_;const Size ksize{ 3, 3 };const int maxBufRows = ksize.height + 3;const Point anchor{ 1, 1 };const Rect roi{ 0, 0, width_, height_ };const int dx1{ 1 }, dx2{ 1 };int borderLength = std::max(ksize.width - 1, 1);std::vector<int> borderTab(borderLength);borderTab[0] = borderInterpolate(-dx1, width_);borderTab[1] = borderInterpolate(width_, width_);std::vector<unsigned char*> rows(maxBufRows);const int* btab = &borderTab[0];int srcstep{ width_ }, dststep{ width_ };std::vector<unsigned char> ringBuf((width_ + ksize.width - 1) * maxBufRows, 0);int bufStep{ width_ + ksize.width - 1 };int startY = std::max(roi.y - anchor.y, 0), startY0 = startY, rowCount{ 0 }, dstY{ 0 };int endY = std::min(roi.y + roi.height + ksize.height - anchor.y - 1, height_);int esz = 1;unsigned char** brows = &rows[0];int bufRows = (int)rows.size();int kwidth = ksize.width;int kheight = ksize.height, ay = anchor.y;int _dx1 = dx1, _dx2 = dx2;int width1 = roi.width + kwidth - 1;int dy = 0, i = 0;int count = endY - startY;for (;; dst += dststep * i, dy += i) {int dcount = bufRows - ay - startY - rowCount + roi.y;dcount = dcount > 0 ? dcount : bufRows - kheight + 1;dcount = std::min(dcount, count);count -= dcount;for (; dcount-- > 0; src += srcstep) {int bi = (startY - startY0 + rowCount) % bufRows;unsigned char* brow = &ringBuf[0] + bi*bufStep;unsigned char* row = brow;if (++rowCount > bufRows) {--rowCount;++startY;}memcpy(row + _dx1*esz, src, (width1 - _dx2 - _dx1)*esz);for (i = 0; i < _dx1*esz; i++)row[i] = src[btab[i]];for (i = 0; i < _dx2*esz; i++)row[i + (width1 - _dx2)*esz] = src[btab[i + _dx1*esz]];}int max_i = std::min(bufRows, roi.height - (dstY + dy) + (kheight - 1));for (i = 0; i < max_i; i++) {int srcY = borderInterpolate(dstY + dy + i + roi.y - ay, height_);if (srcY < startY) return -1;if (srcY >= startY + rowCount) break;int bi = (srcY - startY0) % bufRows;brows[i] = &ringBuf[0] + bi*bufStep;}if (i < kheight) break;i -= kheight - 1;filter2D((const unsigned char**)brows, dst, dststep, i, roi.width, ksize_);}dstY += dy;if (dstY > roi.height) return -1;return 0;
}int Laplacian(const unsigned char* src_, unsigned char* dst_, int width_, int height_, int ksize_)
{const int kernel_size{ 3 };std::vector<float> kernel;if (ksize_ == 1) kernel = { 0.f, 1.f, 0.f, 1.f, -4.f, 1.f, 0.f, 1.f, 0.f };else kernel = { 2.f, 0.f, 2.f, 0.f, -8.f, 0.f, 2.f, 0.f, 2.f };int new_width = width_ + kernel_size - 1, new_height = height_ + kernel_size - 1;std::unique_ptr<unsigned char[]> data(new unsigned char[new_width * new_height]);unsigned char* p = data.get();for (int y = 0; y < new_height; ++y) {if (y != 0 && y != new_height - 1) {for (int x = 0; x < new_width; ++x) {if (x == 0) {p[y * new_width + x] = src_[(y - 1) * width_ + 1];} else if (x == new_width - 1) {p[y * new_width + x] = src_[(y - 1) * width_ + (width_ - 1 - 1)];} else {p[y * new_width + x] = src_[(y - 1) * width_ + (x - 1)];}}}if (y == new_height - 1) {for (int x = 0; x < new_width; ++x) {p[y * new_width + x] = p[(y - 2) * new_width + x];}for (int x = 0; x < new_width; ++x) { // y = 0p[x] = p[2 * new_width + x];}}}for (int y = 1; y < new_height - 1; ++y) {for (int x = 1; x < new_width - 1; ++x) {float value{ 0.f };int count{ 0 };for (int m = -1; m <= 1; ++m) {for (int n = -1; n <= 1; ++n) {value += p[(y + m) * new_width + (x + n)] * kernel[count++];}}if (value < 0.) dst_[(y - 1) * width_ + (x - 1)] = 0;else if (value > 255.) dst_[(y - 1) * width_ + (x - 1)] = 255;else dst_[(y - 1) * width_ + (x - 1)] = static_cast<unsigned char>(value);}}return 0;
}} // namespaceint laplacian_cpu(const unsigned char* src, int width, int height, int ksize, unsigned char* dst, float* elapsed_time)
{int ret{ -1 };// ksize == 1: kernel={ 0, 1, 0, 1, -4, 1, 0, 1, 0 }// ksize == 3: kernel={ 2, 0, 2, 0, -8, 0, 2, 0, 2 }CHECK(ksize == 1 || ksize == 3);//TIME_START_CPUret = Laplacian(src, dst, width, height, ksize);//TIME_END_CPUreturn ret;
}

main.cpp:

#include "funset.hpp"
#include <random>
#include <iostream>
#include <vector>
#include <memory>
#include <string>
#include <algorithm>
#include "common.hpp"int test_image_process_laplacian()
{cv::Mat src = cv::imread("E:/GitCode/CUDA_Test/test_data/images/lena.png", 0);if (!src.data || src.channels() != 1) {fprintf(stderr, "read image fail\n");return -1;}int width{ 400 }, height{ 400 };cv::resize(src, src, cv::Size(width, height));std::unique_ptr<unsigned char[]> data1(new unsigned char[width * height]), data2(new unsigned char[width * height]);float elapsed_time1{ 0.f }, elapsed_time2{ 0.f }; // millisecondsint ksize{ 1 };CHECK(laplacian_cpu(src.data, width, height, ksize, data1.get(), &elapsed_time1) == 0);//CHECK(laplacian_gpu(src.data, width, height, data2.get(), &elapsed_time2) == 0);//fprintf(stdout, "gray image edge detection: laplacian: cpu run time: %f ms, gpu run time: %f ms\n", elapsed_time1, elapsed_time2);cv::Mat dst;cv::Laplacian(src, dst, src.depth(), ksize);cv::imwrite("E:/GitCode/CUDA_Test/test_data/images/laplacian.png", dst);CHECK(compare_result(data1.get(), dst.data, width*height) == 0);//CHECK(compare_result(data1.get(), data2.get(), width*height) == 0);save_image(src, dst, width, height / 2, "E:/GitCode/CUDA_Test/test_data/images/laplacian_result.png");return 0;
}

执行结果如下:

由结果可知:C++实现结果和调用OpenCV的接口结果是完全一致的。
GitHub:https://github.com/fengbingchun/CUDA_Test

图像边缘检测之拉普拉斯(Laplacian)C++实现相关推荐

  1. 基于c语言图像边缘检测的程序,图像边缘检测之拉普拉斯(Laplacian)C++实现

    拉普拉斯算子(Laplacian)可应用到图像边缘检测中.在OpenCV中当kernel大小为3*3时,支持两种kernel算子,分别为: 在OpenCV中默认的计算方式如下,假设有一个5*5的小图像 ...

  2. [Python从零到壹] 五十八.图像增强及运算篇之图像锐化Sobel、Laplacian算子实现边缘检测

    欢迎大家来到"Python从零到壹",在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界.所有文章都将结合案例.代码和作者的经验讲 ...

  3. 拉普拉斯(laplacian)滤波实现图像锐化分析(负值处理方法)

    先上个简单的示例,看MATLAB中拉普拉斯滤波器是如何实现的: 令原图f=magic(3) f = 8 1 6 3 5 7 4 9 2 掩膜采用标准Laplacian掩膜:w=fspecial('la ...

  4. MATLAB梯度和拉普拉斯算子在图像边缘检测中的应用

    MATLAB梯度和拉普拉斯算子在图像边缘检测中的应用 数学方法 边缘检测最通用的方法是检测灰度值的不连续性,这种不连续性用一阶和二阶导数来检测. (1)一阶导数:一阶导数即为梯度,对于平面上的图像来说 ...

  5. 【OpenCV图像处理入门学习教程四】基于LoG算子的图像边缘检测

    OpenCV图像处理入门学习教程系列,上一篇第三篇:基于SIFT特征和SURF特征的微旋转图像拼接与融合生成全景图像的比较 LoG边缘检测算子 LoG边缘检测算子是David Courtnay Mar ...

  6. 主流图像边缘检测算法

    前言:最近在学习关于图像边缘检测技术,更新此博文仅为 了记录个人学习过程,也供有共同志趣的朋友参考! 本文内容包括:图像噪声添加与去除.几种滤波算法[高斯滤波,方框滤波,均值滤波,中值滤波,双边滤波, ...

  7. Matlab数字图像处理 实验3、图像边缘检测的计算机实现

    子曰:桃李不言,下自成蹊. 图 像 边 缘 检 测 的 计 算 机 实 现 图像边缘检测的计算机实现 图像边缘检测的计算机实现 P a r t . 0 实 验 目 的 仅 限 于 学 习 交 流 Pa ...

  8. python进行图像边缘检测

    边缘检测 图像边缘是指图像中表达物体的周围像素灰度发生阶跃变化的那些像素集合. 图像中两个灰度不同的相邻区域的交界处,必然存在灰度的快速过渡或称为跳变,它们与图像中各区域边缘的位置相对应,边缘蕴含了丰 ...

  9. CV笔记6:图像边缘检测之一阶微分算子、二阶微分算子、Canny边缘检测(基于python-opencv实现)

    目录 一.边缘简介 1.1 何为边缘 1.2 产生原因 二.边缘检测方法 2.1 一阶微分算子计算原理 2.2 噪声对一阶微分算子的影响及解决方案 2.3 常见的一阶微分算子 2.3.1 Robert ...

最新文章

  1. 02搭建cdh版本控制
  2. 数据中心服务器网络接入技术 — VEB、VEPA、VN-Tag
  3. SSM个人遇到的问题汇总——不定期更新
  4. Linux多个端口组合,iptables使用multiport 添加多个不连续端口
  5. taz文件_我们将赠送LulzBot Taz 6 3D打印机
  6. android 禁用dlsym_一个绕过移动端系统限制的dlopen库: byOpen
  7. 编程语言对比 执行文件
  8. 【Python-3.5】matplotlib绘制气温折线图
  9. 重新分析connection reset by peer, socket write error错误原因
  10. 某IDC服务商机房宕机致银行业务中断 银监会发布风险提示
  11. 合肥工贸高级技工学校计算机系,合肥工贸高级技工学校扎实推进“新技工系统培养”民生工程...
  12. js 打开新窗口 修改 窗口大小
  13. 量子计算机采用量子力学原理,量子计算机的工作是不是就是根据量子力学原理造的?...
  14. TCP服务器端与多个客户端连接的C#代码实现
  15. 2016第二届美亚杯电子数据取证(个人赛)
  16. 一站式解决网站死链问题
  17. 麻雀虽小,五脏俱全!RT-Thread BK7252 麻雀一号开发板上手体验
  18. 设计模式:行为型模式
  19. 如何看计算机加密方式,电脑常见的几种加密的方法
  20. Linux之命令scp远程拷贝文件

热门文章

  1. 基于视频理解TSM和数据集Kinetics-400的视频行为识别
  2. Linux那些事儿之我是Sysfs(7)dentry与inode
  3. 【Pandas库】(4) 索引操作--重新生成索引
  4. PCL点云分割(2)
  5. Ray Tracing,Ray Casting,Path Tracing,Ray Marching 的区别?
  6. js中Object类型和Array类型的变量被赋值(复制)给其他变量后,修改被赋值(复制)的新变量的值,会影响原始变量的值,这是为什么呢?
  7. 在CentOS 6.6 64bit上安装Python 3.5.0
  8. Ubuntu 14.04 64bit上升级Intel官方集显更新驱动程序
  9. Go 分布式学习利器(10)-- Go语言的接口
  10. redis学习 -- 简单动态字符串