Soy bastante nuevo en el procesamiento de imágenes y Python, así que tengan paciencia conmigo

Estoy tratando de tomar una imagen grande (5632x2048) que es básicamente un mapa del mundo con provincias (extraído de Hearts of Iron 4), y cada provincia está coloreada con un valor RGB diferente, y colorearla con un conjunto de colores. cada uno correspondiente a un determinado país. Actualmente estoy usando este código

import numpy as np
import cv2
import sqlite3

dbPath = 'PATH TO DB'
dirPath = 'PATH TO IMAGE'

con = sqlite3.connect(dbPath)
cur = con.cursor()

im = cv2.imread(dirPath)

cur.execute('SELECT * FROM Provinces ORDER BY id')
provinceTable = cur.fetchall()

for line in provinceTable:
    input_rgb = [line[1], line[2], line[3]]
    if line[7] == None:
        output_rgb = [255,255,255]
    else:
        output_rgb = line[7].replace('[', '').replace(']','').split(',')
    im[np.all(im == (int(input_rgb[0]), int(input_rgb[1]), int(input_rgb[2])), axis=-1)] = (int(output_rgb[0]), int(output_rgb[1]), int(output_rgb[2]))

cv2.imwrite('result.png',im)

El problema con el que me encuentro es que es dolorosamente lento (50 minutos y no ha terminado), debido al hecho de que definitivamente estoy usando numpy mal al recorrerlo en bucle en lugar de vectorizarlo (un concepto que todavía soy nuevo ay no tengo idea de cómo hacerlo). Google tampoco ha sido de mucha ayuda.

¿Cuál es la mejor manera de hacer esto?

Editar: olvidé mencionar que la cantidad de valores que estoy reemplazando es bastante grande (~ 15000)

2
inconspicuoususername 13 mar. 2021 a las 20:25

3 respuestas

La mejor respuesta

Como mencioné en los comentarios, creo que querrá usar np.take(yourImage, LUT) donde LUT es una tabla de búsqueda.

Entonces, si crea una imagen ficticia con la misma forma que la suya:

import numpy as np

# Make a dummy image of 5632x2048 RGB values
im = np.random.randint(0,256,(5632,2048,3), np.uint8)

Eso será 34 MB. Ahora modifíquelo a un vector alto de valores RGB:

# Make image into a tall vector, as tall as necessary and 3 RGB values wide
v = im.reshape((-1,3))

Que tendrá la forma (11534336, 3) y luego aplanará eso a valores de 24 bits en lugar de tres valores de 8 bits con np.dot()

# Make into tall vector of shape 11534336x1 rather than 11534336x3
v24 = np.dot(v.astype(np.uint32),[1,256,65536])

Ahora tendrá un vector 1-D de valores de píxeles de 24 bits con forma (11534336,)

Ahora cree su tabla de búsqueda RGB (estoy haciendo las 2 ^ 24 entradas RGB aquí, es posible que necesite menos):

RGBLUT = np.zeros((2**24,3),np.uint8)

Y configure la LUT. Entonces, suponiendo que desee mapear todos los colores en la imagen original a gris medio (128) en la imagen de salida:

RGBLUT[:] = 128

Ahora haga lo mismo que hicimos con la imagen np.dot() para obtener una LUT con forma (2 24,1) en lugar de forma (2 24,3):

LUT24 = np.dot(RGBLUT.astype(np.uint32), [1,256,65536])

Luego haga la búsqueda real en la tabla:

result = np.take(LUT24, v24)

En mi Mac, eso toma 334ms para su imagen de 5632x2048.


Luego, cambie la forma y conviértala nuevamente en tres valores de 8 bits cambiando y haciendo Y para deshacer el efecto de np.dot().

Actualmente no estoy en condiciones de probar el reensamblaje, pero se verá más o menos así:

BlueChannel   = result & 0xff         # Blue channel is bottom 8 bits
GreenChannel  = (result>>8)  &0 xff   # Green channel is middle 8 bits
RedChannel    = (result>>16) &0 xff   # Red channel is top 8 bits

Ahora combine esos tres canales individuales en una imagen de 3 canales:

RGB = np.dstack(RedChannel, GreenChannel, BlueChannel))

Y vuelva a dar forma desde el vector alto a las dimensiones de la imagen original:

RGB = RGB.reshape(im.shape)

En lo que respecta a la configuración de la LUT, a algo más interesante que el gris medio, si desea mapear, por ejemplo, naranja, es decir, rgb (255,128,0) a magenta, es decir, rgb (255,0,255), haría algo como:

LUT[np.dot([255,128,0],[1,256,65536])] = [255,0,255]  # map orange to magenta
LUT[np.dot([255,255,255],[1,256,65536])] = [0,0,0]  # map white to black
LUT[np.dot([0,0,0],[1,256,65536])] = [255,255,255]  # map black to white

Palabras clave : Python, procesamiento de imágenes, LUT, LUT RGB LUT de 24 bits, tabla de búsqueda.

0
Mark Setchell 14 mar. 2021 a las 18:25

Primero puede crear una máscara de la imagen y usarla para reemplazar los colores. Es probable que exista una forma pura y simple de hacer esto que sea más rápida, pero no lo sé.

Este código tarda ~ 0,5 segundos en ejecutarse. Debería esperar que tome alrededor de medio segundo para cada reemplazo de color.

import cv2
import numpy as np
import time

# make image
res = (5632, 2048, 3);
img = np.zeros(res, np.uint8);

# change black to white
black = (0,0,0);
white = (255,255,255);

# make a mask
start_time = time.time();
mask = cv2.inRange(img, black, black);
print("Mask Time: " + str(time.time() - start_time));

# replace color
start_time = time.time();
img[mask == 255] = white;
print("Replace Time: " + str(time.time() - start_time));

En términos de su código, se verá así

for line in provinceTable:
    input_rgb = [line[1], line[2], line[3]]
    input_rgb = (int(input_rgb[0]), int(input_rgb[1]), int(input_rgb[2]))
    if line[7] == None:
        output_rgb = (255,255,255)
    else:
        output_rgb = line[7].replace('[', '').replace(']','').split(',')
        output_rgb = (int(output_rgb[0]), int(output_rgb[1]), int(output_rgb[2]))
    mask = cv2.inRange(im, input_rgb, input_rgb)
    im[mask == 255] = output_rgb
0
Ian Chu 13 mar. 2021 a las 17:45

Aquí hay una forma de hacerlo usando Numpy y Python / OpenCV. Aquí cambio de rojo a verde.

Entrada:

enter image description here

import cv2
import numpy as np

# load image
img = cv2.imread('test_red.png')

# change color
result = img.copy()
result[np.where((result==[0,0,255]).all(axis=2))] = [0,255,0]

# save output
cv2.imwrite('test_green.png', result)

# Display various images to see the steps
cv2.imshow('result',result)
cv2.waitKey(0)
cv2.destroyAllWindows()

Resultado:

enter image description here

0
fmw42 13 mar. 2021 a las 18:09