双线程检测绿色和红色物体(opencv)

来源:互联网 发布:锥螺纹怎么编程 编辑:程序博客网 时间:2024/06/06 01:52

声明:参加机器人省赛时弄的代码,我是初学者,运行过。(Ubuntu14.04系统,opencv2.4.9)

<span style="font-size:14px;">#ifndef HSV_INIT_H#define HSV_INIT_H#include <opencv2/opencv.hpp>#include<iostream>#include<restul_hsv.h>using namespace cv;using namespace std;class Hsv_init{private:Restul_hsv s1;     public:    Restul_hsv  hsv_init(int H_H,int  H_L,int  S_L,int S_H,int V_L,int V_H,Mat img)     {     Mat dstImage;     Mat xianshi;       Mat imgOriginal;       float centre_y;        int num=0;        int sum=0;        float with;        float heigh;        int iLowH = H_H;        int iHighH=H_L;       int  iLowS=S_L;        int iHighS=S_H;       int  iLowV=V_L;        int iHighV=V_H;        int max=1000;        //bool  flag_hsv=true;        imgOriginal = img;       // return img;          Mat imgHSV;          vector<Mat> hsvSplit;GaussianBlur(imgOriginal,imgOriginal,Size(15,15),0,0);Mat element3 = getStructuringElement(MORPH_RECT, Size(15, 15 ));morphologyEx(imgOriginal, imgOriginal, MORPH_OPEN, element3);//Mat element4= getStructuringElement(MORPH_RECT, Size(15,15));//闭操作 (连接一些连通域)//morphologyEx(imgOriginal, imgOriginal, MORPH_CLOSE, element4);          cvtColor(imgOriginal, imgHSV, COLOR_BGR2HSV); //Convert the captured frame from BGR to HSV          //   因为我们读取的是彩色图,直方图均衡化需要在HSV空间做          split(imgHSV, hsvSplit);          equalizeHist(hsvSplit[2],hsvSplit[2]);          merge(hsvSplit,imgHSV);          Mat imgThresholded;          inRange(imgHSV, Scalar(iLowH, iLowS, iLowV), Scalar(iHighH, iHighS, iHighV), imgThresholded); //检查数组元素是否在两个数量之间          //开操作 (去除一些噪点)          Mat element = getStructuringElement(MORPH_RECT, Size(3, 3));          morphologyEx(imgThresholded, imgThresholded, MORPH_OPEN, element);          Mat element2= getStructuringElement(MORPH_RECT, Size(30,30));          //闭操作 (连接一些连通域)          morphologyEx(imgThresholded, imgThresholded, MORPH_CLOSE, element2);          //初始化结果图          dstImage=Mat::zeros(imgThresholded.rows,imgThresholded.cols,CV_8UC3);          vector<vector<Point> > contours;          vector<Vec4i>hierarchy;          vector<Point>contour;                //寻找轮廓               findContours(imgThresholded, contours,hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);               int m=contours.size();//得到轮廓的数量               int n=0;                //判断是否检测到轮廓,是则处理,不是则输出原图               for(int i=0;i<m;++i)               {                   n=contours[i].size();                   for(int j=0;j<n;++j)                   {                       contour.push_back(contours[i][j]);  //读取每个轮廓的点           // cout<<"ok:"<<m<<endl;                   }                   double area = contourArea(contour); //取得轮廓面积                   if (area>1000)//只画出轮廓大于1000的框,可以修改,640*480=307200                   {                  Scalar color( (rand()&255), (rand()&255), (rand()&255) );                   drawContours(dstImage,contours,i,color,1,8, hierarchy );                    }                            int data1,data2;                           vector<Point> points = contours[i];                           //对给定的2D点集,寻找最小面积的包围矩形                           RotatedRect box = minAreaRect(points);                           Point2f vertex[4];                           box.points(vertex);                           xianshi=dstImage;                        with=vertex[2].x- vertex[1].x;                        heigh=vertex[0].y-vertex[1].y;                        centre_y=(vertex[0].y+vertex[1].y)/2;                        sum=heigh/with;                        //处理像素                        uchar*data=img.ptr<uchar>((vertex[0].y+vertex[1].y)/2);//去中间行的首地址                        data2=(vertex[2].x+vertex[1].x)/2;//中心列                        data1=data[data2]/4*4;                        //cout<<"data1:"<<data1<<endl;                        int cPointR,cPointG,cPointB;//currentPoint;                         cPointB=img.at<Vec3b>(((vertex[0].y+vertex[1].y)/2),(vertex[2].x+vertex[1].x)/2)[0];                         cPointG=img.at<Vec3b>(((vertex[0].y+vertex[1].y)/2),(vertex[2].x+vertex[1].x)/2)[1];                         cPointR=img.at<Vec3b>(((vertex[0].y+vertex[1].y)/2),(vertex[2].x+vertex[1].x)/2)[2];                         //cout<<"cPointB:"<<cPointB<<"cPointG:"<<cPointG<<"cPointR:"<<cPointR<<endl;                        if(with*heigh>1000)                        {                           if((0.45<heigh/with&&heigh/with<0.8)||(1.0<heigh/with&&heigh/with<1.4))                           {                               if((cPointR>35&&cPointR<65)||(cPointG>20&&cPointG<40))                            {                                    //flag_hsv = true;                                    if(max>centre_y)                                    {                                        max = centre_y;                                    }                                                   //s1.heigh=heigh;                                                   //s1.with=with;                               line(dstImage, vertex[0], vertex[1], Scalar(100, 200, 211), 6, CV_AA);                               line(dstImage, vertex[1], vertex[2], Scalar(100, 200, 211), 6, CV_AA);                               line(dstImage, vertex[2], vertex[3], Scalar(100, 200, 211), 6, CV_AA);                               line(dstImage, vertex[3], vertex[0], Scalar(100, 200, 211), 6, CV_AA);                               circle(dstImage,cvPoint(vertex[0].x,vertex[0].y),20,Scalar(255,0,0));                               if(0.45<heigh/with&&heigh/with<0.8){                                   num++;                               }                               else                               {                                   num=num+2;                               }                               //cvCircle(dstImage,cvPoint(vertex[0].x,vertex[0].y),2,CV_RGB(0,255,255),3,8,0);//画圆  cvPoint:确定圆的坐标  200:圆的半径 CV_RGB:圆的颜色 3:线圈的粗细                           //绘制出最小面积的包围矩形                          // cout<<"zuoboao"<<(vertex[0].y-vertex[1].y)/(vertex[2].x- vertex[1].x)<<endl;                            //cout<<"data1:"<<data1<<endl;                            //cout<<"cPointB:"<<cPointB<<"cPointG:"<<cPointG<<"cPointR:"<<cPointR<<endl;                            //cout<<"vertex[0]:("<<vertex[0].x<<vertex[0].y<<")"<<"vertex[1]:("<<vertex[1].x<<vertex[1].y<<")"<<"vertex[2]:("<<vertex[2].x<<vertex[2].y<<")"<<"vertex[3]:("<<vertex[3].x<<vertex[3].y<<")"<<endl;                               }                           }                        }                           }               /*if(flag_hsv){                       s1.y=-1;                       s1.img=dstImage;                       return s1;               }             else{*/               s1.img=dstImage;               s1.ga_img = imgOriginal;               s1.num=num;                s1.y=max;                return s1;//返回图片给img            //   }                }};#endif // HSV_INIT_H</span>
1.这是一个类文件,用来处理图片,框图

<span style="font-size:14px;">#ifndef RESTUL_HSV_H#define RESTUL_HSV_H#include <opencv2/opencv.hpp>#include<iostream>using namespace   cv;class Restul_hsv{public:Mat img;int y;int flag=1;//int heigh;Mat ga_img;//int with;int num;};#endif // RESTUL_HSV_H</span>
1.由于上面那个程序要传回图片和数据,所以就特地建了个类来声明变量。
<span style="font-size:14px;">/**  ******************************************************************************  * @file    uart.hpp  * @author  XXK  * @version V1.0  * @date    2015-11-18  * @brief   head fire(uart.hpp)  ******************************************************************************  * @attention  *  * 实验平台:ubuntu 15.10  *  ******************************************************************************  */#ifndef __UART_H#define __UART_H/*接口定义(8/N1)*/#define SERIAL_PORT         "/dev/ttyUSB0"  //串口地址#define PORT_SPEED          115200          //串口波特率#define DATABITS            8               //数据位#define STOPBITS            1               //停止位#define PARITY              'n'             //校验方式 (不校验)/*用户函数*/int setSpeed(int, int);            //设置波特率int setParity(int, int, int, int); //设置奇偶校验int openPort(void);                                //打开端口int init(void);    //初始化串口int readPort(int);   //读取串口数据/*void writePort(int, double, double);*/   //向串口写入数据void writePort(int fd, float center_f, float center_g, float center_r);void uart_test(float, float,int fd);   //串口测试#endif</span>
1.由于要传递数据给迷你PC,配置串口数据,如果不传数据给迷你PC,这个可以不要。(类文件)


<span style="font-size:14px;">/**  ******************************************************************************  * @file    uart.cpp  * @author  XXK  * @version V1.0  * @date    2015-11-18  * @brief   ubuntu下与rs232设备的串口通信  ******************************************************************************  * @attention  *  * 实验平台:ubuntu 15.10  *  ******************************************************************************  *//*Includes*/#include     <stdio.h>#include     <stdlib.h>#include     <unistd.h>#include     <sys/types.h>#include     <sys/stat.h>#include     <fcntl.h>#include     <termios.h>#include     <errno.h>#include     <string.h>#include     <cstring>#include     <usart1.h>#include        <iostream>using namespace std;#define unsigned char u_charint speed_arr[] = {B38400, B19200, B9600, B4800, B2400, B1200, B300,B38400, B19200, B9600, B4800, B2400, B1200, B300,};int name_arr[] = {38400,  19200,  9600,  4800,  2400,  1200,  300, 38400,  19200,  9600, 4800, 2400, 1200,  300,};struct termios opt; //定义termios结构/******************实现函数****************** *函数名称: uart_test *函数功能: 串口通信测试 *函数参数: 无 *备注    : 注意动态内存 ********************************************/void uart_test(float center_i, float center_j, int fd) {    //printf("%f,%f\n", center_i, center_j);    //char write_buf[256];//    printf("configure complete\n");//    printf("start send and receive data...\n");//    while(1)//    {//        readPort(fd);//        writePort(fd, center_i, center_j);//       readPort(fd);//       sleep(1);//    } unsigned char}/******************实现函数****************** *函数名称: readPort *函数功能: 从串口设备读取数据 *函数参数: int fd *备注    : 无 ********************************************/int readPort(int fd)    {        int n;        char this_buf[256];        int read_flag = 1;            bzero(this_buf, sizeof(this_buf));            while((n = read(fd, this_buf, sizeof(this_buf))) > 0)            {                 printf("receive successful !!!!\n");                printf("Len %d \n", n);                //read_buf[n + 1] = '\0';                printf("receive_str=%s\n", this_buf);                read_flag = 0;            }        if(read_flag != 1)        {            return 1;        }        else            return 0;    }/******************实现函数****************** *函数名称: writePort *函数功能: 向串口设备写入数据 *函数参数: int fd *备注    : 无 ********************************************///void writePort(int fd, double center_m, double center_n)void writePort(int fd, float center_f, float center_g, float center_r)    {//        write(fd,"xxk",3);         char cArray1[1] = {0};         char cArray2[1] = {0};         char cArray3[1] = {0};        int i, m, n;        int len;        //形式2,参数2指定字符串数组的大小,小数点后保留10位        gcvt(center_f,  1, cArray1);        gcvt(center_g, 1, cArray2);        gcvt(center_r,  1, cArray3);        char write_buf[10] = {"a"};        for( i = 0; i < 1; i++ )        {            if( cArray1[i] != 0)            write_buf[i] = cArray1[i];            else                break;        }        for( m = 0; m < 2; m++ )        {            if(m == 0)            write_buf[i] = ',';            else            write_buf[++i] = cArray2[m-1];        }        for( n = 0; n < 2; n++ )        {            if(n == 0)            write_buf[++i] = ',';            else            write_buf[++i] = cArray3[n-1];        }        char * send_buf;        //strncpy(write_buf, cArray1, strlen(cArray1));        len = strlen(write_buf);        send_buf = write_buf;       //printf("nResult1=%s, nResult2=%s, nResult=%s\n",cArray1, cArray2, write_buf);        write( fd, send_buf, len );        write( fd, "\n", 1 );    }/************************************ *init *  初始化串口 *@global *  PORT_SPEED  串口波特率 *  DATABITS    数据位 *  STOPBITS    停止位 *  PARITY      校验方式 *@return *  -1:设置失败  !(-1):设置成功 ************************************/int init(void) {     int fd;     /*      这个变量被用来提供一个健全的线路设置集合, 如果这个端口在被用户初始化前      使用. 驱动初始化这个变量使用一个标准的数值集      struct termios{        unsigned short c_iflag; //输入模式标志        unsigned short c_oflag;     //输出模式标志        unsigned short c_cflag;     //控制模式标志        unsigned short c_lflag;     //区域模式标志或本地模式标志或局部模式        unsigned char c_line;   //行控制line discipline        unsigned char c_cc[NCC];    // 控制字符特性     };        */      //打开串口      fd = openPort();      //设置波特率      if(setSpeed(fd, PORT_SPEED) == 1)      {        printf("setSpeed failed!\n");        exit(1);      }      //设置数据位、停止位和校验位      if(setParity(fd, DATABITS, STOPBITS, PARITY) == 1)      {        printf("setParity failed!\n");        exit(1);      }    if(tcsetattr(fd, TCSANOW, &opt) != 0) //TCSANOW:不等数据传输完毕就立即改变属性。        {               //TCSADRAIN:等待所有数据传输结束才改变属性。          perror("serial error");          return -1;      }      return fd;    } /************************************ *openPort *  打开串口 *@global *  SERIAL_PORT 串口地址 *@return *  -1:设置失败  !(-1):设置成功 ************************************/    int openPort()    {        int fd;        //O_RDWR:   可读写方式打开;        //O_NOCTTY: 若打开的文件为终端机设备时则不会把该终端机当做进程控制终端机        //O_NDELAY: 以不可阻断的方式打开文件        fd = open(SERIAL_PORT, O_RDWR | O_NOCTTY |O_NDELAY);        if(fd == -1)        {            perror("open serial failed!\n");            exit(1);        }        return fd;    }/************************************ *setSpeed *  设置波特率(38400, 19200, 9600, 4800, 2400, 1200, 300) *@para *  fd              文件标识。 *  speed               波特率。 *  struct termios          termios结构变量 *@return *  1:设置失败  0:设置成功 ************************************/    int setSpeed(int fd, int speed)    {        int i;        if(tcgetattr(fd, &opt) != 0) //获取与终端相关的参数;    #并将获得信息保存在 opt 变量中        if(tcgetattr(STDIN_FILENO, &opt) != 0)        {            perror("tcgetattr fd\n");            return 1;        }        //识别波特率,设置输入输出波特率        for(i = 0; i < sizeof(speed_arr) / sizeof(int); i++)        {            if(speed == name_arr[i])            {                tcflush(fd, TCIOFLUSH);                //设置波特率                cfsetispeed(&opt, speed_arr[i]);                cfsetospeed(&opt, speed_arr[i]);                //设置数据接收方式                if(tcsetattr(fd, TCSANOW, &opt) != 0)                {                    perror("tcsetattr fd");                    return 1;                }                tcflush(fd, TCIOFLUSH);            }        }        return 0;    }/************************************ *setParity *  设置数据位、停止位和校验位 *@para *  fd              文件标识。 *  databits            数据位。(8 | 7) *  stopbits            停止位。(1 | 2) *  parity              校验位。(n:无校验位 | o:奇校验 | e:偶校验 | s:空格) *@return *  1:设置失败  0:设置成功 ************************************/    int setParity(int fd, int databits, int stopbits, int parity)    {        if(tcgetattr(fd, &opt) != 0)        {            perror("tcgetattr fd");            return 1;        }            opt.c_cflag |= (CLOCAL | CREAD);        //CLOCAL:忽略 modem 控制线。                                //CREAD:    打开接受者。        switch(databits)                    //设置数据位数        {            case 7:                opt.c_cflag &= ~CSIZE;          //屏蔽字符大小位                opt.c_cflag |= CS7;         //选择7位数据位                break;            case 8:                opt.c_cflag &= ~CSIZE;                opt.c_cflag |= CS8;                break;            default:                fprintf(stderr, "Unsupported data size.\n");                return 1;        }        switch(parity)            //设置校验位        {        case 'n':            opt.c_cflag &= ~PARENB;                 //清除校验位            opt.c_iflag &= ~INPCK;                  //启用输入奇偶检测。            break;        case 'o':            opt.c_cflag |= PARENB;                  //使能校验位            opt.c_cflag |= PARODD;                  //奇校验            opt.c_iflag |= INPCK;                   //启用输入奇偶检测。            break;        case 'e':            opt.c_cflag |= PARENB;                  //使能校验位            opt.c_cflag &= ~PARODD;                 //偶校验            opt.c_iflag |= INPCK;                   //启用输入奇偶检测。            break;        case 's':            opt.c_cflag &= ~PARENB;                 //清除校验位            opt.c_cflag &= ~CSTOPB;                 //设置一个停止位           opt.c_iflag |= INPCK;                   //启用输入奇偶检测。            break;        default:            fprintf(stderr, "Unsupported parity.\n");            return 1;        }        switch(stopbits)        //设置停止位        {        case 1:            opt.c_cflag &= ~CSTOPB;            break;        case 2:            opt.c_cflag |= CSTOPB;            break;        default:            fprintf(stderr, "Unsupported stopbits.\n");            return 1;        }        opt.c_cflag |= (CLOCAL | CREAD);        opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);        opt.c_oflag &= ~OPOST;      //OPOST :启用具体实现自行定义的输出处理。        opt.c_oflag &= ~(ONLCR | OCRNL);    //OCRNL :将输出中的回车映射为新行符                            //ONLCR :(XSI) 将输出中的新行符映射为回车-换行。        opt.c_iflag &= ~(ICRNL | INLCR);        //ICRNL :将输入中的回车翻译为新行 (除非设置了 IGNCR)(否则当输入信号有 CR 时不会终止输入)。                                //INLCR :将输入中的 NL 翻译为 CR。(将收到的换行符号转换为Return)        opt.c_iflag &= ~(IXON | IXOFF | IXANY); //IXON  :启用输出的 XON/XOFF 流控制。                                //IXOFF :启用输入的 XON/XOFF 流控制。                            //IXANY :(不属于 POSIX.1;XSI) 允许任何字符来重新开始输出。        tcflush(fd, TCIFLUSH);          //清空输入缓存        //MIN = 0 , TIME =0; 有READ立即回传否则传回 0,不读取任何字元        opt.c_cc[VTIME] = 0;        opt.c_cc[VMIN] = 0;        if(tcsetattr(fd, TCSANOW, &opt) != 0)   //设置数据接收方式        {            perror("tcsetattr fd");            return 1;        }        return 0;    }</span>
1.这个是我队友写的串口配置代码。(.cpp文件)
<span style="font-size:14px;">#include <iostream>#include<sys/wait.h>#include<sys/mman.h>#include<pthread.h>//多线程相关操作头文件,可移植众多平台#include <opencv2/opencv.hpp>#include"stdio.h"#include     <unistd.h>#include<hsv_init.h>#include<restul_hsv.h>#include <usart1.h>using namespace std;using namespace cv;Mat imgOriginal;Mat img;Mat img1;int iLowH_1 = 51;int iHighH_1 = 72;int iLowS_1 = 110;int iHighS_1 = 255;int iLowV_1 = 0;int iHighV_1 = 255;int num_green;int num_red;int fd;int a;int flag = 2 ;int read_return = 0;bool read_flag=true;Hsv_init hsv2, hsv;Restul_hsv R_h1,R_h2;void *thread_1(void *ptr){    R_h1= hsv.hsv_init(iLowH_1,iHighH_1,iLowS_1,iHighS_1,iLowV_1,iHighV_1,imgOriginal);    //printf("%d\n", read_return);}void *thread_2(void *ptr){    while (true) {    cout<<"port ok"<<endl;    while( read_return==0){         read_return = readPort(fd);    }    printf("OK !!!\n");     writePort(fd, flag, R_h1.num, R_h2.num);    read_return=0;    }}int main(){    fd = init();//初始化端口设置    pthread_t id_2;    int ret_2 = pthread_create(&id_2, NULL, thread_2, NULL);//参数:创建的线程id,线程参数,线程运行函数的起始地址,运行函数的参数    if(ret_2)    {        cout << "Create pthread error!" << endl;        return 1;    }    VideoCapture capture;    capture.open(1);    //Init camera    if (!capture.isOpened())    {        cout << "capture device failed to open!" << endl;        return -1;    }    int iLowH = 165;    int iHighH = 179;    int iLowS = 179;    int iHighS = 255;    int iLowV = 134;    int iHighV = 255;cvNamedWindow("Control", CV_WINDOW_AUTOSIZE);cvNamedWindow("Control_1", CV_WINDOW_AUTOSIZE);//Create trackbars in "Control" windowcvCreateTrackbar("LowH", "Control", &iLowH, 179); //Hue (0 - 179)cvCreateTrackbar("HighH", "Control", &iHighH, 179);cvCreateTrackbar("LowS", "Control", &iLowS, 255); //Saturation (0 - 255)cvCreateTrackbar("HighS", "Control", &iHighS, 255);cvCreateTrackbar("LowV", "Control", &iLowV, 255); //Value (0 - 255)cvCreateTrackbar("HighV", "Control", &iHighV, 255);cvCreateTrackbar("LowH", "Control_1", &iLowH_1, 179); //Hue (0 - 179)cvCreateTrackbar("HighH", "Control_1", &iHighH_1, 179);cvCreateTrackbar("LowS", "Control_1", &iLowS_1, 255); //Saturation (0 - 255)cvCreateTrackbar("HighS", "Control_1", &iHighS_1, 255);cvCreateTrackbar("LowV", "Control_1", &iLowV_1, 255); //Value (0 - 255)cvCreateTrackbar("HighV", "Control_1", &iHighV_1, 255);    while(true)    {        capture>>imgOriginal; // read a new frame from video//        printf("%d\n", read_return);        pthread_t id_1;        int ret_1 = pthread_create(&id_1, NULL, thread_1, NULL);//参数:创建的线程id,线程参数,线程运行函数的起始地址,运行函数的参数        if(ret_1)        {            cout << "Create pthread error!" << endl;            return 1;        }//        pthread_t id_2;//        int ret_2 = pthread_create(&id_2, NULL, thread_2, NULL);//参数:创建的线程id,线程参数,线程运行函数的起始地址,运行函数的参数//        if(ret_2)//        {//            cout << "Create pthread error!" << endl;//            return 1;//        }        //img1= hsv_init_1(iLowH_1,iHighH_1,iLowS_1,iHighS_1,iLowV_1,iHighV_1,imgOriginal);        R_h2 = hsv2.hsv_init(iLowH,iHighH,iLowS,iHighS,iLowV,iHighV,imgOriginal);        pthread_join(id_1, NULL);//        pthread_join(id_2, NULL);           cout<<"red:"<<R_h2.num<<" and "<<"green"<<R_h1.num<<endl;//        cout<<"red_centre_y:"<<R_h2.y<<" and "<<"green_centre_y:"<<R_h1.y<<endl;        if(R_h2.y<R_h1.y){            flag=0;                cout<<"flag:"<<flag<<endl;             }            else if(R_h2.y>R_h1.y){                flag=1;                  cout<<"falg:"<<flag<<endl;            }        else{            flag=2;            cout<<"falg:"<<flag<<endl;        }        //num_green = R_h1.num;        //num_red = R_h2.num;        imshow("green", R_h1.img); //show the dstImage image        imshow("Original", R_h2.ga_img); //show the original image        imshow("red", R_h2.img); //show the dstImage image        waitKey(1);    }       pthread_join(id_2, NULL);        close(fd);        return 0;}</span>
1.这里有三个线程,一个用来传输数据给迷你PC,包括红绿物体的个数,哪个颜色在最上面,都用数字代替,其他两个线程都是调用最前面那个类,处理图片。

最后的测试结果还是可以的,可以调HSV,物体长宽比,面积,像素值,RGB值来增加识别率。

特别注意:需要在Makefile文件里添加-lpthread。

0 0
原创粉丝点击