【徒手写机器学习算法】再谈数据源:从普通图片到Cifar-10(使用C++)

【徒手写机器学习算法】再谈数据源:从普通图片到Cifar-10(使用C++)

在本系列的第一篇文章里,关于机器学习的数据源的问题被一笔带过(使用csv格式的数据),这一篇文章我会给出关于图片数据制作的两个示例.

本文完整代码在:https://github.com/Luomin1993/Cell_For_Img


所需前提

在开始之前我说一下需要准备的东西:

  • g++编译器(废话..)
  • opencv2或opencv3
  • Cifar-10数据集(待会儿会说)

又是Lena

你可能会想,怎么又是这个女人,这是一个经典的图像处理的素材:
【徒手写机器学习算法】再谈数据源:从普通图片到Cifar-10(使用C++)_第1张图片

关于她:

谁是Lena? 最早使用这张相片作测试样本的是谁? 还有这张相片是怎么来的? 人人认识Lena,这些问题大家都很好奇,却很少人知道解答。Lena照片的来源是花花公子(Playboy)杂志1972年11月份玩伴女郎Lena全身裸照的中央插页,在这期杂志中使用了“Lenna”的拼写,而实际莉娜在瑞典语中的拼写是“lena”。
1973年6,7月间,南加州大学信号图像处理研究所的副教授Alexander和学生一起,为了一个同事的学会论文正忙于寻找一副好的图片。他们想要一副具有良好动态范围的人的面部图片用于扫描。不知是谁拿着一本Playboy走进研究室。由于当时实验室里使用的扫描仪(Muirhead wirephoto scanner)分辨率是100行/英寸,试验也仅仅需要一副512X512的图片,所以他们只将图片顶端开始的5.12英寸扫描下来,切掉肩膀一下的部分。

言归正传,我们来提取这个图像的直方图,并将直方图进行PAC降维处理,然后保存到csv文件供机器学习数据使用。


void saveMat(cv::Mat inputMat,char* filename)
{
    FILE* fpt = fopen(filename,"a");
    int rows = inputMat.rows;
    int clos = inputMat.cols;
    for (int i = 0; i < rows;i++)
    {
          for(int j = 0;jif (j < clos-1)
                  fprintf(fpt,"%f,",inputMat.at<float>(i,j));
               else
                  fprintf(fpt,"%f\n",inputMat.at<float>(i,j));
          }
    }
    fclose(fpt);
}

void saveMatAsVec(cv::Mat inputMat,char* filename)
{
    FILE* fpt = fopen(filename,"a");
    int rows = inputMat.rows;
    int clos = inputMat.cols;
    for (int i = 0; i < rows;i++)
    {
          for(int j = 0;j/*if (j < clos-1)
                  fprintf(fpt,"%f,",inputMat.at(i,j));
               else
                  fprintf(fpt,"%f\n",inputMat.at(i,j));*/
               fprintf(fpt,"%f,",inputMat.at<float>(i,j));
          }
    }
    fprintf(fpt,"\n");
    fclose(fpt);
}

void save_pca_vec_to_csv_file(cv::Mat img,char* filename)
{
    int floor_num = 120;  
    //cvtColor(img, img, CV_RGB2GRAY);
    PCA pca(img, Mat(), CV_PCA_DATA_AS_COL, floor_num);  
    //图片大小为400*362  
    //这里按COL的方式降维,保证列数不便,行数降低到120层  
    //所以可以发现打印的均值的规格为1*362  
    cout << "均值的规格:" << pca.mean.size() << endl;//均值  
    cout << "特征值的规格:"<//特征值  
    cout <<"特征向量的规格:" <//特征向量  

    Mat dst = pca.project(img);//映射新空间  
    //cout << dst;  
    //Mat src = pca.backProject(dst);//反映射回来  
    saveMatAsVec(dst,"./color.csv");
}

关于PAC降维:主成分分析,是一种统计方法,通过正交变换将一组可能存在相关性的变量转换为一组线性不相关的变量,转换后的这组变量叫主成分。

  1. 将原始数据按列组成 n n m m 列矩阵 X X
  2. 将X的每一行(代表一个属性字段)进行零均值化,即减去这一行的均值
  3. 求出协方差矩阵 C=1mXXT C = 1 m X X T
  4. 求出协方差矩阵的特征值及对应的特征向量
  5. 将特征向量按对应特征值大小从上到下按行排列成矩阵,取前k行组成矩阵P
  6. Y=PX Y = P X 即为降维到 k k 维后的数据

编译运行方式:

root@master:# g++ -I/usr/local/opencv2/include/ -o color_feature color_feature.cpp  -L/usr/local/opencv2/lib -lopencv_objdetect -lopencv_shape -lopencv_stitching -lopencv_superres -lopencv_videostab -lopencv_calib3d -lopencv_features2d -lopencv_highgui -lopencv_videoio -lopencv_imgcodecs -lopencv_video -lopencv_photo -lopencv_imgproc -lopencv_flann -lopencv_core -lopencv_nonfree
root@master:# ./color_feature lena.jpg

关于Cifar-10

该数据集共有60000张彩色图像,这些图像是32*32,分为10个类,每类6000张图。这里面有50000张用于训练,构成了5个训练批,每一批10000张图;另外10000用于测试,单独构成一批。测试批的数据里,取自10类中的每一类,每一类随机取1000张。抽剩下的就随机排列组成了训练批。注意一个训练批中的各类图像并不一定数量相同,总的来看训练批,每一类都有5000张图。

它的下载地址:http://www.cs.toronto.edu/~kriz/cifar.html

现在让我们用opencv读取它的二进制格式数据,再将其转化为图片,然后我们就可以用上面的特征提取程序来提取特征向量到文本文件。


int main(int argc, char *argv[])
{
    //QCoreApplication a(argc, argv);
    FILE *fpr = fopen("./cifar-10-batches-bin/data_batch_1.bin","rb");//打开cifar-10的一个文件
    if(fpr==NULL)
    {
        cout<<"文件打开失败!"<return 0;
    }
    int labelr(0);//存label
    char buffer = 0;//缓存
    int yrow = 100;//行图片数
    int xcol = 100;//列图片数
    int image_num;
    //Mat image(32*yrow,32*xcol,CV_8UC3,Scalar::all(0));//opencv 的Mat对象,用来存图片的像素矩阵
    //重点是这个循环
    //Cifar file stream:[[label:(R),(G),(B)],[label:(R),(G),(B)],[label:(R),(G),(B)],[label:(R),(G),(B)]...]
    //std::vector RES;
    for(int y = 0;y<=yrow-1;y++)//循环行图片
    {
        for(int x = 0;x<=xcol-1;x++)//列图片
        {
            Mat image(32,32,CV_8UC3,Scalar::all(0));//opencv 的Mat对象,用来存图片的像素矩阵
            fread(&labelr,sizeof(char),1,fpr);//获取每张图片前的label  不要忘记了
            cout<<"label:"<for(int b = 2;b>=0;b--)//循环RGB颜色
            {
                for(int j = 0;j<32;j++)//循环行像素
                {
                    for(int i = 0;i<32;i++)//循环列像素
                    {
                        fread(&buffer,sizeof(char),1,fpr);
                        image.at(j,i)[b] = buffer;//用at来获取数据 ,还有其他办法,大家可以找找
                    }
                }
            }
            imwrite("./CIFAR10/"+std::to_string(x*y)+".jpg", image);
            //RES.push_back(image);
            //show the image one by one
            //if(y==0 && x==0){imshow("1",image);waitKey(100);}
            //sleep(3);
        }
    }
    //imshow("1",RES[10]);waitKey(100000);
    fclose(fpr);
    return 0;
}

提取成果:
这里写图片描述

你可能感兴趣的:(徒手系列,徒手写机器学习算法)