Star

Created With

linkProblema

Introducir el análisis de imágenes/video al implementar: Conversión de la imagen a un foto-mosaico.

linkInformación

linkFoto-Mosaico

En el campo de las imágenes y la fotografía, un fotomosaico es una imagen usualmente una fotografía que ha sido dividida en secciones rectangulares (usualmente del mismo tamaño), tal como es compuesto un mosaico tradicional, con la característica de que cada elemento del mosaico es reemplazado por otra fotografía con colores promedios apropiados al elemento de la imagen original. Cuando es vista en detalle, los píxeles individuales se ven como la imagen principal, sin embargo al verla como un todo, es posible apreciar que la imagen está compuesta por cientos de miles de imágenes.

linkEspacio de color Lab

El CIE Lab* (CIELAB) es el modelo cromático usado normalmente para describir todos los colores que puede percibir el ojo humano. Fue desarrollado específicamente con este propósito por la Commission Internationale d'Eclairage (Comisión Internacional de la Iluminación), razón por la cual se abrevia CIE. Los asteriscos () que siguen a cada letra forman parte del nombre, ya que representan L, a* y b*, de L, a y b.

Los tres parámetros en el modelo representan la luminosidad de color (L, L=0 rendimientos negro y L=100 indica blanca), su posición entre rojo y verde (a, valores negativos indican verde mientras valores positivos indican rojo) y su posición entre amarillo y azul (b*, valores negativos indican azul y valores positivos indican amarillo).

linkSolución

linkProceso

El proceso de creación del mosaico está dividido en 2 fases, la primera es una fase previa de creación del dataset de imágenes utilizadas en el mosaico y la segunda es el proceso de reemplazo de los píxeles de la imagen por otras imágenes que aproximen el color de cada píxel.

Para la primera parte se utilizó un script en Python para descargar imágenes aleatorias de una API publica llamada Pixabay en la cual se permite utilizar un filtro por el color de la imagen, el script hace una serie de peticiones a la API (una por cada color) solicitando un numero determinado de imágenes del color deseado, luego toma las imágenes y les cambia el tamaño para que todas sean imágenes cuadradas, por ultimo se toma el color dominante de cada imagen y se concatenan verticalmente formando una sola imagen diccionario, y para indexar el diccionario se crea simultaneamente una imagen en la que cada píxel es el color dominante de la imagen correspondiente (Esto se hace para poder acceder las imágenes a partir de su color dominante).

La segunda parte del proceso se hace en tiempo real, se cargan la imagen diccionario, la imagen que indexa al diccionario y la imagen que se va a transformar y se envían como variables uniformes al fragment shader, este se encarga de que por cada sección (definida por la resolución) de la imagen original se reemplace por la correspondiente imagen del diccionario, teniendo como color representativo el primer píxel de la región (píxel de la esquina superior izquierda), se busca en el index de colores el color mas cercano utilizando el espacio de color Lab y a partir de este se busca la imagen correspondiente en el diccionario de imágenes.

linkResultado

linkCodigo

1linklet mosaicImages

2linklet indexImages

3linklet theShader

4linklet img

5link

6linkfunction preload(){

7link theShader = loadShader('/vc/docs/sketches/shader.vert', '/vc/docs/sketches/mosaicShader.frag')

8link img = loadImage('/vc/docs/sketches/lenna.png')

9link mosaicImages = loadImage('/vc/docs/sketches/mosaicImages.jpg')

10link indexImages = loadImage('/vc/docs/sketches/indexImage.jpg')

11link}

12link

13linkfunction setup(){

14link createCanvas(512, 512,WEBGL);

15link noStroke();

16link textureMode(NORMAL);

17link shader(theShader);

18link

19link theShader.setUniform('image', img)

20link theShader.setUniform('mosaicImages', mosaicImages)

21link theShader.setUniform('indexImages', indexImages)

22link theShader.setUniform('resolution', 20)

23link

24link

25link slider = createSlider(3, 512, 100, 2);

26link slider.position(150, 552);

27link slider.style('width', '40%');

28link slider.input(() => {

29link theShader.setUniform('resolution', slider.value())

30link redraw()

31link })

32link let div = createDiv('Resolution');

33link div.style('font-size', '18px');

34link div.position(220, 532);

35link}

36link

37linkfunction draw() {

38link background(0);

39link beginShape();

40link vertex(-width/2, -height/2,0,0,0);

41link vertex(width/2, -height/2,0,1,0);

42link vertex(width/2, height/2,0,1,1);

43link vertex(-width/2, height/2,0,0,1);

44link endShape(CLOSE);

45link

46link noLoop();

47link}

1linkimport json

2linkimport requests

3linkimport shutil

4linkfrom os import listdir

5linkfrom os.path import isfile, join

6linkfrom colorthief import ColorThief

7linkfrom tqdm import tqdm

8linkimport cv2

9linkimport tempfile

10linkimport numpy as np

11link

12linkdef get_files(dir):

13link return [f for f in listdir(dir) if isfile(join(dir, f))]

14link

15linkdef resize_images(folder, destination_folder):

16link img_list = [(destination_folder + file, cv2.imread(folder + file)) for file in get_files(folder)]

17link

18link minh = min([img.shape[0] for img_name, img in img_list])

19link minw = min([img.shape[1] for img_name, img in img_list])

20link dim = min(minh, minw)

21link

22link for img_name, img in tqdm(img_list):

23link resized = cv2.resize(img, (dim, dim), interpolation = cv2.INTER_AREA)

24link cv2.imwrite(img_name, resized)

25link

26linkdef concat_images(folder):

27link img_list = []

28link file_list = get_files(folder)

29link indexImage = np.zeros((len(file_list) * 44, 44, 3), np.uint8)

30link for i, file in tqdm(enumerate(file_list)):

31link color_thief = ColorThief(folder + file)

32link r,g,b = color_thief.get_color(quality=1)

33link indexImage[i*44: (i+1)*44, :, 0] = b

34link indexImage[i*44: (i+1)*44, :, 1] = g

35link indexImage[i*44: (i+1)*44, :, 2] = r

36link img = cv2.imread(folder + file)

37link img_list.append(img)

38link

39link result_img = cv2.vconcat(img_list)

40link cv2.imwrite('result.jpg',result_img)

41link cv2.imwrite('indexImage.jpg', indexImage)

42link

43linkdef download_images(folder):

44link key = "20977986-3000fea631f919184c7949341"

45link image_type = "photo"

46link per_page = 200

47link page = 1

48link orientation = "horizontal"

49link base_url = f'https://pixabay.com/api/?key={key}&image_type={image_type}&per_page={per_page}&page={page}&orientation={orientation}&> > colors='

50link

51link colors = ["grayscale", "transparent", "red", "orange", "yellow", "green", "turquoise", "blue", "lilac", "pink", "white", "gray", > > "black", "brown" ]

52link for color in colors:

53link URL = base_url + color

54link res = requests.get(URL)

55link res_data = json.loads(res.text)

56link for image in tqdm(res_data['hits']):

57link # save the image

58link url = image['webformatURL']

59link name = folder + str(image['id']) + '.jpg'

60link r = requests.get(url, stream=True)

61link if r.status_code == 200:

62link with open(name, 'wb') as f:

63link r.raw.decode_content = True

64link shutil.copyfileobj(r.raw, f)

65link

66link

67linkdd = tempfile.TemporaryDirectory()

68linkrd = tempfile.TemporaryDirectory()

69linkfd = tempfile.TemporaryDirectory()

70link

71linkdownload_images(dd.name + '/')

72linkresize_images(dd.name + '/', rd.name + '/')

73linkconcat_images(rd.name + '/')

74link

75linkrd.cleanup()

76linkfd.cleanup()

linkShaders

1linkprecision highp float;

2link

3linkuniform sampler2D image;

4linkuniform sampler2D mosaicImages;

5linkuniform sampler2D indexImages;

6linkuniform float resolution;

7link

8linkvarying vec2 vTexCoord;

9link

10linkvec3 rgb2xyz( vec3 c ) {

11link vec3 tmp;

12link tmp.x = ( c.r > 0.04045 ) ? pow( ( c.r + 0.055 ) / 1.055, 2.4 ) : c.r / 12.92;

13link tmp.y = ( c.g > 0.04045 ) ? pow( ( c.g + 0.055 ) / 1.055, 2.4 ) : c.g / 12.92,

14link tmp.z = ( c.b > 0.04045 ) ? pow( ( c.b + 0.055 ) / 1.055, 2.4 ) : c.b / 12.92;

15link const mat3 mat = mat3(

16link 0.4124, 0.3576, 0.1805,

17link 0.2126, 0.7152, 0.0722,

18link 0.0193, 0.1192, 0.9505

19link );

20link return 100.0 * tmp * mat;

21link}

22link

23linkvec3 xyz2lab( vec3 c ) {

24link vec3 n = c / vec3(95.047, 100, 108.883);

25link vec3 v;

26link v.x = ( n.x > 0.008856 ) ? pow( n.x, 1.0 / 3.0 ) : ( 7.787 * n.x ) + ( 16.0 / 116.0 );

27link v.y = ( n.y > 0.008856 ) ? pow( n.y, 1.0 / 3.0 ) : ( 7.787 * n.y ) + ( 16.0 / 116.0 );

28link v.z = ( n.z > 0.008856 ) ? pow( n.z, 1.0 / 3.0 ) : ( 7.787 * n.z ) + ( 16.0 / 116.0 );

29link return vec3(( 116.0 * v.y ) - 16.0, 500.0 * ( v.x - v.y ), 200.0 * ( v.y - v.z ));

30link}

31link

32linkvec3 rgb2lab( vec3 c ) {

33link vec3 lab = xyz2lab( rgb2xyz( c ) );

34link return vec3( lab.x / 100.0, 0.5 + 0.5 * ( lab.y / 127.0 ), 0.5 + 0.5 * ( lab.z / 127.0 ));

35link}

36link

37linkvec3 lab2xyz( vec3 c ) {

38link float fy = ( c.x + 16.0 ) / 116.0;

39link float fx = c.y / 500.0 + fy;

40link float fz = fy - c.z / 200.0;

41link return vec3(

42link 95.047 * (( fx > 0.206897 ) ? fx * fx * fx : ( fx - 16.0 / 116.0 ) / 7.787),

43link 100.000 * (( fy > 0.206897 ) ? fy * fy * fy : ( fy - 16.0 / 116.0 ) / 7.787),

44link 108.883 * (( fz > 0.206897 ) ? fz * fz * fz : ( fz - 16.0 / 116.0 ) / 7.787)

45link );

46link}

47link

48linkvec3 xyz2rgb( vec3 c ) {

49link const mat3 mat = mat3(

50link 3.2406, -1.5372, -0.4986,

51link -0.9689, 1.8758, 0.0415,

52link 0.0557, -0.2040, 1.0570

53link );

54link vec3 v = (c / vec3(100.0)) * mat;

55link vec3 r;

56link r.x = ( v.r > 0.0031308 ) ? (( 1.055 * pow( v.r, ( 1.0 / 2.4 ))) - 0.055 ) : 12.92 * v.r;

57link r.y = ( v.g > 0.0031308 ) ? (( 1.055 * pow( v.g, ( 1.0 / 2.4 ))) - 0.055 ) : 12.92 * v.g;

58link r.z = ( v.b > 0.0031308 ) ? (( 1.055 * pow( v.b, ( 1.0 / 2.4 ))) - 0.055 ) : 12.92 * v.b;

59link return r;

60link}

61link

62linkvec3 lab2rgb( vec3 c ) {

63link return xyz2rgb( lab2xyz( vec3(100.0 * c.x, 2.0 * 127.0 * (c.y - 0.5), 2.0 * 127.0 * (c.z - 0.5)) ) );

64link}

65link

66linkint getClosestColor(vec4 indexColor){

67link int closest = 0;

68link float minDiff = distance(vec4(1.0), vec4(0.0));

69link

70link vec3 indexColorLab = rgb2lab(vec3(indexColor.r, indexColor.g, indexColor.b));

71link

72link for (int i = 0; i < 276; i++) {

73link vec4 color = texture2D(indexImages, vec2(float(i) + 0.5) / vec2(1.0, 276.0));

74link vec3 colorLab = rgb2lab(vec3(color.r, color.g, color.b));

75link float diff = distance(indexColorLab, colorLab);

76link if(diff <= minDiff) {

77link minDiff = diff;

78link closest = i;

79link }

80link }

81link

82link return closest;

83link}

84link

85link

86linkvoid main() {

87link

88link vec2 symbolCoord = vTexCoord * resolution;

89link vec2 imageCoord = floor(symbolCoord);

90link

91link symbolCoord = (symbolCoord - imageCoord) / vec2(1.0, 276.0);

92link

93link imageCoord = imageCoord / vec2(resolution);

94link

95link vec4 indexColor = texture2D(image, imageCoord);

96link

97link int closest = getClosestColor(indexColor);

98link

99link float imageHeight = (float(closest)) / 276.0;

100link

101link gl_FragColor = texture2D(mosaicImages, symbolCoord + vec2(0.0, imageHeight));

102link}

1linkprecision highp float;

2link

3linkattribute vec3 aPosition;

4link

5linkattribute vec2 aTexCoord;

6link

7linkattribute vec4 aVertexColor;

8link

9linkuniform mat4 uProjectionMatrix;

10link

11linkuniform mat4 uModelViewMatrix;

12link

13linkvarying vec4 vVertexColor;

14link

15linkvarying vec2 vTexCoord;

16link

17linkvoid main() {

18link vVertexColor = aVertexColor;

19link vTexCoord = aTexCoord;

20link gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aPosition, 1.0);

21link}

linkFuentes

ProblemaInformaciónFoto-MosaicoEspacio de color LabSoluciónProcesoResultadoCodigoShadersFuentes

Home

Workshopschevron_right
P5 Code Snippetschevron_right
Teamchevron_right
Deliverieschevron_right
Workshop Softwarechevron_right
Workshop Hardwarechevron_right
Workshop Renderingchevron_right