用opencv简单的检测三角形、正方形、圆以及它们的颜色

2023-11-04

源码下载地址点击打开链接

原始图片:


检测结果:


检测后图片:



下面为完整代码:

#include <iostream>
#include "opencv2/opencv.hpp"
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv/cv.h>
#include <opencv/highgui.h>

using namespace cv;
using namespace std;


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;

double cosine = (dx1*dx2 + dy1*dy2) / (sqrt(dx1*dx1 + dy1*dy1) * sqrt(dx2*dx2 + dy2*dy2) + 1e-10);


return cosine;
}

enum string{ 红, 橙, 黄, 绿, 青, 蓝, 紫, 黑, 白, 其他 }colors;


void  get_point(vector<Point > contour,Point *p){
if (contour.size() == 0)
return ;
int totalx = 0;
int totaly = 0;
int n = 0;
for (size_t i = 0; i < contour.size(); i++){
n++;
totalx = totalx + contour[i].x;
totaly = totaly + contour[i].y;
}
(*p).x = totalx / n;
(*p).y = totaly / n;
}

int detect_color(Point p, Mat & src, Mat & rgb)
{
int res=-1;
int h=src.at<Vec3b>(p.y, p.x)[0];
int s = src.at<Vec3b>(p.y, p.x)[1];
int v = src.at<Vec3b>(p.y, p.x)[2];

if (h >=0 && h <= 10 && s > 43  && v > 46 ){
res = 0;
}
else if (h >= 156 && h <= 180 && s > 43 && v > 46){
res = 0;
}
else if (h >= 11 && h <= 25 && s > 43 && v > 46){
res = 1;
}
else if (h >= 26 && h <= 34 && s > 43 && v > 46){
res = 2;
}
else if (h >= 35 && h <= 77 && s > 43 && v > 46){
res = 3;
}
else if (h >= 78 && h <= 99 && s > 43 && v > 46){
res = 4;
}
else if (h >= 100 && h <= 124 && s > 43 && v > 46){
res = 5;
}
else if (h >= 125 && h <= 155 && s > 43 && v > 46){
res = 6;
}
else 
{
int g = rgb.at<Vec3b>(p.y, p.x)[0];
int b = rgb.at<Vec3b>(p.y, p.x)[1];
int r = rgb.at<Vec3b>(p.y, p.x)[2];
if (g <=10 && b <= 10 && r <=10){
res = 7;
}
else if (g >=200 && b>=200 && r>=200){
res = 8;
}
else{
res = 9;
}
}
return res;
}

void shape_rgb(Mat & src, Mat & rgb,CvSeq *first_contour, int *num_squares, vector<int>& squares_color, vector<vector<Point>>& squares, vector<vector<Point>>& circles, int *num_circles, vector<int>& circles_color, vector<vector<Point>>& triangles, int *num_triangles, vector<int>& triangles_color){
vector<Point > contour;
vector<Point > approx;
Point poi = 0;
int nu = -1;
int cnt = 0;
for (; first_contour != 0; first_contour = first_contour->h_next)
{
vector <Point>().swap(contour);
vector <Point>().swap(approx);
cnt++;
for (int i = 0; i<first_contour->total; ++i) 
{
Point *p = (Point *)cvGetSeqElem(first_contour, i);
Point pp = *p;
contour.push_back(pp);
}

approxPolyDP(contour, approx, arcLength(contour, true)*0.02, true);
squares.push_back(approx);

if (approx.size() == 4 &&
fabs(contourArea(approx)) >= 1000 &&
isContourConvex(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);
}
if (maxCosine <= 0.3)             //直角的矩形应该为0
{
squares.push_back(approx);
*num_squares = *num_squares + 1;
nu = -1;
get_point(approx, &poi);
int nu = detect_color(poi, src, rgb);
squares_color.push_back(nu);
}
//re = re + 1;
}
else if (approx.size() == 3 &&fabs(contourArea(approx)) >= 500 &&isContourConvex(approx)){ //tt = tt + 1; //
triangles.push_back(approx);
*num_triangles = *num_triangles + 1;
nu = -1;
get_point(approx, &poi);
int nu = detect_color(poi, src, rgb);
triangles_color.push_back(nu);
}
else if (approx.size() >=8 && fabs(contourArea(approx)) >= 500 && isContourConvex(approx)){ //tt = tt + 1; //
circles.push_back(approx);
*num_circles = *num_circles + 1;
nu = -1;
get_point(approx, &poi);
int nu = detect_color(poi, src, rgb);
circles_color.push_back(nu);
}
}
}

void drawSquares(Mat img, vector<vector<Point>> squares)
{
polylines(img, squares, true, Scalar(0, 0, 0), 1, LINE_AA);

}


int main()
{
IplImage *src = cvLoadImage("dect.jpg", 1);
IplImage* dst = cvCreateImage(cvGetSize(src), 8, 3);
IplImage* dst1 = cvCreateImage(cvGetSize(src), 8, 1);
IplImage* color_dst = cvCreateImage(cvGetSize(src), 8, 3);
CvMemStorage* storage = cvCreateMemStorage(0);
CvMemStorage* Rstorage = cvCreateMemStorage(0);
CvMemStorage* Gstorage = cvCreateMemStorage(0);
CvMemStorage* Bstorage = cvCreateMemStorage(0);
CvSeq* lines = 0;
int i;
IplImage* src1 = cvCreateImage(cvSize(src->width, src->height), IPL_DEPTH_8U, 1);
IplImage*  RedImage = cvCreateImage(cvGetSize(src), 8, 1);
IplImage*  GreenImage = cvCreateImage(cvGetSize(src), 8, 1);
IplImage*  BlueImage = cvCreateImage(cvGetSize(src), 8, 1);
IplImage*  Rgray = cvCreateImage(cvGetSize(src), 8, 1);
IplImage*  Ggray = cvCreateImage(cvGetSize(src), 8, 1);
IplImage*  Bgray = cvCreateImage(cvGetSize(src), 8, 1);

cvSplit(src, BlueImage, GreenImage, RedImage, 0);
cvThreshold(BlueImage, Bgray, 100, 255, CV_THRESH_BINARY);
cvThreshold(GreenImage, Ggray, 100, 255, CV_THRESH_BINARY);
cvThreshold(RedImage, Rgray, 100, 255, CV_THRESH_BINARY);

vector<vector<Point>> contours;

CvZero(dst);
CvSeq *first_contour = NULL;
CvSeq *Rfirst_contour = NULL;
CvSeq *Gfirst_contour = NULL;
CvSeq *Bfirst_contour = NULL;

cvFindContours(Rgray, storage, &first_contour, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
for (; first_contour != 0; first_contour = first_contour->h_next)
{
if (first_contour->h_next = 0)
break;
cvDrawContours(dst, first_contour, CV_RGB(255, 255, 255), CV_RGB(255, 255, 255), 2, CV_FILLED, 8, cvPoint(0, 0));
}
cvCvtColor(dst, dst1, CV_BGR2GRAY);

cvFindContours(Rgray, Gstorage, &Gfirst_contour, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
cvFindContours(Ggray, Rstorage, &Rfirst_contour, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
cvFindContours(Bgray, Bstorage, &Bfirst_contour, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
cvZero(Rgray);
cvZero(Bgray);
cvZero(Ggray);

cvDrawContours(Rgray, Rfirst_contour, 255, 255, 1, CV_FILLED, 8, cvPoint(0, 0));
cvDrawContours(Ggray, Gfirst_contour, 255, 255, 1, CV_FILLED, 8, cvPoint(0, 0));
cvDrawContours(Bgray, Bfirst_contour, 255,255, 1, CV_FILLED, 8, cvPoint(0, 0));

cvMerge(Bgray, Ggray, Rgray,0,dst);
cvCvtColor(dst, dst1, CV_BGR2GRAY);
cvThreshold(dst1, dst1, 240, 255, CV_THRESH_BINARY_INV);
cvFindContours(dst1, storage, &first_contour, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

int num_squares=0;
vector<int> squares_color;
vector<vector<Point>> squares;
vector<vector<Point>> circles;
int num_circles=0; 
vector<int> circles_color;
vector<vector<Point>> triangles;
int num_triangles = 0;
vector<int> triangles_color;
Mat img;
img = cvarrToMat(src);
Mat imgHSV;

cvtColor(img, imgHSV, COLOR_BGR2HSV); //Convert the captured frame from BGR to HSV  
shape_rgb(imgHSV, img, first_contour, &num_squares, squares_color, squares, circles, &num_circles, circles_color, triangles, &num_triangles, triangles_color);


vector<int> mycircles_color;
IplImage* gray = cvCreateImage(cvGetSize(src), 8, 1);


CvMemStorage* mystorage = cvCreateMemStorage(0);
cvCvtColor(src, gray, CV_BGR2GRAY);
cvSmooth(gray, gray, CV_GAUSSIAN, 9, 9); // smooth it, otherwise a lot of false circles may be detected
CvSeq* mycircles = cvHoughCircles(gray, mystorage, CV_HOUGH_GRADIENT, 2, gray->height / 4, 200, 100);
Point circl = 0;
for (i = 0; i < mycircles->total; i++)
{
float* p = (float*)cvGetSeqElem(mycircles, i);
cvCircle(src, cvPoint(cvRound(p[0]), cvRound(p[1])), 3, CV_RGB(0, 255, 0), -1, 8, 0);
cvCircle(src, cvPoint(cvRound(p[0]), cvRound(p[1])), cvRound(p[2]), CV_RGB(255, 0, 0), 3, 8, 0);
circl.y = p[1];
circl.x = p[0];
int nu = detect_color(circl, imgHSV, img);
mycircles_color.push_back(nu);
}
drawSquares(img, squares);

printf("矩形数量:");
printf("%d", squares_color.size());
printf("\n");
printf("矩形颜色:");
printf("\n");
for (size_t i = 0; i < squares_color.size(); i++){
switch (squares_color[i])//判断枚举变量day的值  
{
case 0:printf("红\t"); break;
case 1:printf("橙\t"); break;
case 2:printf("黄\t"); break;
case 3:printf("绿\t"); break;
case 4:printf("青\t"); break;
case 5:printf("蓝\t"); break;
case 6:printf("紫\t"); break;
case 7:printf("黑\t"); break;
case 8:printf("白\t"); break;
case 9:printf("其他\t"); break;
}
//printf("\n");
}
printf("\n");
printf("圆形数量:");
printf("%d", num_circles);
printf("\n");
printf("圆形颜色:");
printf("\n");
for (size_t i = 0; i < circles_color.size(); i++){
switch (circles_color[i])//判断枚举变量day的值  
{
case 0:printf("红\t"); break;
case 1:printf("橙\t"); break;
case 2:printf("黄\t"); break;
case 3:printf("绿\t"); break;
case 4:printf("青\t"); break;
case 5:printf("蓝\t"); break;
case 6:printf("紫\t"); break;
case 7:printf("黑\t"); break;
case 8:printf("白\t"); break;
case 9:printf("其他\t"); break;
}
}
printf("\n");
printf("三角形数量:");
printf("%d", num_triangles);
printf("\n");
printf("三角形颜色:");
printf("\n");
for (size_t i = 0; i < triangles_color.size(); i++){
switch (triangles_color[i])//判断枚举变量day的值  
{
case 0:printf("红\t"); break;
case 1:printf("橙\t"); break;
case 2:printf("黄\t"); break;
case 3:printf("绿\t"); break;
case 4:printf("青\t"); break;
case 5:printf("蓝\t"); break;
case 6:printf("紫\t"); break;
case 7:printf("黑\t"); break;
case 8:printf("白\t"); break;
case 9:printf("其他\t"); break;
}
}
printf("\n");

cvNamedWindow("原始图片", 1);
cvShowImage("原始图片", dst);
cvNamedWindow("检测后图片", 1);
cvShowImage("检测后图片", src);
cvWaitKey(0);
cvDestroyWindow("原始图片");
cvDestroyWindow("检测后图片");
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

用opencv简单的检测三角形、正方形、圆以及它们的颜色 的相关文章

  • 数据仓库_数据仓库_缓慢渐变维度实现的几种思路

    数仓缓慢渐变维度表设计 另一篇比较好的文章结合实际案例 数仓建设 缓慢变化维的10种处理方式 雾岛与鲸的博客 CSDN博客 缓慢渐变维度 维度数据会随着时间发生变化 变化速度比较缓慢 这种维度数据通常称作缓慢渐变维 由于数据仓库需要追溯历史
  • Opencart3.0后台选择产品分类处分类显示不全解决方案

    无论后台有多少个分类 这里总是显示不全 解决方法 修改admin controller catalog category php 里面的autocomplete方法 过滤数组默认限制了5条数据 修改为自己想要的即可 filter data
  • ARM架构的STM32F103和RISC-V架构的CH32V103对比

    STM32F103和CH32V103对比如下图 STM32F103 概述 STM32F103 资源配置 CH32V103概述 CH32V103资源配置
  • 探索react技术栈-技术选型

    一 前言 陆陆续续接触react有一年多了 深感react的简单优雅 急切地想用react去开发程序 但是热情之后 发现react社区的种种问题 比如在社区打打常常提到的 angular vs react react作为view层的库 并没
  • Vue双向数据绑定原理(面试必问)

    答案 vue js是采用数据劫持结合发布者 订阅者模式的方式 通过Object defineProperty 来劫持各个属性的setter getter 在数据变动时发布消息给订阅者 触发相应的监听回调来渲染视图 具体步骤 需要observ
  • 谭传奇-individual_project_word_frequency

    首先 印证了闫导的一句话 一星期都在写软工作业 其实也没有那么夸张了 装Win7花了1天 装VS2012加熟悉Win7花了接近2天 真正写作业的时间也没有那么长 言归正传 我估计大概用5个小时左右 实际前前后后大大小小时间加起来6 7个小时
  • 2020年第十一届蓝桥杯决赛Python组(真题+解析+代码):阶乘约数

    1 真题 2 解析 难度系数 考察题型 数论 涉及知识点 约数定理 思路分析 一开始我是想着直接分解因数 结果100 的时间复杂度实在太大 所以这道题只能靠巧取解决 搬出适合本题的工具 质数 唯一分解公式 质数有2 3 5 7 11 13
  • 关于mongodb服务自动停止问题

    昨天发现系统宕机了 查了下原因是mongodb服务停了 看了下日志 2017 10 18T18 09 59 181 0800 I CONTROL signalProcessingThread got signal 1 Hangup will
  • RIDE元素定位简单用法二

    悬浮菜单 隐藏菜单定位和点击功能定位 鼠标移动到该位置时 菜单显示 先定位到鼠标停留位置 然后定位隐藏菜单或链接 定位方法 Mouse Over 我们的服务路径 Click Link 测试点击的链接 关于定位鼠标介绍 Mouse Over
  • PAT 5 凑算式(dfs)

    凑算式 这个算式中A I代表1 9的数字 不同的字母代表不同的数字 比如 6 8 3 952 714 就是一种解法 5 3 1 972 486 是另一种解法 这个算式一共有多少种解法 注意 你提交应该是个整数 不要填写任何多余的内容或说明性
  • 彻底解决Jetson Inference关于box.com连不上的问题

    jetson inference教程 国内镜像 1 安装git和cmake 并检出库到本地 sudo apt get install git cmake git clone GitHub dusty nv jetson inference
  • 2021 10-24

    let person name 张三 age 23 let p new Proxy person set target prpoName value Reflect set target prpoName value get target
  • 在物联网中应用机器学习

    欢迎大家前往腾讯云 社区 获取更多腾讯海量技术实践干货哦 本文由未来守护者发表于云 社区专栏 本项目探讨如何将机器学习 Machine learning 应用到物联网 IoT Internet of Things 中 我们将使用 Andro
  • 如何在windows下使用vscode畅快的调试bash shell

    前言 在linux随然有很多的可以调试的bash的工具 但是如果不用ubuntu或者其它linux系的桌面系统 只有命令行的情况下 还是很吃力的 当然了 大神级别可以畅快的书写并调试 但是对于我等小白 空格多一个少一个 只能望尘莫及了 所以
  • Python 安装dlib解决办法

    pypi python org pypi dlib 19 6 0 下载 dlib 19 6 0 cp36 cp36m win amd64 whl 成功安装 dlib 但是import 时候失败 尝试 pip install dlib 19
  • matlab实现神经网络算法,人工神经网络matlab代码

    求一段神经网络MATLAB代码 50 function presim ss net simnonlin y d n y 时间序列数据 列向量 d 时间延迟参数 正整数 n 用于训练的点的个数 正整数trainset gettrain y d
  • 新手学习python语言基础知识第四天

    语言基础 day4 循环 01 for循环 循环 让代码重复执行 代码写一遍 运行的时候可以执行多次 1 for循环 for循环如图 语法 for 变量 in 序列 循环体 说明 for in 关键字 固定写法 变量 写一个变量名 可以是已
  • JAVA ~ FFmpegFrameRecorder用H264编码封装mp4 有声音无图像

    1问题描述 最近公司做的关于摄像机录制视频保存的问题 发送录制了视频上传至服务器中在浏览器上播放有声音无图像 因为自己是这方面的小白 在自己也在前面博客中也分享了rtsp流媒体如何播放及在H5中嵌入vlc 帧抓取图片及录制视频等文章 希望对
  • 使用ai实现后端端口的调用

    一 创建flask框架 1 创建框架提示词 我需要一个flask框架的demo 把端口号改为8080 需要热加载功能 支持在线更改 请给我代码 导入一些依赖库 from flask import Flask render template
  • 视频播放设计测试用例

    功能测试 1 视频资源能否正常获取 无论从服务器后台或者客户端添加 播放是否正常 2 存在多个视频时 能否上下滑动 无论看完未看完 3 如果一个视频涉及另外一个 切换到相应视频能否正常播放 4 视频音量测试 在无声音播放是否 正常声音是否正

随机推荐

  • vue项目 代码中代理 部署后nginx请求代理

    vue项目 代码中代理 vue2 0 文件地址 config index js module exports dev Paths assetsSubDirectory static assetsPublicPath proxyTable 设
  • 金融时间序列分析:Python基于garch模型预测上证指数波动率、计算var和var穿透率、双尾检验

    目录 一 收益率波动效应的分析 1 1 收益率序列平稳性检验 1 2 建立AR p 模型 1 3 Ljung Box混成检验残差序列的相关性 判断是否有ARCH效应 1 4 建立ARCH模型 二 GARCH模型与波动率预测 2 1 建立GA
  • 后端Springboot框架搭建APi接口开发(第一章)

    本文章以IDEA为开发工具 使用SSM框架进行项目编写 第一节 设计并创建数据库 我们用一个简单的用户表进行操作演示 首先创建Data数据库 create database data 创建User数据表 表中包含用户邮箱 用户姓名 用户密码
  • C++ placement new使用

    placement new重载来原来的operator new 且placement new不能被即需重载 placement new是在原有的一块地址上继续创建一个对象 注意对象类型要一致 这样的操作的优势有两个 1 不用花时间在找合适的
  • Anchor DETR

    Anchor DETR Query Design for Transformer Based Detector 2021 9 1 DETR的object query是学习的 没有物理意义也不能解释每个query注意哪 作者认为学习出来的ob
  • 用十条命令在一分钟内检查Linux服务器性能[转]

    概述 通过执行以下命令 可以在1分钟内对系统资源使用情况有个大致的了解 uptime dmesg tail vmstat 1 mpstat P ALL 1 pidstat 1 iostat xz 1 free m sar n DEV 1 s
  • 关于C++中的随机数生成器

    关于C 中的随机数生成器 今天需要生成随机序列来测试代码 记录一下C 中随机数生成器的使用方法 C 中使用random库生成随机数 主要使用两个类 随机数引擎类 调用这个类会生成一个调用运算符 该运算符不接受任何参数 并返回一个随机的uns
  • 你知道两台Linux之间如何传输文件吗?

    你知道两台Linux之间如何传输文件吗 不同的Linux主机之间想要实现文件相互拷贝的方法有三种 第一种 ftp 也就是其中一台Linux安装ftpServer 这样可以另外一台使用ftp的client程序来进行文件的copy 第二种 采用
  • 转载:Java学习路线(完整详细版)超详细

    Java学习路线 第一阶段 Java基础 一 介绍 二 Java开发介绍 三 Java数组 四 Java面向对象 五 异常 六 集合 七 IO流 八 多线程 第二阶段 JavaWeb 一 介绍 二 HTML5 三 CSS3 四 JavaSc
  • 用Java写数据到POST请求

    用Java写数据到POST请求 HTTP POST请求最常见的用途是发送表单参数到服务器 除了发送表单数据 还可以使用POST的消息Body体发送各种数据 如纯文本 XML文档等 本文讲述如何用Java将数据写入POST请求的Body体 j
  • Filter的应用--权限过滤

    因为项目比较长 需要一步步进行实现 所以分解成一个一个需求 一 需求一 1 需求一 可以看某人的权限 同时 可以对这个用户进行权限的修改 2 程序实现 3 程序目录 4 User java 1 package com web 2 3 imp
  • 2023 Google 开发者大会,共创、赋能开发者

    前言 9月6日 2023 Google 开发者大会在上海拉开帷幕 在本次大会 Google 将技术灵感带到了中国 在为期两天的大会中 让我印象最为深刻的是 谷歌帮助中国开发者释放潜能 持续创新 落地创意灵感 不管你是 Mobile 开发者
  • 算子策略如何配,调试宝典帮你忙

    前面几期讲解了网络构建与训练类报错中各类错误的定位解决方法 相信大家应该对于此类问题有一些较为深入的认识了 在深度学习中 当数据集和参数量的规模越来越大 训练所需的时间和硬件资源会随之增加 最后会变成制约训练的瓶颈 分布式并行训练 可以降低
  • JSVC简介之快速入门

    1 JSVC简介 Apache基金会会common 类似于guava 项目下的项目 2 为什么要使用JSVC java应用增加一种启动方式 Java的缺点 只能用main方法启动 应用能使用1024以下端口 为啥tomcat可以指定端口 系
  • python中多线程编程中eoferror_面试官:请你讲讲Python多线程多进程编程

    Python多线程多进程文章目录并行和并发的概念 线程和进程的概念 来点八股文 PythonGIL锁相关以及历史 多线程编程详解 多进程编程详解 重点 一 什么是并行和并发 首先我们来先说一下一个简单的共同点 并行和并发都是完成多任务更加有
  • python QMessageBox设置标签和按钮居中、中文按钮

    from PyQt5 QtCore import Qt from PyQt5 QtWidgets import QApplication QMessageBox QLabel QDialogButtonBox from PyQt5 QtGu
  • IDEA报错 Cannot resolve method ‘xxx‘ in ‘xxx‘

    今天在用Logback做一个小项目的时候 出现了这个bug 一下子给我报了50个错误 如下图所示 后面经过10分钟左右的排查 在网上搜寻解决方式 网上的解决方案差不多有以下三种 1 重装Logback 2 清除IDE缓存 3 重新导包导库
  • 写入位置时发生访问冲突

    写入位置时发生访问冲突是因为待写入的内存空间不能被写入 可能的情况 给野指针赋值 通常在调试的时候 如果一个指针指向的地址为0x00000000那么表示这个指针不指向任何地址 参考文章 1 2
  • Lesson40 FIFO的配置与使用

    摄像头的FIFO配置使用 一 FIFO的基本工作原理讲解 二 Vivado中FIFO IP的添加和基本配置 三 IP文档资料的获取方法 四 编写测试脚本 1 复制 FIFO 的例化模板 2 新建存放FIFO仿真文件的文件夹 3 全部的仿真代
  • 用opencv简单的检测三角形、正方形、圆以及它们的颜色

    源码下载地址点击打开链接 原始图片 检测结果 检测后图片 下面为完整代码 include