Выбор доминирующего цвета в Qt - PullRequest
0 голосов
/ 03 мая 2020

Как я могу получить доминирующий цвет QImage или QIcon в Qt5? Нужно ли создавать гистограмму вручную? И если да, то как?

Ответы [ 2 ]

0 голосов
/ 04 мая 2020

Наконец, с помощью класса QImage я могу это сделать, и я реализовал простой алгоритм с нуля.

Идея состоит в том, чтобы получить наиболее частую интенсивность в каждом цветовом канале, и затем объединяя их, чтобы получить окончательный RGB цвет.

Возвращает QVector<int> из трех каналов.

Вспомогательная функция:

int MainWindow::get_max(QVector<int> vec){
int max = vec[0];
int index = 0;
for(int i = 0 ; i < 255 ; i++){
    if(vec[i] > max){
        max = vec[i];
        index = i;
    }
}
return index;
}

И функция выбора :

QVector<int> MainWindow::get_mean_rgb_for_img(const QImage img){


QRgb *ct;
int width , height;
width = img.width();
height = img.height();

QVector<int> red(256);
QVector<int> green(256);
QVector<int> blue(256);
for(int i = 0 ; i < height ; i++){
    ct = (QRgb *)img.scanLine(i);
    for(int j = 0 ; j < width ; j++){
        red[qRed(ct[j])]+=1;
    }
}
for(int i = 0 ; i < height ; i++){
    ct = (QRgb *)img.scanLine(i);
    for(int j = 0 ; j < width ; j++){
        green[qGreen(ct[j])]+=1;
    }
}
for(int i = 0 ; i < height ; i++){
    ct = (QRgb *)img.scanLine(i);
    for(int j = 0 ; j < width ; j++){
        blue[qBlue(ct[j])]+=1;
    }
}

int red_val = get_max(red);
int green_val = get_max(green);
int blue_val = get_max(blue);

QVector<int> result(3);
result.insert(0 , red_val);
result.insert(1 , green_val);
result.insert(2 , blue_val);

return result;
}

Хотя для этих вычислений есть только один поток (по крайней мере, на бумаге, но на практике Qt породил несколько потоков без моего уведомления сам по себе), он довольно быстрый и требует небольшого процессорного времени даже для изображений 4K !!!

0 голосов
/ 03 мая 2020

Вы не можете получить эти данные через Qt. Но я нашел для вас эту веб-страницу (http://robocraft.ru/blog/computervision/1063.html). На этой веб-странице какой-то парень написал код для вашего вопроса! Но все описания только на русском языке. Я перевел это для вас. Он использует OpenCV lib.

//
// determination of the prevailing colors in the image
// via k-Means
//

#include <cv.h>
#include <highgui.h>
#include <stdlib.h>
#include <stdio.h>

#include <vector>
#include <algorithm>

// getting an image pixel (by type of image and coordinates)
#define CV_PIXEL(type,img,x,y) (((type*)((img)->imageData+(y)*(img)->widthStep))+(x)*(img)->nChannels)

const CvScalar BLACK = CV_RGB(0, 0, 0);             // black
const CvScalar WHITE = CV_RGB(255, 255, 255);       // white

const CvScalar RED = CV_RGB(255, 0, 0);             // red
const CvScalar ORANGE = CV_RGB(255, 100, 0);        // orange
const CvScalar YELLOW = CV_RGB(255, 255, 0);        // yellow
const CvScalar GREEN = CV_RGB(0, 255, 0);           // green  
const CvScalar LIGHTBLUE = CV_RGB(60, 170, 255);    // blue
const CvScalar BLUE = CV_RGB(0, 0, 255);            // blue 2 
const CvScalar VIOLET = CV_RGB(194, 0, 255);        // Violet

const CvScalar GINGER = CV_RGB(215, 125, 49);       // redhead
const CvScalar PINK = CV_RGB(255, 192, 203);        // pink
const CvScalar LIGHTGREEN = CV_RGB(153, 255, 153);  // light green
const CvScalar BROWN = CV_RGB(150, 75, 0);          // brown 

typedef unsigned char uchar;
typedef unsigned int uint;

typedef struct ColorCluster {
    CvScalar color;
    CvScalar new_color;
    int count;

    ColorCluster():count(0) {
    }
} ColorCluster;

float rgb_euclidean(CvScalar p1, CvScalar p2)
{
    float val = sqrtf( (p1.val[0]-p2.val[0])*(p1.val[0]-p2.val[0]) + 
        (p1.val[1]-p2.val[1])*(p1.val[1]-p2.val[1]) +
        (p1.val[2]-p2.val[2])*(p1.val[2]-p2.val[2]) +
        (p1.val[3]-p2.val[3])*(p1.val[3]-p2.val[3]));

    return val;
}

// sorting flowers by quantity
bool colors_sort(std::pair< int, uint > a, std::pair< int, uint > b)
{
    return (a.second > b.second);
}

int main(int argc, char* argv[])
{
    // for save image
    IplImage* image=0, *src=0, *dst=0, *dst2=0;

    //
    // loading image
    //
    char img_name[] =  "Image0.jpg";
    // name of image in first arg 
    char* image_filename = argc >= 2 ? argv[1] : img_name;

    // get image
    image = cvLoadImage(image_filename, 1);

    printf("[i] image: %s\n", image_filename);
    if(!image){
        printf("[!] Error: cant load test image: %s\n", image_filename);
        return -1;
    }

    // show image
    cvNamedWindow("image");
    cvShowImage("image", image);

    // resize image (for processing speed)
    src = cvCreateImage(cvSize(image->width/2, image->height/2), IPL_DEPTH_8U, 3);
    cvResize(image, src, CV_INTER_LINEAR);

    cvNamedWindow("img");
    cvShowImage("img", src);

    // image for storing cluster indexes
    IplImage* cluster_indexes = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
    cvZero(cluster_indexes);

#define CLUSTER_COUNT 10
    int cluster_count = CLUSTER_COUNT;
    ColorCluster clusters[CLUSTER_COUNT];

    int i=0, j=0, k=0, x=0, y=0;

    // initial cluster colors
#if 0
    clusters[0].new_color = RED;
    clusters[1].new_color = ORANGE;
    clusters[2].new_color = YELLOW;
    //  clusters[3].new_color = GREEN;
    //  clusters[4].new_color = LIGHTBLUE;
    //  clusters[5].new_color = BLUE;
    //  clusters[6].new_color = VIOLET;
#elif 0
    clusters[0].new_color = BLACK;
    clusters[1].new_color = GREEN;
    clusters[2].new_color = WHITE;
#else
    CvRNG rng = cvRNG(-1);
    for(k=0; k<cluster_count; k++)
        clusters[k].new_color = CV_RGB(cvRandInt(&rng)%255, cvRandInt(&rng)%255, cvRandInt(&rng)%255);
#endif

    // k-means
    float min_rgb_euclidean = 0, old_rgb_euclidean=0;

    while(1) {
        for(k=0; k<cluster_count; k++) {
            clusters[k].count = 0;
            clusters[k].color = clusters[k].new_color;
            clusters[k].new_color = cvScalarAll(0);
        }

        for (y=0; y<src->height; y++) {
            for (x=0; x<src->width; x++) {
                // get the RGB components of the pixel
                uchar B = CV_PIXEL(uchar, src, x, y)[0];    // B
                uchar G = CV_PIXEL(uchar, src, x, y)[1];    // G
                uchar R = CV_PIXEL(uchar, src, x, y)[2];    // R

                min_rgb_euclidean = 255*255*255;
                int cluster_index = -1;
                for(k=0; k<cluster_count; k++) {
                    float euclid = rgb_euclidean(cvScalar(B, G, R, 0), clusters[k].color);
                    if(  euclid < min_rgb_euclidean ) {
                        min_rgb_euclidean = euclid;
                        cluster_index = k;
                    }
                }
                // set the cluster index
                CV_PIXEL(uchar, cluster_indexes, x, y)[0] = cluster_index;

                clusters[cluster_index].count++;
                clusters[cluster_index].new_color.val[0] += B;
                clusters[cluster_index].new_color.val[1] += G;
                clusters[cluster_index].new_color.val[2] += R;
            }
        }

        min_rgb_euclidean = 0;
        for(k=0; k<cluster_count; k++) {
            // new color
            clusters[k].new_color.val[0] /= clusters[k].count;
            clusters[k].new_color.val[1] /= clusters[k].count;
            clusters[k].new_color.val[2] /= clusters[k].count;
            float ecli = rgb_euclidean(clusters[k].new_color, clusters[k].color);
            if(ecli > min_rgb_euclidean)
                min_rgb_euclidean = ecli;
        }

        //printf("%f\n", min_rgb_euclidean);
        if( fabs(min_rgb_euclidean - old_rgb_euclidean)<1 ) 
            break;

        old_rgb_euclidean = min_rgb_euclidean;
    }
    //-----------------------------------------------------

    // Now we drive the array into a vector and sort it :)
    std::vector< std::pair< int, uint > > colors;
    colors.reserve(CLUSTER_COUNT);

    int colors_count = 0;
    for(i=0; i<CLUSTER_COUNT; i++){
        std::pair< int, uint > color;
        color.first = i;
        color.second = clusters[i].count;
        colors.push_back( color );
        if(clusters[i].count>0)
            colors_count++;
    }
    // sort
    std::sort( colors.begin(), colors.end(), colors_sort );

    // show color
    dst2 = cvCreateImage( cvGetSize(src), IPL_DEPTH_8U, 3 );
    cvZero(dst2);
    int h = dst2->height / CLUSTER_COUNT;
    int w = dst2->width;
    for(i=0; i<CLUSTER_COUNT; i++ ){
        cvRectangle(dst2, cvPoint(0, i*h), cvPoint(w, i*h+h), clusters[colors[i].first].color, -1);
        printf("[i] Color: %d %d %d (%d)\n", (int)clusters[colors[i].first].color.val[2],
            (int)clusters[colors[i].first].color.val[1],
            (int)clusters[colors[i].first].color.val[0],
            clusters[colors[i].first].count);
    }
    cvNamedWindow("colors");
    cvShowImage("colors", dst2);
    //cvResize(dst2, image, CV_INTER_LINEAR);
    //cvSaveImage("dominate_colors_table.png", image);
    //-----------------------------------------------------

    // show the picture in the colors found
    dst = cvCloneImage(src);
    for (y=0; y<dst->height; y++) {
        for (x=0; x<dst->width; x++) {
            int cluster_index = CV_PIXEL(uchar, cluster_indexes, x, y)[0];

            CV_PIXEL(uchar, dst, x, y)[0] = clusters[cluster_index].color.val[0];
            CV_PIXEL(uchar, dst, x, y)[1] = clusters[cluster_index].color.val[1];
            CV_PIXEL(uchar, dst, x, y)[2] = clusters[cluster_index].color.val[2];
        }
    }

    cvNamedWindow("dst");
    cvShowImage("dst", dst);
    //cvResize(dst, image, CV_INTER_LINEAR);
    //cvSaveImage("dominate_colors.png", image);
    //-----------------------------------------------------

    // waiting for the keystroke
    cvWaitKey(0);

    // free up resources
    cvReleaseImage(&image);
    cvReleaseImage(&src);

    cvReleaseImage(&cluster_indexes);

    cvReleaseImage(&dst);
    cvReleaseImage(&dst2);

    // delete windows 
    cvDestroyAllWindows();
    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...