zoukankan      html  css  js  c++  java
  • 利用opencv绘制 灰度直方图 RGB直方图 HSV直方图 直方图均衡化

    灰度直方图介绍:

    http://hi.baidu.com/wen_sift/blog/item/83fd56ca3e6b1b36b600c887.html

    灰度直方图均衡化:

    http://hi.baidu.com/wen_sift/blog/item/b808fd0d9f67392b6b60fb54.html

    利用OpenCV计算并绘制灰度直方图  :

    View Code
    #include <cv.h>
    #include <highgui.h>
    #pragma comment( lib, "cv.lib" )
    #pragma comment( lib, "cxcore.lib" )
    #pragma comment( lib, "highgui.lib" )
    int main()
    {
             IplImage* src=cvLoadImage("lena.jpg",0);
             int width=src->width;
             int height=src->height;
             int step=src->widthStep;
             uchar* data=(uchar *)src->imageData;
             int hist[256]={0};
             for(int i=0;i<height;i++)
             {
                       for(int j=0;j<width;j++)
                       {
                                hist[data[i*step+j]]++;
                       }
             }
             int max=0;
             for(i=0;i<256;i++)
             {
                       if(hist[i]>max)
                       {
                                max=hist[i];
                       }
             }
             IplImage* dst=cvCreateImage(cvSize(400,300),8,3);
             cvSet(dst,cvScalarAll(255),0);
             double bin_width=(double)dst->width/256;
             double bin_unith=(double)dst->height/max;
             for(i=0;i<256;i++)
             {
                       CvPoint p0=cvPoint(i*bin_width,dst->height);
                       CvPoint p1=cvPoint((i+1)*bin_width,dst->height-hist[i]*bin_unith);
                       cvRectangle(dst,p0,p1,cvScalar(0,255),-1,8,0);
             }
             cvNamedWindow("src",1);
             cvShowImage("src",src);
             cvNamedWindow("dst",1);
             cvShowImage("dst",dst);
             cvWaitKey(0);
             cvDestroyAllWindows();
             cvReleaseImage(&src);
             cvReleaseImage(&dst);
             return 0;
    }
    利用opencv现有函数:
    View Code
    #include <cv.h>
    #include <highgui.h>
    #pragma comment( lib, "cv.lib" )
    #pragma comment( lib, "cxcore.lib" )
    #pragma comment( lib, "highgui.lib" )
    int main()
    {
             IplImage* src=cvLoadImage("lena.jpg",0);
             int size=256;
             float range[]={0,255};
             float* ranges[]={range};
             CvHistogram* hist=cvCreateHist(1,&size,CV_HIST_ARRAY,ranges,1);
             cvCalcHist(&src,hist,0,NULL);
             float max=0;
             cvGetMinMaxHistValue(hist,NULL,&max,NULL,NULL);
             IplImage* dst=cvCreateImage(cvSize(400,300),8,3);
             cvSet(dst,cvScalarAll(255),0);
             double bin_width=(double)dst->width/size;
             double bin_unith=(double)dst->height/max;
             for(int i=0;i<size;i++)
             {
                       CvPoint p0=cvPoint(i*bin_width,dst->height);
                       CvPoint p1=cvPoint((i+1)*bin_width,dst->height-cvGetReal1D(hist->bins,i)*bin_unith);
                       cvRectangle(dst,p0,p1,cvScalar(0,255),-1,8,0);
             }
             cvNamedWindow("src",1);
             cvShowImage("src",src);
             cvNamedWindow("dst",1);
             cvShowImage("dst",dst);
             cvWaitKey(0);
             cvDestroyAllWindows();
             cvReleaseImage(&src);
             cvReleaseImage(&dst);
             return 0;
    }

    通过复用上面的代码。可以得到彩色图像各通道的直方图,RGB直方图代码如下:

    View Code
    #include <cv.h>
    
    #include <highgui.h>
    
    #pragma comment( lib, "cv.lib" )
    
    #pragma comment( lib, "cxcore.lib" )
    
    #pragma comment( lib, "highgui.lib" )
    
    int main()
    
    {
    
             IplImage* src=cvLoadImage("lena.jpg",1);
    
             IplImage* r=cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1); 
    
             IplImage* g=cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1); 
    
             IplImage* b=cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1); 
    
             cvSplit(src,b,g,r,NULL); 
    
             IplImage* gray = cvCreateImage(cvGetSize(src),8,1);
    
             cvCvtColor(src,gray,CV_BGR2GRAY);
    
             int size=256;
    
             float range[]={0,255};
    
             float* ranges[]={range};
    
             
    
             CvHistogram* r_hist = cvCreateHist(1,&size,CV_HIST_ARRAY,ranges,1);
    
             CvHistogram* g_hist = cvCreateHist(1,&size,CV_HIST_ARRAY,ranges,1);
    
             CvHistogram* b_hist = cvCreateHist(1,&size,CV_HIST_ARRAY,ranges,1);
    
             CvHistogram* hist   = cvCreateHist(1,&size,CV_HIST_ARRAY,ranges,1);
    
             cvCalcHist(&r,r_hist,0,NULL);
    
             IplImage* r_dst=cvCreateImage(cvSize(400,300),8,3);
    
             cvSet(r_dst,cvScalarAll(255),0);
    
             float r_max=0;
    
             cvGetMinMaxHistValue(r_hist,NULL,&r_max,NULL,NULL);
    
             double r_bin_width=(double)r_dst->width/size;
    
             double r_bin_unith=(double)r_dst->height/r_max;
    
             for(int i=0;i<size;i++)
    
             {
    
                       CvPoint p0=cvPoint(i*r_bin_width,r_dst->height);
    
                       CvPoint p1=cvPoint((i+1)*r_bin_width,r_dst->height-cvGetReal1D(r_hist->bins,i)*r_bin_unith);
    
                       cvRectangle(r_dst,p0,p1,cvScalar(255,0,0),-1,8,0);
    
             }
    
             cvCalcHist(&g,g_hist,0,NULL);
    
             IplImage* g_dst=cvCreateImage(cvSize(400,300),8,3);
    
             cvSet(g_dst,cvScalarAll(255),0);
    
             float g_max=0;
    
             cvGetMinMaxHistValue(g_hist,NULL,&g_max,NULL,NULL);
    
             double g_bin_width=(double)g_dst->width/size;
    
             double g_bin_unith=(double)g_dst->height/g_max;
    
             for(i=0;i<size;i++)
    
             {
    
                       CvPoint p0=cvPoint(i*g_bin_width,g_dst->height);
    
                       CvPoint p1=cvPoint((i+1)*g_bin_width,g_dst->height-cvGetReal1D(g_hist->bins,i)*g_bin_unith);
    
                       cvRectangle(g_dst,p0,p1,cvScalar(0,255,0),-1,8,0);
    
             }
    
             cvCalcHist(&b,b_hist,0,NULL);
    
             IplImage* b_dst=cvCreateImage(cvSize(400,300),8,3);
    
             cvSet(b_dst,cvScalarAll(255),0);
    
             float b_max=0;
    
             cvGetMinMaxHistValue(b_hist,NULL,&b_max,NULL,NULL);
    
             double b_bin_width=(double)b_dst->width/size;
    
             double b_bin_unith=(double)b_dst->height/b_max;
    
             for(i=0;i<size;i++)
    
             {
    
                       CvPoint p0=cvPoint(i*b_bin_width,b_dst->height);
    
                       CvPoint p1=cvPoint((i+1)*b_bin_width,b_dst->height-cvGetReal1D(b_hist->bins,i)*b_bin_unith);
    
                       cvRectangle(b_dst,p0,p1,cvScalar(0,0,255),-1,8,0);
    
             }
    
             cvCalcHist(&gray,hist,0,NULL);
    
             IplImage* gray_dst=cvCreateImage(cvSize(400,300),8,3);
    
             cvSet(gray_dst,cvScalarAll(255),0);
    
             float max=0;
    
             cvGetMinMaxHistValue(hist,NULL,&max,NULL,NULL);
    
             double bin_width=(double)gray_dst->width/size;
    
             double bin_unith=(double)gray_dst->height/max;
    
             for(i=0;i<size;i++)
    
             {
    
                       CvPoint p0=cvPoint(i*bin_width,gray_dst->height);
    
                       CvPoint p1=cvPoint((i+1)*bin_width,gray_dst->height-cvGetReal1D(hist->bins,i)*bin_unith);
    
                       cvRectangle(gray_dst,p0,p1,cvScalar(0),-1,8,0);
    
             }
    
             IplImage* dst=cvCreateImage(cvSize(800,600),8,3);
    
             cvSetZero(dst);
    
             CvRect rect = cvRect(0, 0, 400, 300); 
    
             cvSetImageROI(dst, rect); 
    
             cvCopy(r_dst, dst); 
    
             rect = cvRect(400, 0, 400, 300);
    
             cvSetImageROI(dst, rect); 
    
             cvCopy(g_dst, dst);
    
             rect = cvRect(0, 300, 400, 300);
    
             cvSetImageROI(dst, rect); 
    
             cvCopy(b_dst, dst);
    
             rect = cvRect(400, 300, 400, 300);
    
             cvSetImageROI(dst, rect); 
    
             cvCopy(gray_dst, dst);
    
             cvResetImageROI(dst);
    
             cvNamedWindow("src",1);
    
             cvShowImage("src",src);
    
             cvNamedWindow("dst",1);
    
             cvShowImage("dst",dst);
    
             cvSaveImage("dst.jpg",dst);
    
             cvWaitKey(0); 
    
             cvDestroyAllWindows();
    
             cvReleaseImage(&src);
    
             cvReleaseImage(&dst);
    
             cvReleaseImage(&r);
    
             cvReleaseImage(&g);
    
             cvReleaseImage(&b);
    
             cvReleaseImage(&gray);
    
             cvReleaseImage(&r_dst);
    
             cvReleaseImage(&g_dst);
    
             cvReleaseImage(&b_dst);
    
             cvReleaseImage(&gray_dst);
    
             return 0;
    
    }

    HSV通道直方图如下:
    View Code
    // Color_2DHistogram.cpp : 定义控制台应用程序的入口点。
    //
    
    #pragma comment(lib, "cv210.lib")
    #pragma comment(lib, "cxcore210.lib")
    #pragma comment(lib, "highgui210.lib")
    #pragma comment(lib, "cvaux210.lib")
    
    #include <cv.h>
    #include <highgui.h>
    #include <iostream>
    using namespace std;
    
    int main( int argc, char** argv )
    {
     IplImage * src;
         if(argc<2)
    {
            printf("Usage: main <image-file-name>\n\7");
            exit(0);
          }
          // 载入图像  
          src=cvLoadImage(argv[1],-1);
          if(!src)
    {
               printf("Could not load image file: %s\n",argv[1]);
            exit(0);
          }
    
        IplImage* hsv = cvCreateImage( cvGetSize(src), 8, 3 );  //第一个为size,第二个为位深度(8为256度),第三个通道数   
        IplImage* h_plane = cvCreateImage( cvGetSize(src), 8, 1 );  
        IplImage* s_plane = cvCreateImage( cvGetSize(src), 8, 1 );  
        IplImage* v_plane = cvCreateImage( cvGetSize(src), 8, 1 );  
        IplImage* planes[] = { h_plane, s_plane,v_plane };  
       
        //** H 分量划分为16个等级,S分量划分为8个等级 */   
        int h_bins =16 , s_bins =8, v_bins = 8;  
        int hist_size[] = {h_bins, s_bins, v_bins};  
       
        //** H 分量的变化范围 */   
        float h_ranges[] = { 0, 180 };   
       
        //** S 分量的变化范围*/   
        float s_ranges[] = { 0, 255 };  
        float v_ranges[] = { 0, 255 };  
      
        float* ranges[] = { h_ranges, s_ranges,v_ranges};  
       
        //** 输入图像转换到HSV颜色空间 */   
        cvCvtColor( src, hsv, CV_BGR2HSV );  
        cvCvtPixToPlane( hsv, h_plane, s_plane, v_plane, 0 );  
       
        //** 创建直方图,二维, 每个维度上均分 */   
        CvHistogram * hist = cvCreateHist( 3, hist_size, CV_HIST_ARRAY, ranges, 1 );  
        //** 根据H,S两个平面数据统计直方图 */   
        cvCalcHist( planes, hist, 0, 0 );  
       
        //** 获取直方图统计的最大值,用于动态显示直方图 */   
        float max_value;  
        cvGetMinMaxHistValue( hist, 0, &max_value, 0, 0 );  
       
       
        //** 设置直方图显示图像 */   
        int height = 100;  
        int width = (h_bins*s_bins*v_bins*5);  
        IplImage* hist_img = cvCreateImage( cvSize(width,height), 8, 3 );  
        cvZero( hist_img );  
       
        //** 用来进行HSV到RGB颜色转换的临时单位图像 */   
        IplImage * hsv_color = cvCreateImage(cvSize(1,1),8,3);  
        IplImage * rgb_color = cvCreateImage(cvSize(1,1),8,3);  
        int bin_w = width / (h_bins * s_bins);  
        for(int h = 0; h < h_bins; h++)  
        {  
            for(int s = 0; s < s_bins; s++)  
            {  
                for(int v = 0; v < v_bins; v++)  
                {  
                int i = h*s_bins + s*v_bins + v;  
                /** 获得直方图中的统计次数,计算显示在图像中的高度 */  
                float bin_val = cvQueryHistValue_3D( hist, h, s,v );  
                int intensity = cvRound(bin_val*height/max_value);  
       
                /** 获得当前直方图代表的颜色,转换成RGB用于绘制 */  
                cvSet2D(hsv_color,0,0,cvScalar(h*180.f / h_bins,s*255.f/s_bins,v*255.f/v_bins,0));  
                cvCvtColor(hsv_color,rgb_color,CV_HSV2BGR);  
                CvScalar color = cvGet2D(rgb_color,0,0);  
       
                cvRectangle( hist_img, cvPoint(i*bin_w,height),  
                    cvPoint((i+1)*bin_w,height - intensity),  
                    color, -1, 8, 0 );  
                }  
            }  
        }  
      
        cvNamedWindow( "Source", 1 );  
        cvShowImage( "Source", src );  
        cvNamedWindow( "H-S-V Histogram",1);  
        cvShowImage( "H-S-V Histogram", hist_img );
        cvWaitKey(0);  
    
        cvReleaseImage(&src);
        cvReleaseImage(&hist_img);
        cvDestroyWindow("Source");
        cvDestroyWindow("H-S-V Histogram");
        return 0;
    }  

    直方图均衡化代码:

    View Code
    // Gray_Hist.cpp : 定义控制台应用程序的入口点。
    //
    #include <iostream>
    #include <stdio.h>
    #include <cv.h>
    #include <highgui.h>
    #include <math.h>
    
    using namespace std;
    using namespace cv;
    
    int main(int argc, char **argv)
    {
        IplImage* src=cvLoadImage(argv[1],CV_LOAD_IMAGE_GRAYSCALE);
        //"C:\\Users\\hellmonky\\Desktop\\LeastSquaresMethod\\Debug\\1.bmp"
        cvNamedWindow("原始图像",1);
        cvShowImage("原始图像",src);
    
        int width=src->width;
        int height=src->height;
        int sum = width*height;
    
        int step=src->widthStep;
        uchar* data=(uchar *)src->imageData;
        int hist[256]={0};
        int CalHist[256] = {0};
        int CH[256] = {0};
        int max1 = 0;
        int max2=0;
    
        //////////////////////////////////////////////////////////////////////////
        //计算输入图像的灰度分布
        //////////////////////////////////////////////////////////////////////////
        for(int i=0;i<height;i++)
        {
            for(int j=0;j<width;j++)
            {
                hist[data[i*step+j]]++;
            }
        }
        for(int i=0;i<256;i++)
        {
            if(hist[i]>max1)
            {
                max1=hist[i];
            }
        }
        for (int i=0;i<256;i++)
        {
            for (int j=0;j<=i;j++)
            {
                CalHist[i] += (int)(255* (float)hist[j]/sum );
            }
        }
        IplImage* dst1=cvCreateImage(cvSize(400,300),8,1);
        cvSet(dst1,cvScalarAll(255),0);
        double bin_width=(double)dst1->width/256;//建立比例因子
        double bin_unith=(double)dst1->height/max1;
        for(int i=0;i<256;i++)
        {
            CvPoint p0=cvPoint(i*bin_width,dst1->height);
            CvPoint p1=cvPoint((i+1)*bin_width,dst1->height-hist[i]*bin_unith);
            cvRectangle(dst1,p0,p1,cvScalar(1),-1,8,0);
        }
    
        //////////////////////////////////////////////////////////////////////////
        //对原始图像进行重新计算
        //////////////////////////////////////////////////////////////////////////
        for(int i=0;i<height;i++)
        {
            for(int j=0;j<width;j++)
            {
                data[i*step+j] = CalHist[data[i*step+j]];
            }
        }
    
        //////////////////////////////////////////////////////////////////////////
        //计算变换以后的图像的灰度分布
        //////////////////////////////////////////////////////////////////////////
        for(int i=0;i<height;i++)
        {
            for(int j=0;j<width;j++)
            {
                CH[data[i*step+j]]++;
            }
        }
        for(int i=0;i<256;i++)
        {  
            if(CH[i]>max2)
            {  
                max2=CH[i];  
            }  
        }
        IplImage* you=cvCreateImage(cvSize(400,300),8,1);
        cvSet(you,cvScalarAll(255),0);
        double binwidth=(double)you->width/256;//建立比例因子
        double binunith=(double)you->height/max2;
        for(int i=0;i<256;i++)
        {
            CvPoint p0=cvPoint(i*binwidth,you->height);
            CvPoint p1=cvPoint((i+1)*binwidth,you->height-CH[i]*binunith);
            cvRectangle(you,p0,p1,cvScalar(1),-1,8,0);
        }
    
        
        cvNamedWindow("原始图像灰度分布",1);
        cvNamedWindow("直方图均衡化图像",1);
        cvNamedWindow("均衡化后直方图",1);
    
        
        cvShowImage("原始图像灰度分布",dst1);
        cvShowImage("直方图均衡化图像",src);
        cvShowImage("均衡化后直方图",you);
    
        waitKey(0);
        cvDestroyWindow("原始图像");
        cvDestroyWindow("原始图像灰度分布");
        cvDestroyWindow("直方图均衡化图像");
        cvDestroyWindow("均衡化后直方图");
        return 0;
    }
     opencv里也自带直方图均衡化代码:
    View Code
    #include "cv.h"
    #include "highgui.h"
    
    #define HDIM    256    // bin of HIST, default = 256
    
    int main( int argc, char** argv ) 
    {
        IplImage *src = 0, *dst = 0;
        CvHistogram *hist = 0;
        
        int n = HDIM;     
        double nn[HDIM];
        uchar T[HDIM];
        CvMat *T_mat;
        
        int x;
        int sum = 0; // sum of pixels of the source image 图像中象素点的总和
        double val = 0;
        
        if( argc != 2 || (src=cvLoadImage(argv[1], 0)) == NULL)  // force to gray image
            return -1;
        
        cvNamedWindow( "source", 1 );
        cvNamedWindow( "result", 1 );
        
        // 计算直方图
        hist = cvCreateHist( 1, &n, CV_HIST_ARRAY, 0, 1 );  
        cvCalcHist( &src, hist, 0, 0 ); 
        
        // Create Accumulative Distribute Function of histgram
        val = 0;
        for ( x = 0; x < n; x++)
        {
            val = val + cvGetReal1D (hist->bins, x);
            nn[x] = val;
        }
    
        // 归一化直方图
        sum = src->height * src->width;
        for( x = 0; x < n; x++ )
        {
            T[x] = (uchar) (255 * nn[x] / sum); // range is [0,255]
        }
    
        // Using look-up table to perform intensity transform for source image 
        dst = cvCloneImage( src );
        T_mat = cvCreateMatHeader( 1, 256, CV_8UC1 );
        cvSetData( T_mat, T, 0 );    
        // 直接调用内部函数完成 look-up-table 的过程
        cvLUT( src, dst, T_mat ); 
    
        cvShowImage( "source", src );
        cvShowImage( "result", dst );
        cvWaitKey(0);
    
        cvDestroyWindow("source");
        cvDestroyWindow("result");
        cvReleaseImage( &src );
        cvReleaseImage( &dst );
        cvReleaseHist ( &hist );
        
        return 0;
    }

     图像对比度增强的方法可以分成两类:一类是直接对比度增强方法;另一类是间接对比度增强方法。直方图拉伸和直方图均衡化是两种最常见的间接对比度增强方法。直方图拉伸是通过对比度拉伸对直方图进行调整,从而“扩大”前景和背景灰度的差别,以达到增强对比度的目的,这种方法可以利用线性或非线性的方法来实现;直方图均衡化则通过使用累积函数对灰度值进行“调整”以实现对比度的增强。

    直方图拉伸进行图像增强的代码如下:

    View Code
    // Contrast_Enhance.cpp : 定义控制台应用程序的入口点。
    //
    
    #include "stdafx.h" 
    #include "cv.h"
    #include "highgui.h"
    
    int ImageStretchByHistogram(IplImage *src,IplImage *dst); 
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        IplImage * pImg;    
        pImg=cvLoadImage("F:/test_photo/12.jpg",-1); 
    
    //创建一个灰度图像
        IplImage* GrayImage = cvCreateImage(cvGetSize(pImg), IPL_DEPTH_8U, 1);
        IplImage* dstGrayImage = cvCreateImage(cvGetSize(pImg), IPL_DEPTH_8U, 1);
        cvCvtColor(pImg, GrayImage, CV_BGR2GRAY);
        ImageStretchByHistogram(GrayImage,dstGrayImage); 
    
      cvNamedWindow( "dstGrayImage", 1 ); //创建窗口
            cvNamedWindow( "GrayImage", 1 ); //创建窗口
            cvShowImage( "dstGrayImage", dstGrayImage ); //显示图像
            cvShowImage( "GrayImage", GrayImage ); //显示图像
            cvWaitKey(0); //等待按键 
    
      cvDestroyWindow( "dstGrayImage" );//销毁窗口
            cvDestroyWindow( "GrayImage" );//销毁窗口
            cvReleaseImage( &pImg ); //释放图像
            cvReleaseImage( &GrayImage ); //释放图像
            cvReleaseImage( &dstGrayImage ); //释放图像 
    
      return 0;
    } 
    
    int ImageStretchByHistogram(IplImage *src,IplImage *dst)
    /*************************************************
      Function:        
      Description:     因为摄像头图像质量差,需要根据直方图进行图像增强,
                       将图像灰度的域值拉伸到0-255
      Calls:          
      Called By:      
      Input:           单通道灰度图像                  
      Output:          同样大小的单通道灰度图像 
      Return:          
      Others:           http://www.xiaozhou.net/ReadNews.asp?NewsID=771
      DATE:               2007-1-5
    *************************************************/
    {
        //p[]存放图像各个灰度级的出现概率;
        //p1[]存放各个灰度级之前的概率和,用于直方图变换;
        //num[]存放图象各个灰度级出现的次数; 
    
        assert(src->width==dst->width);
        float p[256],p1[256],num[256];
        //清空三个数组
        memset(p,0,sizeof(p));
        memset(p1,0,sizeof(p1));
        memset(num,0,sizeof(num)); 
    
        int height=src->height;
        int width=src->width;
        long wMulh = height * width; 
    
        //求存放图象各个灰度级出现的次数
        // to do use openmp
          for(int x=0;x<width;x++)   {
             for(int y=0;y<height;y++)    {
                uchar v=((uchar*)(src->imageData + src->widthStep*y))[x];
                num[v]++;
            }
        } 
    
        //求存放图像各个灰度级的出现概率
        for(int i=0;i<256;i++)
        {
            p[i]=num[i]/wMulh;
        } 
    
        //求存放各个灰度级之前的概率和
        for(int i=0;i<256;i++)
        {
            for(int k=0;k<=i;k++)
                p1[i]+=p[k];
        } 
    
        //直方图变换
        // to do use openmp
        for(int x=0;x<width;x++)  {
             for(int y=0;y<height;y++)
         {
                uchar v=((uchar*)(src->imageData + src->widthStep*y))[x];
                ((uchar*)(dst->imageData + dst->widthStep*y))[x]= p1[v]*255+0.5;            
            }
        } 
    
        return 0; 
    
    } 
  • 相关阅读:
    从零搭建一个IdentityServer——项目搭建
    自学是门手艺-准备好好读读这本书
    Python学习路径
    如何查看一套Android代码的版本
    用tmux让程序在ssh退出后保持运行
    AOSP patch
    The Update Engine Action System
    职 工 养 老 保 险 转 移—陕西省外转入
    uml资料
    ABOTA资料汇集
  • 原文地址:https://www.cnblogs.com/yingying0907/p/2608597.html
Copyright © 2011-2022 走看看