raspberry - ¿Cómo reducir el número de colores en una imagen con OpenCV?
segmentacion de color opencv (7)
¿Por qué no haces la multiplicación / división de Matrix? Los valores se redondearán automáticamente.
Pseudocódigo
convierte tus canales a caracteres sin firmar (CV_8UC3),
Divide por colores totales / colores deseados. Mat = Mat / (256/64). Los puntos decimales serán truncados.
Multiplica por el mismo número. Mat = mat * 4
Hecho. Cada canal ahora solo contiene 64 colores.
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 histograma de imágenes de tamaño 64. Estoy implementando técnicas CBIR
Lo que quiero es la cuantización del color en una paleta de 4 bits.
Este tema fue bien cubierto en el Libro de cocina de programación de aplicaciones de visión por ordenador OpenCV 2 :
El Capítulo 2 muestra algunas operaciones de reducción, una de ellas demostrada aquí en C ++:
#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;
}
A continuación puede encontrar la imagen de entrada (izquierda) y la salida de esta operación (derecha):
Existe el algoritmo de agrupamiento de 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 definido por el usuario de k (= no de clústeres). Entonces, en su caso, podría encontrar los centroides alrededor de los cuales agrupar sus valores de píxeles para un valor dado de k = 64. Los detalles están ahí si buscas en google. Here una breve introducción a k-means.
Algo similar a lo que probablemente está intentando se le preguntó aquí sobre SO usando k-means, espero que ayude.
Otro enfoque sería utilizar la función de filtro de cambio de media de pirámide en OpenCV. Produce imágenes un tanto "aplanadas", es decir, el número de colores es menor, por lo que podría ayudarlo.
Hay muchas maneras de hacerlo. Los métodos sugeridos por jeff7 están bien, pero algunos inconvenientes son:
- el método 1 tiene los parámetros N y M, que debe elegir, y también debe convertirlo en otro espacio de color.
- El método 2 contestado puede ser muy lento, ya que debe calcular un histograma de 16.7 Milion bins y clasificarlo 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 usar en un color RGB y convertirlo en una imagen de 64 colores. Si está utilizando C / OpenCV, puede usar algo como la siguiente función.
Si está trabajando con imágenes de nivel de gris, recomiendo usar la función LUT () de OpenCV 2.3, ya que es más rápido. Hay un tutorial sobre cómo usar la LUT para reducir la cantidad de colores. Consulte: Tutorial: Cómo escanear imágenes, tablas de búsqueda ... Sin embargo, me parece 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
}
}
}
Las respuestas sugeridas aquí son realmente buenas. Pensé que añadiría mi idea también. Sigo la formulación de muchos comentarios aquí, en los que se dice que 64 colores pueden representarse por 2 bits de cada canal en una imagen RGB.
La función en el código siguiente toma como entrada una imagen y el número de bits necesarios para la cuantificació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 cuantificar la imagen a cualquier número 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<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;
}
Aquí hay una imagen que se usa en la llamada a la función anterior:
Imagen cuantificada a 2 bits para cada canal RGB (Total de 64 colores):
3 bits para cada canal:
4 bits ...
Podría considerar las K-medias, pero en este caso lo más probable es que sea extremadamente lento. Un mejor enfoque podría ser hacerlo "manualmente" por su cuenta. Digamos que tiene una imagen del tipo CV_8UC3
, es decir, una imagen donde 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 daría como resultado 4 x 4 x 4
= 64
colores posibles.
He tenido un conjunto de datos, en el que necesitaba asegurarme de que dark = black, light = white y redujera la cantidad de colores de todo. 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
}
}
}
causando que [0,64)
convierta en 0
, [64,128)
-> 64
y [128,255)
-> 255
, produciendo 27
colores:
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 algún número, digamos:
inline uchar reduceVal(const uchar val)
{
if (val < 192) return uchar(val / 64.0 + 0.5) * 64;
return 255;
}
lo que daría un conjunto de 5 valores posibles: {0, 64, 128, 192, 255}
, es decir, 125 colores.
Suponiendo que desea utilizar los mismos 64 colores para todas las imágenes (es decir, la paleta no está optimizada por imagen), hay al menos un par de opciones que se me ocurren:
1) Convierta al espacio de color Lab o YCrCb y cuantifique utilizando N bits para la luminancia y M bits para cada canal de color, N debe ser mayor que M.
2) Calcule un histograma 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 asigne 3 bits al canal de luminancia y 1 bits a cada canal de color. Para hacer esta cuantización, haga una división entera del canal de luminancia por 2 ^ 8/2 ^ 3 = 32 y cada canal de color por 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 cambio de bit o matemática (valor de color cuantificado = luminancia * 4 + color1 * 2 + color2);