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 principalmente 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 como tal de reemplazo de los pixeles de la imagen por otras imágenes que aproximen el color de cada pixel.

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 funcionamiento del script es simple, hace una serie de peticiones a la API (una por cada color) solicitando 20 imágenes del color deseado, luego toma las imágenes de vista previa (una imagen de menor resolución) y las almacena con el código hexadecimal del color dominante de las imágenes, en total se descargaron 277 imágenes.

Una vez ya se tiene el dataset de imágenes listo, viene la segunda parte, en esta fase se lee la imagen que se desea transformar a mosaico y se le baja la resolución por cuestión de rendimiento, una vez tenga una resolución baja se reemplaza cada pixel de la imagen por la imagen del dataset con el color dominante más aproximado al color del pixel, para esto se utilizo la representación CIELAB del color, debido a que esta permite hallar correctamente la "distancia" entre 2 colores.

linkImagen original

linkMosaico

1linklet img, images;

2linklet pixel_size;

3linklet newImage = {pixels: [], height: 0, width: 0}

4linklet files = ["043431.jpg","060505.jpg","061709.jpg","062627.jpg","070504.jpg","07090d.jpg","090605.jpg","09090b.jpg","0b0806.jpg","0b2b36.jpg","0c0d0e.jpg","0e0d0c.jpg","0e3947.jpg","0f0e12.jpg","100f0d.jpg","101010.jpg","103a0e.jpg","121812.jpg","160e10.jpg","163c3a.jpg","182d2c.jpg","1d2318.jpg","222327.jpg","238e92.jpg","25bbbc.jpg","262626.jpg","272c26.jpg","274141.jpg","274c3a.jpg","282124.jpg","2a2a2a.jpg","2a5e4a.jpg","2f3841.jpg","2f6a89.jpg","303b3f.jpg","303c1e.jpg","31233f.jpg","31aa72.jpg","331f44.jpg","343737.jpg","3572d2.jpg","36312f.jpg","368785.jpg","383a3f.jpg","3a2b20.jpg","3a4324.jpg","3c3c3c.jpg","3e3320.jpg","3e8163.jpg","3f3f3f.jpg","3f511f.jpg","40576e.jpg","405d72.jpg","41413b.jpg","414141.jpg","434343.jpg","453f38.jpg","473328.jpg","474735.jpg","476695.jpg","476931.jpg","484d57.jpg","49652e.jpg","4a2c54.jpg","4b4b4b.jpg","4c296d.jpg","4c4c4c.jpg","4d5460.jpg","4d5b17.jpg","4e3f34.jpg","4e563d.jpg","4f2e27.jpg","506e33.jpg","515046.jpg","515f66.jpg","51817a.jpg","524122.jpg","525a45.jpg","55423d.jpg","584f5b.jpg","599b9d.jpg","5a6345.jpg","5b5650.jpg","5b8db8.jpg","5c783b.jpg","5f673f.jpg","60c1e3.jpg","614f42.jpg","616366.jpg","657546.jpg","685435.jpg","685941.jpg","685f56.jpg","68676d.jpg","68809e.jpg","699853.jpg","6ac2e0.jpg","6b5b4b.jpg","6b5b76.jpg","6c4b55.jpg","6d726a.jpg","6e7846.jpg","6e7e8e.jpg","6eadca.jpg","6f18cc.jpg","6f34b7.jpg","6f588a.jpg","6f6f6f.jpg","6f7172.jpg","6fc1b9.jpg","7090a1.jpg","726f65.jpg","747474.jpg","775885.jpg","775c96.jpg","77804f.jpg","77c2c2.jpg","785b89.jpg","787878.jpg","7b6958.jpg","7c7374.jpg","7d6953.jpg","7ed47e.jpg","818a87.jpg","8197b1.jpg","835330.jpg","83aeab.jpg","855bbb.jpg","888988.jpg","89573c.jpg","8a6d69.jpg","8b8787.jpg","8d4d6c.jpg","8d69cd.jpg","8d8a8a.jpg","8eaca3.jpg","908e8d.jpg","939393.jpg","94898a.jpg","955a41.jpg","9d948f.jpg","9e713c.jpg","9f9d9b.jpg","a03638.jpg","a0a7a1.jpg","a0bdbc.jpg","a3688b.jpg","a44543.jpg","a6262e.jpg","a69075.jpg","a7908a.jpg","a81c48.jpg","a8b4b9.jpg","a9528f.jpg","aa0e0b.jpg","aaaaaa.jpg","ac8ac2.jpg","accbce.jpg","af6118.jpg","afafaf.jpg","b17bcd.jpg","b24e78.jpg","b461a0.jpg","b682a3.jpg","b6b7b8.jpg","b82825.jpg","b8b8b8.jpg","b8c1c0.jpg","b92825.jpg","ba723b.jpg","ba9dbd.jpg","baa388.jpg","bababa.jpg","bc7298.jpg","be5e77.jpg","beadb9.jpg","bfbfbf.jpg","c06c80.jpg","c08b3f.jpg","c11010.jpg","c2addb.jpg","c3396d.jpg","c49766.jpg","c4b348.jpg","c51f29.jpg","c5b148.jpg","c696b1.jpg","c6b6bb.jpg","c6d0da.jpg","c8b9a7.jpg","c9c9c9.jpg","ca2031.jpg","cb2b21.jpg","cbbb4e.jpg","ccd9d9.jpg","cd6664.jpg","ce9fb1.jpg","cf4837.jpg","cfa1f2.jpg","d07065.jpg","d13a3a.jpg","d2d3d0.jpg","d2d7df.jpg","d34d4b.jpg","d393b7.jpg","d48b1d.jpg","d56457.jpg","d5cec2.jpg","d62428.jpg","d677ed.jpg","d79828.jpg","d7c2b9.jpg","d886a0.jpg","d8afca.jpg","d8cab2.jpg","d8d2d2.jpg","d8dce6.jpg","da448e.jpg","dacdd4.jpg","dad6d9.jpg","db8704.jpg","dbc1d8.jpg","dc9437.jpg","dcb7ce.jpg","dd9ecc.jpg","dddcdc.jpg","de4446.jpg","dea06b.jpg","dea458.jpg","dedede.jpg","dfb383.jpg","e1ce4b.jpg","e1d5c1.jpg","e1ded9.jpg","e4d233.jpg","e4dfd9.jpg","e5e5e8.jpg","e6cabd.jpg","e6d9da.jpg","e8c549.jpg","e8c641.jpg","e8d18a.jpg","e8dcd6.jpg","e9b44c.jpg","e9e8e9.jpg","ea8531.jpg","eabfcd.jpg","eac00b.jpg","eadb0c.jpg","eae12c.jpg","eae77f.jpg","ebc497.jpg","ebe7e1.jpg","ec8e15.jpg","edae29.jpg","edca79.jpg","ede8e1.jpg","eee8c4.jpg","efd83b.jpg","efde3a.jpg","f0bd0d.jpg","f0f7fa.jpg","f13a40.jpg","f19d2a.jpg","f1e6bf.jpg","f4bd38.jpg","f5f2ea.jpg","f697b9.jpg","f7c038.jpg","f7c604.jpg","f88e3f.jpg","f8c13a.jpg","f8ebc6.jpg","f9c941.jpg","fac43a.jpg","faeab6.jpg","fbdc04.jpg"]

5linklet RGBtoXYZ_RtoX = [];

6linklet RGBtoXYZ_GtoX = [];

7linklet RGBtoXYZ_BtoX = [];

8linklet RGBtoXYZ_RtoY = [];

9linklet RGBtoXYZ_GtoY = [];

10linklet RGBtoXYZ_BtoY = [];

11linklet RGBtoXYZ_RtoZ = [];

12linklet RGBtoXYZ_GtoZ = [];

13linklet RGBtoXYZ_BtoZ = [];

14linklet slider;

15link

16linkfunction preload() {

17link // TODO: find a way to use fs library :|

18link for(var i = 0; i < 256; i++){ //i from 0 to 255

19link r = parseFloat(i/255) ; //r from 0 to 1

20link

21link if (r > 0.04045 )

22link r = Math.pow((r+0.055)/1.055 , 2.4);

23link else

24link r = r/12.92;

25link

26link r = r * 100

27link

28link var ref_X = 95.047;

29link var ref_Y = 100.000;

30link var ref_Z = 108.883;

31link

32link RGBtoXYZ_RtoX[i] = r * 0.4124/ref_X;

33link RGBtoXYZ_GtoX[i] = r * 0.3576/ref_X;

34link RGBtoXYZ_BtoX[i] = r * 0.1805/ref_X;

35link RGBtoXYZ_RtoY[i] = r * 0.2126/ref_Y;

36link RGBtoXYZ_GtoY[i] = r * 0.7152/ref_Y;

37link RGBtoXYZ_BtoY[i] = r * 0.0722/ref_Y;

38link RGBtoXYZ_RtoZ[i] = r * 0.0193/ref_Z;

39link RGBtoXYZ_GtoZ[i] = r * 0.1192/ref_Z;

40link RGBtoXYZ_BtoZ[i] = r * 0.9505/ref_Z;

41link }

42link images = {}

43link for (var t = 0; t < files.length; t++) {

44link const file = files[t];

45link images[file] = loadImage("/vc/docs/sketches/assets/mosaic_images/" + file)

46link }

47link img = loadImage("/vc/docs/sketches/lenna.png")

48link}

49link

50linkfunction setup() {

51link // Slider to control pixel_size

52link slider = createSlider(0, 8, 1, 1);

53link pixel_size = Math.pow(2, slider.value())

54link slider.position(150, 552);

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

56link slider.input(() => {

57link pixel_size = Math.pow(2, slider.value())

58link newImage = preProcessImage()

59link for (var k = 0; k < newImage.pixels.length; k++) {

60link const [R,G,B] = newImage.pixels[k];

61link const filename = getClosestColor(R,G,B)

62link image(images[filename], (k % newImage.width) *pixel_size, int(k / newImage.height) *pixel_size, pixel_size, pixel_size);

63link }

64link redraw()

65link })

66link let div = createDiv('Pixel size:');

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

68link div.position(220, 532);

69link

70link createCanvas(512, 562)

71link newImage = preProcessImage()

72link for (var k = 0; k < newImage.pixels.length; k++) {

73link const [R,G,B] = newImage.pixels[k];

74link const filename = getClosestColor(R,G,B)

75link image(images[filename], (k % newImage.width) *pixel_size, int(k / newImage.height) *pixel_size, pixel_size, pixel_size);

76link }

77link}

78link

79linkfunction draw() {

80link noLoop()

81link}

82link

83linkfunction preProcessImage(){

84link var new_pixels = []

85link pixelDensity(1)

86link img.loadPixels()

87link

88link for (let h = 0; h < img.height; h+=pixel_size) {

89link for (let w = 0; w < img.width; w+=pixel_size) {

90link new_pixels.push([0,0,0])

91link

92link for (let j = 0; j < pixel_size; j++) {

93link const wihi = 4 * ((h + j) * img.width + w)

94link const wfhi = 4 * ((h + j) * img.width + (w + pixel_size))

95link

96link for (let i = wihi; i < wfhi; i+=4) {

97link const [R,G,B] = [img.pixels[i], img.pixels[i + 1], img.pixels[i + 2]] // get pixel rgb

98link new_pixels[new_pixels.length -1][0] = new_pixels[new_pixels.length -1][0] + R

99link new_pixels[new_pixels.length -1][1] = new_pixels[new_pixels.length -1][1] + G

100link new_pixels[new_pixels.length -1][2] = new_pixels[new_pixels.length -1][2] + B

101link }

102link }

103link

104link new_pixels[new_pixels.length -1][0] = int(new_pixels[new_pixels.length -1][0] / (pixel_size * pixel_size))

105link new_pixels[new_pixels.length -1][1] = int(new_pixels[new_pixels.length -1][1] / (pixel_size * pixel_size))

106link new_pixels[new_pixels.length -1][2] = int(new_pixels[new_pixels.length -1][2] / (pixel_size * pixel_size))

107link }

108link }

109link return {pixels:new_pixels, height: img.height/pixel_size, width: img.width/pixel_size}

110link}

111link

112linkfunction getClosestColor(R,G,B){

113link var minDiff = 10000

114link var closest

115link var [L,a,b] = RGBtoLAB(R,G,B)

116link

117link for (let i = 0; i < files.length; i++) {

118link const color = files[i].replace(".jpg","")

119link var cr = parseInt(color.substring(0,2), 16)

120link var cg = parseInt(color.substring(2,4), 16)

121link var cb = parseInt(color.substring(4), 16)

122link

123link var [cL,ca,cb] = RGBtoLAB(cr,cg,cb)

124link

125link var diff = Math.sqrt( Math.pow(L - cL, 2) + Math.pow(a - ca, 2) + Math.pow(b - cb, 2) )

126link if (diff < minDiff){

127link minDiff = diff

128link closest = files[i]

129link }

130link }

131link return closest

132link}

133link

134linkfunction RGBtoLAB(r,g,b){

135link //RGBtoXYZ

136link var x = RGBtoXYZ_RtoX[r] + RGBtoXYZ_GtoX[g] + RGBtoXYZ_BtoX[b];

137link var y = RGBtoXYZ_RtoY[r] + RGBtoXYZ_GtoY[g] + RGBtoXYZ_BtoY[b];

138link var z = RGBtoXYZ_RtoZ[r] + RGBtoXYZ_GtoZ[g] + RGBtoXYZ_BtoZ[b];

139link

140link if (x > 0.008856)

141link x = Math.cbrt(x);

142link else

143link x = (7.787 * x) + 0.13793103448275862;

144link

145link if (y > 0.008856)

146link y = Math.cbrt(y);

147link else

148link y = (7.787 * y) + 0.13793103448275862;

149link

150link if (z > 0.008856)

151link z = Math.cbrt(z);

152link else

153link z = (7.787 * z) + 0.13793103448275862;

154link

155link L = (116 * y) - 16;

156link a = 500 * (x - y);

157link b = 200 * (y - z);

158link

159link return [L,a,b];

160link}

1linkimport json

2linkimport requests

3linkimport shutil

4linkfrom time import sleep

5linkfrom colorthief import ColorThief

6linkfrom tqdm import tqdm

7linkfrom os import rename

8link

9link

10linkdef to_hex(number):

11link res = ""

12link aux = hex(int(number))[2:]

13link return ("0" * (2-len(aux))) + aux

14link

15linkkey = "YOUR API KEY"

16linkimage_type = "photo"

17linkper_page = 20

18linkpage = 1

19linkorientation = "horizontal"

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

21link

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

23linkfor color in colors:

24link URL = base_url + color

25link res = requests.get(URL)

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

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

28link # save the image

29link url = image['previewURL']

30link name = './data/' + str(image['id']) + '.jpg'

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

32link if r.status_code == 200:

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

34link r.raw.decode_content = True

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

36link

37link # Get the dominant color of the image

38link color_thief = ColorThief(name)

39link dominant_color = color_thief.get_color(quality=1)

40link dom = ""

41link for x in dominant_color:

42link dom += to_hex(x)

43link rename(name, './data/' + dom + '.jpg')

linkFuentes

ProblemaInformaciónFoto-MosaicoEspacio de color LabSoluciónProcesoImagen originalMosaicoFuentes

Home

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