使用opencv检测图片中的牙齿(附C++代码)

 

实现效果如下:

873e27a671ce4967b4b71e23b9769736.png

f203a303bc4a409bb9bcde434f558a74.png


牙齿识别项目


对通过摄像头输入的每一帧执行以下算法处理。
通过蓝色滤镜去除杂音/为了让牙齿间的分隔线清晰可辨。
图像二进制化(黑白)/OpenCV提供的Contour函数。
边缘检测识别牙齿形状。
Threshold指定变量K值,对RGB值大于K值的区域进行Threshold处理。此外,K值被指定为与牙齿颜色最相似的值。
采用多边形顶点提取/OpenCV的Contour函数,探索与牙齿形状相似的多边形,并将每个点的集合存储在多边形数据结构中。

关键问题
不能保证在主要的牙齿识别算法中提取的多边形是一个牙齿形状的多边形。这种误认为在大多数情况下,2~3颗牙齿被认为是一个多边形,因此在实际牙科诊疗中使用不足,而非模型模型。
此外,为了解决这个问题,我们利用递归方法再次从一个多边形内部提取牙齿形状,并利用多边形顶点内角来解决多个牙齿下的问题。

 

代码如下:

 

#pragma once
#include "opencv2/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
class teeth {
private:
	int k=180;
public:
	teeth(int _k) {
		k = _k;
	};
	int run(Mat& frame) {
		Mat b = frame.clone();
		auto size = b.size();
		char str[255];
		sprintf(str, "K=%d", k);
		putText(frame, str, Point(10, 40), 2, 1.3, Vec3b(255, 0, 0));
		for (int i = 0; i < size.height; i++)
		{
			for (int j = 0; j < size.width; j++) {
				auto& p = b.at<Vec3b>(i, j);
				float f = (p[0] + p[1] + p[2]) / 3;
				if (f > k && (p[0]>k && p[1]>k && p[2]>k)) { p[0] = 255; p[1] = 255; p[2] = 255; }
				else { p[0] = 0; p[1] = 0; p[2] = 0; }
			}
		}
		teetdetect(k, b, frame);
		int key = waitKey(30);
		if (key == 27)return 0;
		else if (key == 119)k += 10;
		else if (key == 115)k -= 10;
		return 1;
	};
	void teetdetect(int k, Mat& frame, Mat& org) {

		Mat gray;
		vector<vector<Point> > contours;
		cvtColor(frame, gray, COLOR_BGR2GRAY);
		threshold(gray, gray, k, 255, THRESH_BINARY_INV | THRESH_OTSU);
		//imshow("edge", gray);
		findContours(gray, contours, RETR_LIST, CHAIN_APPROX_SIMPLE);

		//contour¸¦ ±Ù»çÈ­ÇÑ´Ù.
		vector<Point2f> approx;
		Mat img_result = frame.clone();

		for (size_t i = 0; i < contours.size(); i++)
		{
			approxPolyDP(Mat(contours[i]), approx, arcLength(Mat(contours[i]), true)*0.0002, true);
			double a = fabs(contourArea(Mat(approx)));
			if (a>1000 && a<10000)
			{
				int size = approx.size();
				vector<int> angle;
				int fi = -1;
				vector<Point2f> ca;
				for (int k = 0; k < size; k++) {
					int ang = GetAngleABC(approx[k], approx[(k + 1) % size], approx[(k + 2) % size]);
					//if (ang >180)break;
					ca.push_back(approx[k]);
					angle.push_back(ang);
				}
				if (ca.size() != 0)approx = ca;
				size = ca.size();
				sort(angle.begin(), angle.end());

				int minAngle = angle.front();
				int maxAngle = angle.back();
				int threshold = 8;

				//µµÇüÀ» ÆÇÁ¤ÇÑ´Ù.
				/*
				if (size <= 3) {
				//continue;
				setLabel(org, "triangle", contours[i]);
				}
				else if (size == 4 && minAngle >= 90 - threshold && maxAngle <= 90 + threshold)
				setLabel(org, "rectangle", contours[i]);
				else if (size == 5 && minAngle >= 108 - threshold && maxAngle <= 108 + threshold)
				setLabel(org, "pentagon", contours[i]);
				else if (size == 6 && minAngle >= 120 - threshold && maxAngle <= 120 + threshold)
				setLabel(org, "hexagon", contours[i]);
				else {
				setLabel(org, to_string(approx.size()), contours[i]);//¾Ë¼ö ¾ø´Â °æ¿ì¿¡´Â ã¾Æ³½ ²ÀÁöÁ¡ °¹¼ö¸¦ Ç¥
				}*/
				//cout << "===" << size << " " << a << endl;
				//Contour¸¦ ±Ù»çÈ­ÇÑ Á÷¼±À» ±×¸°´Ù.
				if (size % 2 == 0) {
					line(org, approx[0], approx[approx.size() - 1], Scalar(0, 255, 0), 3);

					for (int k = 0; k < size - 1; k++)
						line(org, approx[k], approx[k + 1], Scalar(0, 255, 0), 3);
					/*
					for (int k = 0; k < size; k++)
					circle(org, approx[k], 3, Scalar(0, 0, 255));*/
				}
				else {
					line(org, approx[0], approx[approx.size() - 1], Scalar(0, 255, 0), 3);

					for (int k = 0; k < size - 1; k++)
						line(org, approx[k], approx[k + 1], Scalar(0, 255, 0), 3);
					/*
					for (int k = 0; k < size; k++)
					circle(org, approx[k], 3, Scalar(0, 0, 255));*/

				}
			}
		}
		imshow("frame", org);
	};
	void setLabel(Mat& image, string str, vector<Point> contour)
	{
		int fontface = FONT_HERSHEY_SIMPLEX;
		double scale = 0.5;
		int thickness = 1;
		int baseline = 0;

		Size text = getTextSize(str, fontface, scale, thickness, &baseline);
		Rect r = boundingRect(contour);

		Point pt(r.x + ((r.width - text.width) / 2), r.y + ((r.height + text.height) / 2));
		rectangle(image, pt + Point(0, baseline), pt + Point(text.width, -text.height), CV_RGB(200, 200, 200), CV_FILLED);
		putText(image, str, pt, fontface, scale, CV_RGB(0, 0, 0), thickness, 8);
	};
	int GetAngleABC(Point a, Point b, Point c)
	{
		Point ab = { b.x - a.x, b.y - a.y };
		Point cb = { b.x - c.x, b.y - c.y };

		float dot = (ab.x * cb.x + ab.y * cb.y); // dot product
		float cross = (ab.x * cb.y - ab.y * cb.x); // cross product

		float alpha = atan2(cross, dot);

		return (int)floor(alpha * 180.0 / CV_PI + 0.5);
	};

};

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

点云-激光雷达-Slam-三维牙齿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值