¿Cómo reducir el número de colores en una imagen con OpenCV?

Tengo un conjunto de archivos de imagen, y quiero reducir el número de colores de ellos a 64. ¿Cómo puedo hacer esto con OpenCV?

Necesito esto para poder trabajar con un histogtwig de imagen de 64 dimensiones. Estoy implementando técnicas CBIR

Lo que quiero es la cuantización del color a una paleta de 4 bits.

Hay muchas formas de hacerlo. Los métodos sugeridos por jeff7 son correctos, pero algunos inconvenientes son:

  • el método 1 tiene los parámetros N y M, que debe elegir, y también debe convertirlo a otro espacio de color.
  • El método 2 que se responde puede ser muy lento, ya que debe calcular un histogtwig de 16.7 Milion Bins y ordenarlo por frecuencia (para obtener los 64 valores de frecuencia más altos)

Me gusta usar un algoritmo basado en los bits más significativos para utilizar en un color RGB y convertirlo en una imagen de 64 colores. Si está utilizando C / OpenCV, puede usar algo como la función a continuación.

Si está trabajando con imágenes de nivel de grises, recomiendo usar la función LUT () de OpenCV 2.3, ya que es más rápido. Hay un tutorial sobre cómo usar LUT para reducir la cantidad de colores. Consulte: Tutorial: Cómo escanear imágenes, tablas de búsqueda … Sin embargo, me resulta más complicado si está trabajando con imágenes 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 } } } 

Puede considerar K-means, sin embargo, en este caso, es muy probable que sea extremadamente lento. Un mejor enfoque podría estar haciendo esto “manualmente” por su cuenta. Supongamos que tiene una imagen del tipo CV_8UC3 , es decir, una imagen en la que cada píxel está representado por 3 valores RGB de 0 a 255 ( Vec3b ). Puede “asignar” estos 256 valores a solo 4 valores específicos, lo que arrojaría 4 x 4 x 4 = 64 colores posibles.

He tenido un conjunto de datos, donde necesitaba asegurarme de que era oscuro = negro, claro = blanco y reducía la cantidad de colores de todo lo demás. Esto es lo que hice (C ++):

 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 } } } 

haciendo que [0,64) convierta en 0 , [64,128) -> 64 y [128,255) -> 255 , produciendo 27 colores:

enter image description hereenter image description here

Para mí, esto parece ser claro, perfectamente claro y más rápido que cualquier otra cosa mencionada en otras respuestas.

También podría considerar reducir estos valores a uno de los múltiplos de un número, digamos:

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

lo que produciría un conjunto de 5 valores posibles: {0, 64, 128, 192, 255} , es decir, 125 colores.

Este tema fue bien cubierto en OpenCV 2 Computer Vision Application Programming Cookbook :

El Capítulo 2 muestra algunas operaciones de reducción, una de ellas demostrada aquí en C ++:

 #include  #include  #include  #include  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(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; } 

A continuación puede encontrar la imagen de entrada (izquierda) y la salida de esta operación (derecha):

Existe el algoritmo de agrupación K-means que ya está disponible en la biblioteca OpenCV. En resumen, determina cuáles son los mejores centroides alrededor de los cuales agrupar sus datos para un valor de k definido por el usuario (= no de clusters). Entonces, en su caso podría encontrar los centroides alrededor de los cuales agrupar sus valores de píxel para un valor dado de k = 64. Los detalles están ahí si buscas en Google. Aquí hay una breve introducción a k-means.

Algo similar a lo que probablemente estés intentando fue preguntado aquí en SO usando k-means, espero que ayude.

Otro enfoque sería utilizar la función de filtro de cambio de medias de la pirámide en OpenCV. Produce imágenes algo “aplanadas”, es decir, la cantidad de colores es menor, por lo que podría ayudarte.

Las respuestas sugeridas aquí son realmente buenas. Pensé que agregaría mi idea también. Sigo la formulación de muchos comentarios aquí, en los que se dice que 64 colores pueden representarse con 2 bits de cada canal en una imagen RGB.

La función en el código a continuación toma como entrada una imagen y el número de bits requeridos para la cuantización. Utiliza la manipulación de bits para ‘soltar’ los bits LSB y mantener solo el número requerido de bits. El resultado es un método flexible que puede cuantizar la imagen en cualquier cantidad de bits.

 #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(j, i); valVec[0] = valVec[0] & maskBit; valVec[1] = valVec[1] & maskBit; valVec[2] = valVec[2] & maskBit; retImage.at(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; } 

Aquí hay una imagen que se utiliza en la llamada a la función anterior:

enter image description here

Imagen cuantizada en 2 bits para cada canal RGB (Total 64 colores):

enter image description here

3 bits para cada canal:

enter image description here

4 bits ...

enter image description here

Suponiendo que quiera usar los mismos 64 colores para todas las imágenes (es decir, paleta no optimizada por imagen), hay al menos un par de opciones que puedo pensar:

1) Convierte a espacio de color Lab o YCrCb y cuantifica usando N bits para la luminancia y M bits para cada canal de color, N debe ser mayor que M.

2) Calcule un histogtwig 3D de valores de color sobre todas sus imágenes de entrenamiento, luego elija los 64 colores con los valores de bin más grandes. Cuantifique sus imágenes asignando a cada píxel el color del contenedor más cercano del conjunto de entrenamiento.

El Método 1 es el más genérico y el más fácil de implementar, mientras que el Método 2 se puede adaptar mejor a su conjunto de datos específico.

Actualización: por ejemplo, 32 colores son 5 bits, así que asigna 3 bits al canal de luminancia y 1 bit a cada canal de color. Para hacer esta cuantización, haga la división entera del canal de luminancia en 2 ^ 8/2 ^ 3 = 32 y cada canal de color en 2 ^ 8/2 ^ 1 = 128. Ahora solo hay 8 valores de luminancia diferentes y 2 canales de color diferentes cada. Recombine estos valores en un solo entero haciendo desplazamiento de bits o matemática (valor de color cuantificado = luminancia * 4 + color1 * 2 + color2);

¿Por qué no haces la multiplicación / división de Matrix? Los valores se redondearán automáticamente.

Pseudocódigo:

convierta sus canales a caracteres sin firmar (CV_8UC3),
Divida por el total de colores / colores deseados. Mat = Mat / (256/64). Los puntos decimales se truncarán.
Multiplica por el mismo número. Mat = mat * 4

Hecho. Cada canal ahora solo contiene 64 colores.

    Intereting Posts