基于模糊聚类的色彩迁移算法

文章目录


  接下来要讲到的色彩迁移算法是国内学者钱小燕等人提出的一种基于模糊集理论的色彩迁移算法。
  在介绍FCM色彩迁移算法之前需要对模糊集与聚类算法有一些基础的了解,具体可参照这份文档,讲的比较详细:https://download.csdn.net/download/qq_38701868/11231322

  这种颜色聚类算法利用FCM思想把源图像和目标图像分成若干包含不同颜色特征的聚类域;模糊聚类思想使得图像中各点的不同隶属度属于各聚类域;在选择匹配域,为每个聚类域设置一个匹配权值参数,根据目标图的特征采用不同的权值计算方法,并根据目标图中的匹配权值在源图中选取一个与之最接近的域作为匹配域;在颜色传输过程中,引入隶属度因子对各聚类域的处理结果进行平滑,以解决图像中存在不确定性信息的情况。
  设待处理图像的大小 I = S * H, 进行聚类的个数 N = S * H,则 I 可表示为 I = {p1,p2,p3…Pn},其中 p i = { p k l , p k a , p k b } p_{i}=\left \{ p_{k}^{l},p_{k}^{a},p_{k}^{b} \right \} pi={ pkl,pka,pkb},将图像分为 c 类,聚类中心可表示为 V = {V1,V2,V3…Vc}。用 Uik 表示像素 Pk 到聚类域 Vi 的隶属度。
  算法的具体步骤如下:
  step1:将图像从 RGB 颜色空间转到 lab 颜色空间
  step2:初始化基本参数:聚类个数 c、加权指数 m、算法最大迭代次数 LOOP、终止误差 e > 0。
  step3:初始化聚类中心与隶属度矩阵
  step4:当迭代次数 T < LOOP 时,对所有聚类中心Vi(i = 1,2,3…c)根据公式计算隶属度矩阵:
    如果Pk != Vi,则
               u i k = 1 / ∑ j = 1 c ( d i k / d j k ) 2 / ( m − 1 ) u_{ik} = 1/\sum_{j=1}^{c}(d_{ik}/d_{jk})^{2/(m-1)} uik=1/j=1c(dik/djk)2/(m1),k = 1,2,3…N
  否则,若当前像素为聚类中心 Vi ,则 Uik = 1,若当前像素点为其他非 Vi 的聚类中心,则 Uik = 0。
  其中,dik 为第 k 个像素点到 第 i 个聚类中心的欧氏距离:

  step5:进行聚类划分,计算新的聚类中心:

  step6:检查收敛情况,若新计算出的聚类中心到上一个聚类中心的距离小于 e ,则算法介绍,否则返回步骤4继续迭代计算。

  在聚类划分后,根据目标图像的不同,计算匹配权值:
  若图像为灰度图,根据 l 通道计算权值:

  其中,Z为规范化加权因子 Ck 为第 k 个聚类域

  若目标图为彩色图像,根据三个通道计算匹配权值:

基于模糊聚类的色彩迁移算法_第1张图片

  最后,扫描目标图像的每个像素点,设像素点 Pk 的归属聚类域的对应匹配域为 h,则按公式转换通道值:

基于模糊聚类的色彩迁移算法_第2张图片

其中,下标 T 表示源图像,下标 S 表示目标图像。

算法结果:
彩色图像之间:
基于模糊聚类的色彩迁移算法_第3张图片
基于模糊聚类的色彩迁移算法_第4张图片
彩色图像与灰度图像之间:
基于模糊聚类的色彩迁移算法_第5张图片
基于模糊聚类的色彩迁移算法_第6张图片

代码有点多…就贴比较重要的代码了:

颜色聚类:

 void FCMCluster(uchar *lpDIBBits, int lmageWidth, int lmageHeight, double* belong, double* center, int classnum, int m, int LOOP, double threshold)
    {
     
        int i, j, l, nindex;//循环控制变量
        int k = 0;
        //int LOOP = 500;
        double* center2 = new double[classnum * 3];//聚类中心
        int x, y;//随机确定聚类中心坐标
        int* num = new int[classnum];//每个类的像素个数
        double* lpImageLab = new  double[lmageWidth*lmageHeight * 3];
        double sumu, suml, suma, sumb;

        //初始化聚类中心
        for (i = 0; i < classnum; i++)
        {
     
            x = rand() % lmageWidth;
            y = rand() % lmageHeight;
            nindex = ((lmageHeight - y - 1)*lmageWidth + x);
            RgbToLab2(lpDIBBits[nindex * 3 + 2], lpDIBBits[nindex * 3 + 1], lpDIBBits[nindex * 3 + 0], center[i * 3 + 0], center[i * 3 + 1], center[i * 3 + 2]);
            for (j = 0; j < i; j++)
            {
     
                double dis = DistanceLab(center[i * 3 + 0], center[i * 3 + 1], center[i * 3 + 2], center[j * 3 + 0], center[j * 3 + 1], center[j * 3 + 2]);
                if (dis < 0.2) {
      i--; break; }//限值公式 暂取限值为1待优化 对初始化聚类中心的选择非常关键,若新选择的聚类中心与之前选择的中心聚类距离过近,则重新选择
            }
        }

        //计算隶属度矩阵、更新聚类中心、直至前后聚类中心距离小于限值e暂定0.1待优化
        while (k != classnum && LOOP--)//限值公式
        {
     
            //计算隶属度矩阵
            for (j = 0; j < lmageHeight; j++)
            {
     
                for (i = 0; i < lmageWidth; i++)
                {
     
                    nindex = ((lmageHeight - j - 1)*lmageWidth + i);
                    RgbToLab2(lpDIBBits[nindex * 3 + 2], lpDIBBits[nindex * 3 + 1], lpDIBBits[nindex * 3 + 0],
                        lpImageLab[nindex * 3 + 0], lpImageLab[nindex * 3 + 1], lpImageLab[nindex * 3 + 2]);
                    //means_Assign();
                    for (k = 0; k < classnum; k++)
                    {
     
                        sumu = 0;
                        double dis1 = DistanceLab(lpImageLab[nindex * 3 + 0], lpImageLab[nindex * 3 + 1], lpImageLab[nindex * 3 + 2], center[k * 3 + 0], center[k * 3 + 1], center[k * 3 + 2]);
                        if (dis1 == 0) {
      belong[lmageWidth*lmageHeight*k + nindex] = 1; continue; }
                        for (l = 0; l < classnum; l++)
                        {
     
                            double dis2 = DistanceLab(lpImageLab[nindex * 3 + 0], lpImageLab[nindex * 3 + 1], lpImageLab[nindex * 3 + 2], center[l * 3 + 0], center[l * 3 + 1], center[l * 3 + 2]);
                            if (dis2 == 0) break;
                            sumu += pow((dis1*dis1) / (dis2*dis2), 1.0 / (m - 1));
                        }
                        if (l != classnum) {
      belong[lmageWidth*lmageHeight*k + nindex] = 0; continue; }
                        belong[lmageWidth*lmageHeight*k + nindex] = 1 / sumu;
                    }
                }
            }

            //更新聚类中心
            for (k = 0; k < classnum; k++)
            {
     
                suml = suma = sumb = sumu = 0;
                for (j = 0; j < lmageHeight; j++)
                {
     
                    for (i = 0; i < lmageWidth; i++)
                    {
     
                        nindex = ((lmageHeight - j - 1)*lmageWidth + i);
                        suml += pow(belong[lmageWidth*lmageHeight*k + nindex], m)*lpImageLab[nindex * 3 + 0];
                        suma += pow(belong[lmageWidth*lmageHeight*k + nindex], m)*lpImageLab[nindex * 3 + 1];
                        sumb += pow(belong[lmageWidth*lmageHeight*k + nindex], m)*lpImageLab[nindex * 3 + 2];
                        sumu += pow(belong[lmageWidth*lmageHeight*k + nindex], m);
                    }
                }
                center2[k * 3 + 0] = suml / sumu;
                center2[k * 3 + 1] = suma / sumu;
                center2[k * 3 + 2] = sumb / sumu;
            }

            //判断循环终止条件
            for (k = 0; k < classnum; k++)
            {
     
                if (DistanceLab(center[k * 3 + 0], center[k * 3 + 1], center[k * 3 + 2], center2[k * 3 + 0], center2[k * 3 + 1], center2[k * 3 + 2]) > threshold)
                    break;//限值e暂定0.1待优化
            }
            for (i = 0; i < classnum * 3; i++)
            {
     
                center[i] = center2[i];
            }

        }
    }

FCM实现:

void TranFCM(uchar *lpDIBBits, int lmageWidth, int lmageHeight, uchar *lpDIBBits2, int lmageWidth2, int lmageHeight2, uchar *lpDIBBits3,
                 int classnum, int m, int loop, double threshold, bool flag, Mat& srcTest, Mat& dstTest, Mat& dstTest2)
        {
     
            //int classnum = 3;		//聚类数
            //int m = 2;
            int i, j, k, nindex;
            double l, a, b;
            double* belong = new double[lmageWidth*lmageHeight*classnum];
            double* belong2 = new double[lmageWidth2*lmageHeight2*classnum];
            double* center = new double[classnum * 3];
            double* center2 = new double[classnum * 3];
            int* clustermap = new int[classnum];
            double suml, suma, sumb;
            FCMCluster(lpDIBBits, lmageWidth, lmageHeight, belong, center, classnum, m,loop,threshold);
            FCMCluster(lpDIBBits2, lmageWidth2, lmageHeight2, belong2, center2, classnum, m,loop,threshold);

            //ClusterMap(center,center2);
            double* vl = new double[classnum];
            double* va = new double[classnum];
            double* vb = new double[classnum];
            double* vl2 = new double[classnum];
            double* va2 = new double[classnum];
            double* vb2 = new double[classnum];

            for (i = 0; i < classnum; i++)
            {
     
                uchar distance = 255;
                int map = -1;
                uchar r, g, b, r2, g2, b2;
                for (j = 0; j < classnum; j++)
                {
     
                    LabToRgb2(center[i * 3 + 0], center[i * 3 + 1], center[i * 3 + 2], r, g, b);
                    LabToRgb2(center2[j * 3 + 0], center2[j * 3 + 1], center2[j * 3 + 2], r2, g2, b2);
                    uchar dis = abs(RgbToGray(r2, g2, b2) - RgbToGray(r, g, b));
                    if (distance > dis) {
      distance = dis; map = j; }
                }
                clustermap[i] = map;
            }
            //TranColor(belong,belong2,center,center2);
            //求各聚类域的标准差
            double sumu = 0;
            for(k=0;k<classnum;k++)
            {
     
                suml=suma=sumb=sumu = 0;
                for(j = 0;j <lmageHeight; j++)
                {
     
                    for(i = 0; i <lmageWidth; i++)
                    {
     
                        nindex=((lmageHeight-j-1)*lmageWidth+i);
                        RgbToLab2(lpDIBBits[nindex*3+2],lpDIBBits[nindex*3+1],lpDIBBits[nindex*3+0],l,a,b);
                        suml+=pow(belong[lmageWidth*lmageHeight*k+nindex],m)*pow((l-center[k*3+0]),2);
                        suma+=pow(belong[lmageWidth*lmageHeight*k+nindex],m)*pow((a-center[k*3+1]),2);
                        sumb+=pow(belong[lmageWidth*lmageHeight*k+nindex],m)*pow((b-center[k*3+2]),2);
                        sumu+=pow(belong[lmageWidth*lmageHeight*k+nindex],m);
                    }
                }
                vl[k]=sqrt(suml/sumu);
                va[k]=sqrt(suma/sumu);
                vb[k]=sqrt(sumb/sumu);
            }

            for(k=0;k<classnum;k++)
            {
     
                suml = suma = sumb = sumu = 0;
                for(j = 0;j <lmageHeight2; j++)
                {
     
                    for(i = 0; i <lmageWidth2; i++)
                    {
     
                        nindex=((lmageHeight2-j-1)*lmageWidth2+i);
                        RgbToLab2(lpDIBBits2[nindex*3+2],lpDIBBits2[nindex*3+1],lpDIBBits2[nindex*3+0],l,a,b);
                        suml+=pow(belong2[lmageWidth2*lmageHeight2*k+nindex],m)*pow((l-center2[k*3+0]),2);
                        suma+=pow(belong2[lmageWidth2*lmageHeight2*k+nindex],m)*pow((a-center2[k*3+1]),2);
                        sumb+=pow(belong2[lmageWidth2*lmageHeight2*k+nindex],m)*pow((b-center2[k*3+2]),2);
                        sumu+=pow(belong2[lmageWidth2*lmageHeight2*k+nindex],m);
                    }
                }
                vl2[k]=sqrt(suml/sumu);
                va2[k]=sqrt(suma/sumu);
                vb2[k]=sqrt(sumb/sumu);
            }

            int *srcPixClusterMap = new int[lmageWidth2 * lmageHeight2];	//保存源图各像素的聚类域
            int *dstPixClusterMap = new int[lmageWidth * lmageHeight];	//保存目标图各像素的聚类域

            //获取各像素的聚类域
            getClusterMap(srcPixClusterMap, belong2, lmageWidth2, lmageHeight2, classnum);
            getClusterMap(dstPixClusterMap, belong, lmageWidth, lmageHeight, classnum);

            showCluster(srcTest,srcPixClusterMap,lmageWidth2,lmageHeight2);
            showCluster(dstTest2,dstPixClusterMap,lmageWidth,lmageHeight);
            showCluster1(dstTest,clustermap,dstPixClusterMap,lmageWidth,lmageHeight);


        //求结果图像的lab
            for (j = 0; j < lmageHeight; j++)
            {
     
                for (i = 0; i < lmageWidth; i++)
                {
     
                    nindex = ((lmageHeight - j - 1)*lmageWidth + i);
                    suml = suma = sumb = 0;
                    RgbToLab2(lpDIBBits[nindex * 3 + 2], lpDIBBits[nindex * 3 + 1], lpDIBBits[nindex * 3 + 0], l, a, b);
                    for (k = 0; k < classnum; k++)
                    {
     
                        if(flag == true){
     
                            suml += belong[lmageWidth*lmageHeight*k+nindex]*((l - center[k*3+0]) * vl2[clustermap[k]]/vl[k] + center2[clustermap[k]*3+0]);
                            suma += belong[lmageWidth*lmageHeight*k+nindex]*((a - center[k*3+1]) * va2[clustermap[k]]/va[k] + center2[clustermap[k]*3+1]);
                            sumb += belong[lmageWidth*lmageHeight*k+nindex]*((b - center[k*3+2]) * vb2[clustermap[k]]/vb[k] + center2[clustermap[k]*3+2]);
                        }else{
     
                            suml += belong[lmageWidth*lmageHeight*k + nindex] * center2[clustermap[k] * 3 + 0];
                            suma += belong[lmageWidth*lmageHeight*k + nindex] * center2[clustermap[k] * 3 + 1];
                            sumb += belong[lmageWidth*lmageHeight*k + nindex] * center2[clustermap[k] * 3 + 2];
                        }
                    }
                    LabToRgb2(l, suma, sumb, lpDIBBits3[nindex * 3 + 2], lpDIBBits3[nindex * 3 + 1], lpDIBBits3[nindex * 3 + 0]);
                }
            }
        }

之前写过的一个QT项目,里面有 Reinhard、Welsh和FCM色彩迁移算法的实现:https://download.csdn.net/download/qq_38701868/12034242

没下载积分的可百度网盘自取:
链接:https://pan.baidu.com/s/1kW98v9g_Nq1Yj6_YIHW3mQ
提取码:1u97

你可能感兴趣的:(#,色彩传递)