开源 企业版 高校版 私有云 模力方舟 AI 队友
代码拉取完成,页面将自动刷新
捐赠
捐赠前请先登录
扫描微信二维码支付
取消
支付完成
支付提示
将跳转至支付宝完成支付
确定
取消
1 Star 0 Fork 0

杨慧强/Image-processing-algorithm

加入 Gitee
与超过 1400万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
已有帐号? 立即登录
文件
master
分支 (1)
master
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
项目仓库所选许可证以仓库主分支所使用许可证为准
master
分支 (1)
master
克隆/下载
克隆/下载
提示
下载代码请复制以下命令到终端执行
为确保你提交的代码身份被 Gitee 正确识别,请执行以下命令完成配置
初次使用 SSH 协议进行代码克隆、推送等操作时,需按下述提示完成 SSH 配置
1 生成 RSA 密钥
2 获取 RSA 公钥内容,并配置到 SSH公钥
在 Gitee 上使用 SVN,请访问 使用指南
使用 HTTPS 协议时,命令行会出现如下账号密码验证步骤。基于安全考虑,Gitee 建议 配置并使用私人令牌 替代登录密码进行克隆、推送等操作
Username for 'https://gitee.com': userName
Password for 'https://userName@gitee.com': # 私人令牌
master
分支 (1)
master
Image-processing-algorithm
/
RectangleDetection.cpp
Image-processing-algorithm
/
RectangleDetection.cpp
RectangleDetection.cpp 10.80 KB
一键复制 编辑 原始数据 按行查看 历史
BBuf 提交于 2020年03月09日 22:11 +08:00 . Add RectangleDtection.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
#include <stdio.h>
#include <iostream>
#include <immintrin.h>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
//针对灰度图的中值滤波+CVPR 2019的SideWindowFilter
//其他种类的滤波直接换核即可
//记录每一个方向的核的不为0的元素个数
int cnt[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
// 记录每一个方向的滤波器
vector <int> filter[8];
//初始化半径为radius的滤波器,原理可以看https://mp.weixin.qq.com/s/vjzZjRoQw7MnkqAfvwBUNA
void InitFilter(int radius) {
int n = radius * 2 + 1;
for (int i = 0; i < 8; i++) {
cnt[i] = 0;
filter[i].clear();
}
for (int i = 0; i < 8; i++) {
for (int x = 0; x < n; x++) {
for (int y = 0; y < n; y++) {
if (i == 0 && x <= radius && y <= radius) {
filter[i].push_back(1);
}
else if (i == 1 && x <= radius && y >= radius) {
filter[i].push_back(1);
}
else if (i == 2 && x >= radius && y <= radius) {
filter[i].push_back(1);
}
else if (i == 3 && x >= radius && y >= radius) {
filter[i].push_back(1);
}
else if (i == 4 && x <= radius) {
filter[i].push_back(1);
}
else if (i == 5 && x >= radius) {
filter[i].push_back(1);
}
else if (i == 6 && y >= radius) {
filter[i].push_back(1);
}
else if (i == 7 && y <= radius) {
filter[i].push_back(1);
}
else {
filter[i].push_back(0);
}
}
}
}
for (int i = 0; i < 8; i++) {
int sum = 0;
for (int j = 0; j < filter[i].size(); j++) sum += filter[i][j] == 1;
cnt[i] = sum;
}
}
//实现Side Window Filter的中值滤波,强制保边
Mat MedianSideWindowFilter(Mat src, int radius = 1) {
int row = src.rows;
int col = src.cols;
int channels = src.channels();
InitFilter(radius);
//针对灰度图
vector <int> now;
if (channels == 1) {
Mat dst(row, col, CV_8UC1);
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (i < radius || i + radius >= row || j < radius || j + radius >= col) {
dst.at<uchar>(i, j) = src.at<uchar>(i, j);
continue;
}
int minn = 256;
int pos = 0;
for (int k = 0; k < 8; k++) {
int val = 0;
int id = 0;
now.clear();
for (int x = -radius; x <= radius; x++) {
for (int y = -radius; y <= radius; y++) {
//if (x == 0 && y == 0) continue;
if (filter[k][id]) now.push_back(src.at<uchar>(i + x, j + y) * filter[k][id]);
id++;
//val += src.at<uchar>(i + x, j + y) * filter[k][id++];
}
}
sort(now.begin(), now.end());
int mid = (int)(now.size());
val = now[mid / 2];
if (abs(val - src.at<uchar>(i, j)) < minn) {
minn = abs(val - src.at<uchar>(i, j));
pos = k;
}
}
int val = 0;
int id = 0;
now.clear();
for (int x = -radius; x <= radius; x++) {
for (int y = -radius; y <= radius; y++) {
//if (x == 0 && y == 0) continue;
if (filter[pos][id]) now.push_back(src.at<uchar>(i + x, j + y) * filter[pos][id]);
id++;
//val += src.at<uchar>(i + x, j + y) * filter[k][id++];
}
}
sort(now.begin(), now.end());
int mid = (int)(now.size());
val = now[mid / 2];
dst.at<uchar>(i, j) = val;
}
}
return dst;
}
//针对RGB图
Mat dst(row, col, CV_8UC3);
for (int c = 0; c < 3; c++) {
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (i < radius || i + radius >= row || j < radius || j + radius >= col) {
dst.at<Vec3b>(i, j)[c] = src.at<Vec3b>(i, j)[c];
continue;
}
int minn = 256;
int pos = 0;
for (int k = 0; k < 8; k++) {
int val = 0;
int id = 0;
now.clear();
for (int x = -radius; x <= radius; x++) {
for (int y = -radius; y <= radius; y++) {
//if (x == 0 && y == 0) continue;
//val += src.at<Vec3b>(i + x, j + y)[c] * filter[k][id++];
if (filter[k][id]) now.push_back(src.at<Vec3b>(i + x, j + y)[c] * filter[k][id]);
id++;
}
}
sort(now.begin(), now.end());
int mid = (int)(now.size());
val = now[mid / 2];
if (abs(val - src.at<Vec3b>(i, j)[c]) < minn) {
minn = abs(val - src.at<Vec3b>(i, j)[c]);
pos = k;
}
}
int val = 0;
int id = 0;
now.clear();
for (int x = -radius; x <= radius; x++) {
for (int y = -radius; y <= radius; y++) {
//if (x == 0 && y == 0) continue;
//val += src.at<Vec3b>(i + x, j + y)[c] * filter[k][id++];
if (filter[pos][id]) now.push_back(src.at<Vec3b>(i + x, j + y)[c] * filter[pos][id]);
id++;
}
}
sort(now.begin(), now.end());
int mid = (int)(now.size());
val = now[mid / 2];
dst.at<Vec3b>(i, j)[c] = val;
}
}
}
return dst;
}
const double eps = 1e-7;
//获取pt0->pt1向量和pt0->pt2向量之间的夹角
static double angle(Point pt1, Point pt2, Point pt0)
{
double dx1 = pt1.x - pt0.x;
double dy1 = pt1.y - pt0.y;
double dx2 = pt2.x - pt0.x;
double dy2 = pt2.y - pt0.y;
return (dx1*dx2 + dy1*dy2) / sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + eps);
}
//寻找矩形
static void findSquares(const Mat& image, vector<vector<Point> >& squares, int N = 5, int thresh = 50)
{
//滤波可以提升边缘检测的性能
Mat timg(image);
// 普通中值滤波
//medianBlur(image, timg, 9);
// SideWindowFilter的中值滤波
timg = MedianSideWindowFilter(image, 4);
Mat gray0(timg.size(), CV_8U), gray;
// 存储轮廓
vector<vector<Point> > contours;
// 在图像的每一个颜色通道寻找矩形
for (int c = 0; c < 3; c++)
{
int ch[] = { c, 0 };
// 函数功能:mixChannels主要就是把输入的矩阵(或矩阵数组)的某些通道拆分复制给对应的输出矩阵(或矩阵数组)的某些通道中,其中的对应关系就由fromTo参数制定.
// 接口:void mixChannels (const Mat* src , int nsrc , Mat* dst , int ndst , const int* fromTo , size_t npairs );
// src: 输入矩阵,可以为一个也可以为多个,但是矩阵必须有相同的大小和深度.
// nsrc: 输入矩阵的个数.
// dst: 输出矩阵,可以为一个也可以为多个,但是所有的矩阵必须事先分配空间(如用create),大小和深度须与输入矩阵等同.
// ndst: 输出矩阵的个数
// fromTo:设置输入矩阵的通道对应输出矩阵的通道,规则如下:首先用数字标记输入矩阵的各个通道。输入矩阵个数可能多于一个并且每个矩阵的通道可能不一样,
// 第一个输入矩阵的通道标记范围为:0 ~src[0].channels() - 1,第二个输入矩阵的通道标记范围为:src[0].channels() ~src[0].channels() + src[1].channels() - 1,
// 以此类推;其次输出矩阵也用同样的规则标记,第一个输出矩阵的通道标记范围为:0 ~dst[0].channels() - 1,第二个输入矩阵的通道标记范围为:dst[0].channels()
// ~dst[0].channels() + dst[1].channels() - 1, 以此类推;最后,数组fromTo的第一个元素即fromTo[0]应该填入输入矩阵的某个通道标记,而fromTo的第二个元素即
// fromTo[1]应该填入输出矩阵的某个通道标记,这样函数就会把输入矩阵的fromTo[0]通道里面的数据复制给输出矩阵的fromTo[1]通道。fromTo后面的元素也是这个
// 道理,总之就是一个输入矩阵的通道标记后面必须跟着个输出矩阵的通道标记.
// npairs: 即参数fromTo中的有几组输入输出通道关系,其实就是参数fromTo的数组元素个数除以2.
mixChannels(&timg, 1, &gray0, 1, ch, 1);
// 尝试几个不同的阈值
for (int l = 0; l < N; l++)
{
// hack: use Canny instead of zero threshold level.
// Canny helps to catch squares with gradient shading
// 在级别为0的时候不使用阈值为0,而是使用Canny边缘检测算子
if (l == 0)
{
// void Canny( InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize = 3, bool L2gradient = false);
// 第一个参数:输入图像(八位的图像)
// 第二个参数:输出的边缘图像
// 第三个参数:下限阈值,如果像素梯度低于下限阈值,则将像素不被认为边缘
// 第四个参数:上限阈值,如果像素梯度高于上限阈值,则将像素被认为是边缘(建议上限是下限的2倍或者3倍)
// 第五个参数:为Sobel()运算提供内核大小,默认值为3
// 第六个参数:计算图像梯度幅值的标志,默认值为false
Canny(gray0, gray, 5, thresh, 5);
// 执行形态学膨胀操作
dilate(gray, gray, Mat(), Point(-1, -1));
}
else
{
// 当l不等于0的时候,执行 tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0
gray = gray0 >= (l + 1) * 255 / N;
}
// 寻找轮廓并将它们全部存储为列表
findContours(gray, contours, RETR_LIST, CHAIN_APPROX_SIMPLE);
//存储一个多边形(矩形)
vector<Point> approx;
// 测试每一个轮廓
for (size_t i = 0; i < contours.size(); i++)
{
// 近似轮廓,精度与轮廓周长成正比,主要功能是把一个连续光滑曲线折线化,对图像轮廓点进行多边形拟合。
// 函数声明:void approxPolyDP(InputArray curve, OutputArray approxCurve, double epsilon, bool closed)
// InputArray curve:一般是由图像的轮廓点组成的点集
// OutputArray approxCurve:表示输出的多边形点集
// double epsilon:主要表示输出的精度,就是两个轮廓点之间最大距离数,5,6,7,,8,,,,,
// bool closed:表示输出的多边形是否封闭
// arcLength 计算图像轮廓的周长
approxPolyDP(Mat(contours[i]), approx, 20, true);
// 近似后,方形轮廓应具有4个顶点
// 相对较大的区域(以滤除嘈杂的轮廓)并且是凸集。
// 注意: 使用面积的绝对值,因为面积可以是正值或负值-根据轮廓方向
if (approx.size() == 4 &&
fabs(contourArea(Mat(approx))) > 1000 &&
isContourConvex(Mat(approx)))
{
double maxCosine = 0;
for (int j = 2; j < 5; j++)
{
// 找到相邻边之间的角度的最大余弦
double cosine = fabs(angle(approx[j % 4], approx[j - 2], approx[j - 1]));
maxCosine = MAX(maxCosine, cosine);
}
// 如果所有角度的余弦都很小(所有角度均为90度),将顶点集合写入结果vector
if (maxCosine < 0.3)
squares.push_back(approx);
}
}
}
}
}
//在图像上画出方形
void drawSquares(Mat &image, const vector<vector<Point> >& squares) {
for (size_t i = 0; i < squares.size(); i++)
{
const Point* p = &squares[i][0];
int n = (int)squares[i].size();
//不检测边界
if (p->x > 3 && p->y > 3)
polylines(image, &p, &n, 1, true, Scalar(0, 255, 0), 3, LINE_AA);
}
}
int main() {
Mat src = cv::imread("F:\\stone.jpg");
vector<vector<Point> > squares;
findSquares(src, squares, 5, 50);
drawSquares(src, squares);
imshow("result", src);
imwrite("F:\\res2.jpg", src);
waitKey(0);
return 0;
}
Loading...
举报
举报成功
我们将于2个工作日内通过站内信反馈结果给你!
请认真填写举报原因,尽可能描述详细。
请选择举报类型
取消
发送
误判申诉

此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。

如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。

取消
提交

发行版

暂无发行版

贡献者

全部

语言

近期动态

不能加载更多了
编辑仓库简介
简介内容
主页
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/i-android/Image-processing-algorithm.git
git@gitee.com:i-android/Image-processing-algorithm.git
i-android
Image-processing-algorithm
Image-processing-algorithm
master
点此查找更多帮助

搜索帮助

评论
仓库举报
回到顶部
登录提示
该操作需登录 Gitee 帐号,请先登录后再操作。
立即登录
没有帐号,去注册

AltStyle によって変換されたページ (->オリジナル) /