3

入力データとして、24 ビット RGB 画像と 2..20 固定色のパレットがあります。これらの色は、色の範囲全体に規則的に広がっているわけではありません。

ここで、指定されたパレットの色のみが使用されるように入力画像の色を変更する必要があります-元の色に最も近いパレットの色を使用します(数学的に最も近いのではなく、人間の視覚的印象のため)。したがって、入力色を使用して、この色に視覚的に最適なターゲット パレットの色を見つけるアルゴリズムが必要です。注意してください: 私は愚かな比較/差分アルゴリズムを探しているのではなく、色が人間に与える印象を実際に取り入れたものを探しています!

Since this is something that already should have been done and because I do not want to re-invent the wheel again: is there some example source code out there that does this job? In best case it is really a piece of code and not a link to a desastrous huge library ;-)

(I'd guess OpenCV does not provide such a function?)

Thanks

4

2 に答える 2

3

Lab 色空間を見る必要があります。色空間の距離が知覚距離と等しくなるように設計されています。したがって、画像を変換したら、以前と同じように距離を計算できますが、知覚的な観点からはより良い結果が得られるはずです。OpenCV では、関数を使用できますcvtColor(source, destination, CV_BGR2Lab)

別のアイデアは、ディザリングを使用することです。アイデアは、隣接するピクセルを使用して欠落している色を混合することです。このための一般的なアルゴリズムは、Floyd-Steinberg ditheringです。

これは私の例で、k-means を使用して最適化されたパレットを Lab 色空間とフロイド スタインバーグ ディザリングと組み合わせたものです。

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

cv::Mat floydSteinberg(cv::Mat img, cv::Mat palette);
cv::Vec3b findClosestPaletteColor(cv::Vec3b color, cv::Mat palette);

int main(int argc, char** argv)
{
    // Number of clusters (colors on result image)
    int nrColors = 18;

    cv::Mat imgBGR = imread(argv[1],1);

    cv::Mat img;
    cvtColor(imgBGR, img, CV_BGR2Lab);


    cv::Mat colVec = img.reshape(1, img.rows*img.cols); // change to a Nx3 column vector

    cv::Mat colVecD;
    colVec.convertTo(colVecD, CV_32FC3, 1.0); // convert to floating point


    cv::Mat labels, centers;
    cv::kmeans(colVecD, nrColors, labels,
            cv::TermCriteria(CV_TERMCRIT_ITER, 100, 0.1),
            3, cv::KMEANS_PP_CENTERS, centers); // compute k mean centers

    // replace pixels by there corresponding image centers
    cv::Mat imgPosterized = img.clone();
    for(int i = 0; i < img.rows; i++ )
        for(int j = 0; j < img.cols; j++ )
            for(int k = 0; k < 3; k++)
                imgPosterized.at<Vec3b>(i,j)[k] = centers.at<float>(labels.at<int>(j+img.cols*i),k);

    // convert palette back to uchar
    cv::Mat palette;
    centers.convertTo(palette,CV_8UC3,1.0);

    // call floyd steinberg dithering algorithm
    cv::Mat fs = floydSteinberg(img, palette);

    cv::Mat imgPosterizedBGR, fsBGR;
    cvtColor(imgPosterized, imgPosterizedBGR, CV_Lab2BGR);
    cvtColor(fs, fsBGR, CV_Lab2BGR);


    imshow("input",imgBGR); // original image
    imshow("result",imgPosterizedBGR); // posterized image
    imshow("fs",fsBGR); // floyd steinberg dithering
    waitKey();

  return 0;
}

cv::Mat floydSteinberg(cv::Mat imgOrig, cv::Mat palette)
{
    cv::Mat img = imgOrig.clone();
    cv::Mat resImg = img.clone();
    for(int i = 0; i < img.rows; i++ )
        for(int j = 0; j < img.cols; j++ )
        {
            cv::Vec3b newpixel = findClosestPaletteColor(img.at<Vec3b>(i,j), palette);
            resImg.at<Vec3b>(i,j) = newpixel;

            for(int k=0;k<3;k++)
            {
                int quant_error = (int)img.at<Vec3b>(i,j)[k] - newpixel[k];
                if(i+1<img.rows)
                    img.at<Vec3b>(i+1,j)[k] = min(255,max(0,(int)img.at<Vec3b>(i+1,j)[k] + (7 * quant_error) / 16));
                if(i-1 > 0 && j+1 < img.cols)
                    img.at<Vec3b>(i-1,j+1)[k] = min(255,max(0,(int)img.at<Vec3b>(i-1,j+1)[k] + (3 * quant_error) / 16));
                if(j+1 < img.cols)
                    img.at<Vec3b>(i,j+1)[k] = min(255,max(0,(int)img.at<Vec3b>(i,j+1)[k] + (5 * quant_error) / 16));
                if(i+1 < img.rows && j+1 < img.cols)
                    img.at<Vec3b>(i+1,j+1)[k] = min(255,max(0,(int)img.at<Vec3b>(i+1,j+1)[k] + (1 * quant_error) / 16));
            }
        }
    return resImg;
}

float vec3bDist(cv::Vec3b a, cv::Vec3b b) 
{
    return sqrt( pow((float)a[0]-b[0],2) + pow((float)a[1]-b[1],2) + pow((float)a[2]-b[2],2) );
}

cv::Vec3b findClosestPaletteColor(cv::Vec3b color, cv::Mat palette)
{
    int i=0;
    int minI = 0;
    cv::Vec3b diff = color - palette.at<Vec3b>(0);
    float minDistance = vec3bDist(color, palette.at<Vec3b>(0));
    for (int i=0;i<palette.rows;i++)
    {
        float distance = vec3bDist(color, palette.at<Vec3b>(i));
        if (distance < minDistance)
        {
            minDistance = distance;
            minI = i;
        }
    }
    return palette.at<Vec3b>(minI);
}
于 2013-08-22T14:39:46.887 に答える