Подтвердить что ты не робот

Как уменьшить количество цветов в изображении с помощью OpenCV?

У меня есть набор файлов изображений, и я хочу уменьшить их количество до 64. Как я могу сделать это с помощью OpenCV?

Мне нужно это, чтобы я мог работать с гистограммой изображения размером 64. Я внедряю методы CBIR

То, что я хочу, - это квантование цвета для 4-битной палитры.

4b9b3361

Ответ 1

Есть много способов сделать это. Методы, предложенные jeff7, в порядке, но некоторые недостатки:

  • метод 1 имеет параметры N и M, которые вы должны выбрать, и вы также должны преобразовать его в другое цветовое пространство.
  • Ответ на метод 2 может быть очень медленным, так как вы должны вычислить гистограмму 16,7 Миньонов и отсортировать ее по частоте (чтобы получить 64 значения более высокой частоты).

Мне нравится использовать алгоритм на основе Наиболее значимых битов для использования в цвете RGB и преобразования его в 64-цветное изображение. Если вы используете C/OpenCV, вы можете использовать что-то вроде функции ниже.

Если вы работаете с изображениями на уровне серого, я рекомендовал использовать функцию LUT() OpenCV 2.3, так как она быстрее. Существует руководство по использованию LUT для уменьшения количества цветов. См.: Учебное пособие. Как сканировать изображения, таблицы поиска... Однако я считаю это более сложным, если вы работаете с изображениями RGB.

void reduceTo64Colors(IplImage *img, IplImage *img_quant) {
    int i,j;
    int height   = img->height;   
    int width    = img->width;    
    int step     = img->widthStep;

    uchar *data = (uchar *)img->imageData;
    int step2 = img_quant->widthStep;
    uchar *data2 = (uchar *)img_quant->imageData;

    for (i = 0; i < height ; i++)  {
        for (j = 0; j < width; j++)  {

          // operator XXXXXXXX & 11000000 equivalent to  XXXXXXXX AND 11000000 (=192)
          // operator 01000000 >> 2 is a 2-bit shift to the right = 00010000 
          uchar C1 = (data[i*step+j*3+0] & 192)>>2;
          uchar C2 = (data[i*step+j*3+1] & 192)>>4;
          uchar C3 = (data[i*step+j*3+2] & 192)>>6;

          data2[i*step2+j] = C1 | C2 | C3; // merges the 2 MSB of each channel
        }     
    }
}

Ответ 2

Этот вопрос был хорошо освещен в OpenCV 2 "Книга по программированию приложений для ПК" :

tp6vz.jpg

Глава 2 показывает несколько операций сокращения, один из которых продемонстрирован здесь на С++:

#include <iostream>
#include <vector>

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>


void colorReduce(cv::Mat& image, int div=64)
{    
    int nl = image.rows;                    // number of lines
    int nc = image.cols * image.channels(); // number of elements per line

    for (int j = 0; j < nl; j++)
    {
        // get the address of row j
        uchar* data = image.ptr<uchar>(j);

        for (int i = 0; i < nc; i++)
        {
            // process each pixel
            data[i] = data[i] / div * div + div / 2;
        }
    }
}

int main(int argc, char* argv[])
{   
    // Load input image (colored, 3-channel, BGR)
    cv::Mat input = cv::imread(argv[1]);
    if (input.empty())
    {
        std::cout << "!!! Failed imread()" << std::endl;
        return -1;
    } 

    colorReduce(input);

    cv::imshow("Color Reduction", input);   
    cv::imwrite("output.jpg", input);   
    cv::waitKey(0);

    return 0;
}

Ниже вы можете найти изображение ввода (слева) и вывод этой операции (справа):

XzFVo.jpg6iTTd.jpg

Ответ 3

Вы можете рассматривать K-средства, но в этом случае он, скорее всего, будет очень медленным. Лучший подход может сделать это "вручную" самостоятельно. Скажем, у вас есть изображение типа CV_8UC3, то есть изображение, где каждый пиксель представлен тремя значениями RGB от 0 до 255 (Vec3b). Вы можете "сопоставить" эти 256 значений только с 4 конкретными значениями, что даст 4 x 4 x 4= 64 возможные цвета.

У меня был набор данных, где мне нужно было убедиться, что dark = black, light = white и уменьшить количество цветов всего. Это то, что я сделал (С++):

inline uchar reduceVal(const uchar val)
{
    if (val < 64) return 0;
    if (val < 128) return 64;
    return 255;
}

void processColors(Mat& img)
{
    uchar* pixelPtr = img.data;
    for (int i = 0; i < img.rows; i++)
    {
        for (int j = 0; j < img.cols; j++)
        {
            const int pi = i*img.cols*3 + j*3;
            pixelPtr[pi + 0] = reduceVal(pixelPtr[pi + 0]); // B
            pixelPtr[pi + 1] = reduceVal(pixelPtr[pi + 1]); // G
            pixelPtr[pi + 2] = reduceVal(pixelPtr[pi + 2]); // R
        }
    }
}

в результате чего [0,64) станет 0, [64,128)64 и [128,255)255, получив 27 colors:

enter image description hereenter image description here

Для меня это кажется аккуратным, совершенно ясным и быстрым, чем что-либо еще, упомянутое в других ответах.

Вы также можете рассмотреть возможность уменьшения этих значений до одного из кратных некоторого числа, скажем:

inline uchar reduceVal(const uchar val)
{
    if (val < 192) return uchar(val / 64.0 + 0.5) * 64;
    return 255;
}

который даст набор из 5 возможных значений: {0, 64, 128, 192, 255}, т.е. 125 цветов.

Ответ 4

Существует алгоритм кластеризации K-mean, который уже доступен в библиотеке OpenCV. Короче говоря, он определяет, какие из них являются лучшими центроидами, вокруг которых кластеризуют ваши данные для пользовательского значения k (= нет кластеров). Таким образом, в вашем случае вы можете найти центроиды, вокруг которых будут группироваться ваши значения пикселей для заданного значения k = 64. Подробности там, если вы google вокруг. Здесь краткое введение в k-означает.

Что-то похожее на то, что вы, вероятно, пытаетесь, задали здесь на fooobar.com/questions/219090/... с помощью k-средств, надеюсь, что это поможет.

Другим подходом было бы использовать функцию функции среднего сдвига пирамиды в OpenCV. Это дает несколько "сплющенных" изображений, т.е. Количество цветов меньше, поэтому оно может помочь вам.

Ответ 5

Ответы, предлагаемые здесь, действительно хороши. Я думал, что добавлю свою идею. Я следую формулировке многих комментариев здесь, в которых говорится, что 64 цвета могут быть представлены 2 битами каждого канала в RGB-изображении.

Функция в коде ниже принимает в качестве входного изображения и количество бит, необходимое для квантования. Он использует бит-манипуляцию, чтобы "отбросить" бит LSB и сохранить только необходимое количество бит. Результатом является гибкий метод, который может квантовать изображение на любое количество бит.

#include "include\opencv\cv.h"
#include "include\opencv\highgui.h"

// quantize the image to numBits 
cv::Mat quantizeImage(const cv::Mat& inImage, int numBits)
{
    cv::Mat retImage = inImage.clone();

    uchar maskBit = 0xFF;

    // keep numBits as 1 and (8 - numBits) would be all 0 towards the right
    maskBit = maskBit << (8 - numBits);

    for(int j = 0; j < retImage.rows; j++)
        for(int i = 0; i < retImage.cols; i++)
        {
            cv::Vec3b valVec = retImage.at<cv::Vec3b>(j, i);
            valVec[0] = valVec[0] & maskBit;
            valVec[1] = valVec[1] & maskBit;
            valVec[2] = valVec[2] & maskBit;
            retImage.at<cv::Vec3b>(j, i) = valVec;
        }

        return retImage;
}


int main ()
{
    cv::Mat inImage;
    inImage = cv::imread("testImage.jpg");
    char buffer[30];
    for(int i = 1; i <= 8; i++)
    {
        cv::Mat quantizedImage = quantizeImage(inImage, i);
        sprintf(buffer, "%d Bit Image", i);
        cv::imshow(buffer, quantizedImage);

        sprintf(buffer, "%d Bit Image.png", i);
        cv::imwrite(buffer, quantizedImage);
    }

    cv::waitKey(0);
    return 0;
}

Вот изображение, которое используется в приведенном выше вызове функции:

введите описание изображения здесь

Изображение квантовано на 2 бита для каждого канала RGB (всего 64 цвета):

введите описание изображения здесь

3 бита для каждого канала:

введите описание изображения здесь

4 бит...

введите описание изображения здесь

Ответ 6

Предполагая, что вы хотите использовать те же самые 64 цвета для всех изображений (т.е. палитры не оптимизированы для каждого изображения), есть, по крайней мере, несколько вариантов выбора, которые я могу придумать:

1) Преобразуйте в Lab или YCrCb цветовое пространство и квантуйте, используя N бит для яркости и М бит для каждого цветового канала, N должно быть больше, чем M.

2) Вычислите трехмерную гистограмму значений цвета по всем вашим обучающим изображениям, затем выберите 64 цвета с наибольшими значениями бинов. Сделайте квантование ваших изображений, назначив каждому пикселю цвет ближайшего бина из учебного набора.

Метод 1 является наиболее общим и простым в реализации, тогда как метод 2 может быть лучше адаптирован к вашему конкретному набору данных.

Обновление: Например, 32 цвета - 5 бит, поэтому назначьте 3 бита каналу яркости и 1 бит для каждого цветового канала. Чтобы сделать это квантование, сделайте целочисленное деление канала яркости на 2 ^ 8/2 ^ 3 = 32 и каждый цветной канал на 2 ^ 8/2 ^ 1 = 128. Теперь есть только 8 различных значений яркости и 2 разных цветовых канала каждый. Рекомбинируйте эти значения в одно целое, выполнив битовую сдвиг или математику (квантованное значение цвета = яркость * 4 + color1 * 2 + color2);

Ответ 7

Почему бы вам просто не умножить/делить матрицу? Значения будут автоматически округлены.

псевдокод:

конвертировать ваши каналы в неподписанные символы (CV_8UC3),
Поделить на общие цвета/желаемые цвета. Mat = Mat/(256/64). Десятичные точки будут усечены.
Умножьте на тот же номер. Mat = mat * 4

Готово. Каждый канал теперь содержит только 64 цвета.