前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >OpenCV极坐标变换函数warpPolar的使用

OpenCV极坐标变换函数warpPolar的使用

作者头像
Vaccae
发布2023-12-14 10:28:36
2000
发布2023-12-14 10:28:36
举报
文章被收录于专栏:微卡智享微卡智享

学更好的别人,做更好的自己。——《微卡智享》

本文长度为1702,预计阅读4分钟

前言

前阵子在做方案时,得了几张骨钉的图片,骨科耗材批号效期管理一直是比较麻烦的,贴RFID标签成本太高,所以一般考虑还是OCR的识别比较好,因为本身骨钉的字符是按圆印上去的,直接截取图片进行OCR没法识别,需要经过图像处理后再识别,所以这篇就是学习一下OpenCV的极坐标变换函数。

实现效果

源图像

查找出骨钉后用极坐标变换生成的图像

图像本来就是手机拍的,反光也比较厉害,所以本篇主要就是介绍极坐标变换,最后的OCR识别就不在这个范围内了。

极坐标变换函数

代码语言:javascript
复制
void cv::warpPolar(InputArray   src, —原图像,单通道灰度图和三通道彩色图都可
                   OutputArray dst,  —输出图像,与原图像具有相同的数据类型和通道数
                   Size   dsize, —目标图像大小,如图不填这个值或dsize两个值中的某个值
                   Point2f  center, —极坐标变换时原点坐标
                   double   maxRadius, —极坐标系的极半径最大值
                   int   flags ) —插值方法与极坐标映射方法标志。方法之间通过“+”或者“|”号进行连接

重点说明:

dsize:目标图像大小,Size(0,0)主要就是填写里面的width和height

  1. 当width和height两个值均<=0(默认),则目标图像将具有(几乎)相同的源边界圆面积
  2. 当width>0并且height <= 0,目标图像区域将与边界圆区域按比例缩放
  3. 当width和height都 > 0, 目标图像将具有给定的大小,因此边界圆的面积将缩放为dsize

flags:插值方法与极坐标映射方法标志

插值方法:图像缩放之后,肯定像素要进行重新计算的,就靠这个参数来指定重新计算像素的方式,一般分为:

  • INTER_NEAREST - 最邻近插值
  • INTER_LINEAR - 双线性插值,如果最后一个参数你不指定,默认使用这种方法
  • INTER_AREA -区域插值
  • INTER_CUBIC - 4x4像素邻域内的双立方插值
  • INTER_LANCZOS4 - 8x8像素邻域内的Lanczos插值

这个我们一般都默认为INTER_LINEAR - 双线性插值即可。

极坐标映射方法里面就三个,主要是极坐标变换、半对数极坐标变换和逆变换。

  • WARP_POLAR_LINEAR - 极坐标变换
  • WARP_POLAR_LOG - 半对数极坐标变换
  • WARP_INVERSE_MAP - 逆变换

核心来说其实就是两个极坐标变换和半对数极坐标变换,它们两个都是由圆变换为矩形,而逆变换就是针对两种变换由矩形再转为圆。逆变换在原来的flags后面用“+”连接即可。


warpPolar的起始位置和图像旋转角度

极坐标变换的起始位置默认就是3点钟的方向,上图中用蓝色箭头指的就是开始的位置,从3点钟方向顺时针来进行变换。

生成的矩形默认是向下垂直排列的,按我们自己的习惯肯定还需要将图像进行旋转,所以转换完后需要用rotate做一个图像的旋转

代码如下:

代码语言:javascript
复制
//极坐标变换
//参数 flags=INTER_LINEAR 双线性插值
Mat warpPolarMat(Mat src, int flags = INTER_LINEAR + WARP_POLAR_LINEAR) {
  // 圆心坐标
  Point2f center = Point2f(src.cols / 2, src.rows / 2);
  // 圆的半径
  double maxRadius = min(center.y, center.x) - 1;
  // 圆的周长
  int circumference = maxRadius * 2 * 3.14;


  //输出图像
  Mat dst;
  // 极坐标变换, Size()表示OpenCV根据输入自行决定输出图像尺寸
  warpPolar(src, dst, Size(0, 0), center, maxRadius, flags);

  // 改变结果方向
  rotate(dst, dst, ROTATE_90_COUNTERCLOCKWISE);

  return dst;
}

实现思路及完整代码

#

实现步骤

1

图像简单处理(灰度图、中值滤波)

2

霍夫圆检测获取到图像中的骨钉

3

将获取到的圆每个分别截取出来进行极坐标变换显示出来

完整代码

代码语言:javascript
复制
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

//显示窗口设置  
//参数  img 显示的图像源,
//      winname 显示的窗口名称,
//      pointx  显示的坐标x
//      pointy  显示的坐标y
void setshowwindow(Mat img, string winname, int pointx, int pointy)
{
  //设置显示窗口
  namedWindow(winname, WindowFlags::WINDOW_NORMAL);
  //设置图像显示大小
  resizeWindow(winname, img.size());
  //设置图像显示位置
  moveWindow(winname, pointx, pointy);
}


//极坐标变换
//参数 flags=INTER_LINEAR 双线性插值
Mat warpPolarMat(Mat src, int flags = INTER_LINEAR + WARP_POLAR_LINEAR) {
  // 圆心坐标
  Point2f center = Point2f(src.cols / 2, src.rows / 2);
  // 圆的半径
  double maxRadius = min(center.y, center.x) - 1;
  // 圆的周长
  int circumference = maxRadius * 2 * 3.14;


  //输出图像
  Mat dst;
  // 极坐标变换, Size()表示OpenCV根据输入自行决定输出图像尺寸
  warpPolar(src, dst, Size(0, 0), center, maxRadius, flags);

  // 改变结果方向
  rotate(dst, dst, ROTATE_90_COUNTERCLOCKWISE);

  return dst;
}

int main(int argc, char** argv) {

  //测试图片文件  
  string testfile = "E:/DCIM/imagetest/06.png";

  //读取图片
  Mat src = imread(testfile);
  //修改图片大小
  setshowwindow(src, "src", 50, 200);
  imshow("src", src);
  //灰度图
  Mat gray;
  cvtColor(src, gray, COLOR_BGR2GRAY);

  //中值滤波
  medianBlur(gray, gray, 9);

  //霍夫圆检测
  vector<Vec3f> circles;
  HoughCircles(gray, circles, HOUGH_GRADIENT, 1, 50, 100, 100);

  //绘制检测到的圆型
  for (size_t i = 0; i < circles.size(); ++i) {
    Vec3f item = circles[i];
    //绘制检测到的圆
    circle(src, Point(item[0], item[1]), item[2], Scalar(0, 0, 255));
    //根据圆点和半径生成矩形
    Rect rect = Rect(Point(item[0] - item[2], item[1] - item[2]), Point(item[0] + item[2], item[1] + item[2]));
    //截图到当前圆的图像
    Mat rectsrc = src(rect);

    //极坐标变换
    Mat rectdst, showmat;
    rectdst = warpPolarMat(rectsrc);

    //设置当前圆显示位置
    String title = "circle" + to_string(i);
    String title2 = "roi" + to_string(i);
    if ((i % 2) == 0) {
      setshowwindow(rectsrc, title, 50, rectsrc.rows * (i / 2) + 2);
      setshowwindow(rectdst, title2, 50 + rectsrc.cols + 2, rectsrc.rows * (i / 2) + 2);
    }
    else {
      setshowwindow(rectsrc, title, (rectsrc.rows + rectdst.rows) * 2 , rectsrc.rows * (i / 2) + (i % 2) + 2);
      setshowwindow(rectdst, title2, (rectsrc.rows + rectdst.rows) *2 + rectsrc.rows , rectsrc.rows * (i / 2) + (i % 2) + 2);
    }
    imshow(title, rectsrc);
    imshow(title2, rectdst);
  }



  waitKey();

  return 0;
}

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-12-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 微卡智享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 完整代码
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档