基于c语言图像边缘检测的程序,图像边缘检测之拉普拉斯(Laplacian)C++实现
拉普拉斯算子(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
#include
#include
#include
#include
#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
;
} else {
if (len == 1) return 0;
int delta = 1;
do {
if (p
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 coords;
std::vector 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 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
kp[k] = (const unsigned char*)src[pt[k].y] + pt[k].x;
for (int i = 0; i
float s0 = _delta;
for (int k = 0; 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 borderTab(borderLength);
borderTab[0] = borderInterpolate(-dx1, width_);
borderTab[1] = borderInterpolate(width_, width_);
std::vector rows(maxBufRows);
const int* btab = &borderTab[0];
int srcstep{ width_ }, dststep{ width_ };
std::vector 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
row[i] = src[btab[i]];
for (i = 0; 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
int srcY = borderInterpolate(dstY + dy + i + roi.y - ay, height_);
if (srcY
if (srcY >= startY + rowCount) break;
int bi = (srcY - startY0) % bufRows;
brows[i] = &ringBuf[0] + bi*bufStep;
}
if (i
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 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 data(new unsigned char[new_width * new_height]);
unsigned char* p = data.get();
for (int y = 0; y
if (y != 0 && y != new_height - 1) {
for (int x = 0; 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
p[y * new_width + x] = p[(y - 2) * new_width + x];
}
for (int x = 0; x
p[x] = p[2 * new_width + x];
}
}
}
for (int y = 1; y
for (int x = 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
else if (value > 255.) dst_[(y - 1) * width_ + (x - 1)] = 255;
else dst_[(y - 1) * width_ + (x - 1)] = static_cast(value);
}
}
return 0;
}
} // namespace
int 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_CPU
ret = Laplacian(src, dst, width, height, ksize);
//TIME_END_CPU
return ret;
}
main.cpp:#include "funset.hpp"
#include
#include
#include
#include
#include
#include
#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 data1(new unsigned char[width * height]), data2(new unsigned char[width * height]);
float elapsed_time1{ 0.f }, elapsed_time2{ 0.f }; // milliseconds
int 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的接口结果是完全一致的。
基于c语言图像边缘检测的程序,图像边缘检测之拉普拉斯(Laplacian)C++实现相关推荐
- c语言逻辑运用及宏程序编写,基于C语言基础的宏程序编程
基于C语言基础的宏程序编程 王恒厂,周燕飞,姚裕,吕常奎 (南京航空航天大学工程训练中心,江苏南京211100) 摘 要:用户宏指令编程是FANUC系统所提供的一种先进的编程方法[1],使用变量代替程 ...
- 基于go语言结合微信小程序开发的微商城系统
最近我录制的一门<Golang微信小程序微商城系统原型>,这门免费课程特别适合在校大学生或者刚毕业的大学生,go语言初学者以及想要从事微商城开发项目入门的小伙伴们来学习.在课程当中我不仅仅 ...
- 图像分块matlab程序,图像分块 matlab程序
clear,clc; [filename,filepath] = uigetfile('*.*','Select the image'); if isequal(filename,0)||isequa ...
- 基于c语言抓包软件程序,C或者C++写的抓包工具。
满意答案 给你一个winpcap的示例代码,具体的可以根据自己的需求修改 #include #include #include #define LINE_LEN 16 int main(int arg ...
- 基于c语言的图像边缘检测,基于C语言的医学图像处理的设计
内容介绍 原文档由会员 莎士比亚 发布 资源包括:正文DOC格式共41页 19611字 摘 要:B超图像作为医学图像的一个部分,具有医学数字图像的某些共性.B超图像处理技术作为医学数字图像处理技术的一 ...
- 图像haar特征提取 c语言,基于C语言的特征点提取程序 本程序是利用C语言对各种信号数据的特征点提取程序 - 下载 - 搜珍网...
基于C语言的特征点提取程序\修改后 特征点提取程序\提取特征点\Debug\football.ilk 基于C语言的特征点提取程序\修改后 特征点提取程序\提取特征点\Debug\football.ob ...
- 基于c语言矩阵数组透视变换,一种图像透视变换方法与流程
本发明属于图像处理的技术领域,尤其涉及一种图像透视变换方法. 背景技术: 透视变换是一种常见图像处理方法.透视变换常用于图象的校正,例如在移动机器人视觉导航研究中,由于摄像机与地面之间有一倾斜角,而不 ...
- C语言数字图像处理----3.2 图像边缘检测之Canny边缘检测算法
本篇将介绍图像边缘检测中的Canny边缘检测,这是传统边缘检测学习中的必学内容,也是图像算法面试中的常见问题,本文将教会初学者如何在理解算法的基础之上,通过C语言来实现Canny边缘检测算法,从而做到 ...
- 对彩色花图像进行分类-基于R语言的Keras实现
该案例摘自<Keras深度学习入门.实战及进阶>第四章综合案例内容. 该案例的数据来源于Kaggle上的Flower Color Images(https://www.kaggle.com ...
最新文章
- CTFshow 命令执行 web120
- 前端dashboard框架_微前端在网易七鱼的实践
- 小学计算机说课稿,小学信息技术说课稿《新建文件夹》
- string’ does not name a type 错误解析
- UVA-11988 悲剧文本-静态链表
- 简单c语言课设计题目,C语言课程设计题目
- 某单位会java_Java核心API -- 4(日期类)
- LeetCode 1609. 奇偶树(层序遍历)
- 数据算法之二叉树删除(BinaryTreeL Remove)的Java实现
- Judge whether the user is in some windows group.
- 数据结构【图】—023邻接表深度和广度遍历
- Demo之JavaEE的Web中数据分页显示
- RuiJie命令行划VLAN
- 2021考研数学二汤家凤接力题典1800【题目册】
- arma 预测 matlab代码,求助:ARMA模型进行预测
- java 网站 分类
- GeForce GTX 1050-2G驱动安装
- JS实现复制页面文字弹出消息提醒/在sweetalert(swal)弹出窗插件中加入html代码
- 超文本咖啡壶控制协议
- 如何炸掉……呃,月球?
热门文章
- 西游记中孙悟空的等级地位
- 若要运行此应用程序 您必须首先安装 .NET Framework的以下版本之一
- Infragistics控件使用
- TensorFlow入门教程(16)分离歌曲中的人声和背景音乐
- setlocale(LC_ALL,) 中的 LC_* 是什么意思,以及 LANGUAGE/LANG 都是些啥?
- Docker 镜像管理
- HTML5文字转语音源码,微软TTS语音源码(将文本转为语音并播放)
- 怎么在抖音中一键复制微信号打开微信引流
- 第一百篇博客,想谈谈大学的前端的学习历程和未来的计划
- 企业即时通讯软件有哪些功能?对企业有什么帮助?