GitHubのIdenticonの生成と取得

# はじめに
　GitHubのプロフィール画像を自分で変更したのですが，初期のものに戻したくなりました．
　また，そもそもGitHubの初期プロフィール画像はどのように生成されているのか気になって調べてみました．

　本記事では，初期プロフィール画像の生成方法と初期プロフィール画像への戻し方を解説します． -> [プロフィール画像の戻し方](#プロフィール画像の戻し方)

# Identiconの生成

## Identiconとは
　GitHubにはIdenticonと呼ばれるアイコンが存在します．GitHubアカウントを作成したときにプロフィール画像になってるこんなやつです．

<img src="https://qiita-image-store.s3.amazonaws.com/0/187083/54d88cd6-0afe-8959-d8c5-5b50b6773213.png" "Identicon" width=40%>

## Identiconの生成方法
　GitHubのBlogにIdenticonの生成に関する記事があります．

　[Identicons!](https://github.com/blog/1586-identicons) - The GitHub Blog 

>
Our Identicons are simple 5x5 "pixel" sprites that are generated using a hash of the user's ID. The algorithm walks through the hash and turns pixels on or off depending on even or odd values. These generated patterns, combined with hash-determined color values, ensures a huge number of unique Identicons.
>

　Identiconは5x5のピクセルで，ユーザーIDのハッシュ値を元に生成されているそうです．ハッシュ値が偶数か奇数かで各ピクセルのon/offを決定してパターンを生成します．また，Identiconの色も同様に，ハッシュ値に依存して決定しています．これによって，多数のユニークなIdenticonsを保証しているとのことです．

## Identiconを生成してみる
　ユーザーIDのハッシュを利用して各ユーザーのIdenticonを決定していることがわかりました．実際にIdenticonを生成してみたいと思います．

1. ハッシュ値の生成   
　ユーザーIDをMD5します．128[bit]（16進数で32桁）のハッシュ値が得られます．

　ex) 19229051 -> 73e516f03ddfd989d9ab2975dcbcdbac

2. パターンの決定   
パターンは以下のように，70×70を1ピクセルした5x5のピクセルから成ります．

<img src="https://qiita-image-store.s3.amazonaws.com/0/187083/7c41ecd8-7c43-5b36-00ac-640d9977ce64.jpeg" "pattern1" width=40%>

　ハッシュ値の前から15文字を使って以下のようにパターンを決定します．ハッシュ値が偶数ならピクセルをON，奇数ならピクセルをOFFにします．順番は真ん中の列の上から下，その右の列の上から下，最後に右端の列の上から下の順です．

　ex) 73e516f03ddfd98

<img src="https://qiita-image-store.s3.amazonaws.com/0/187083/3a805dfa-3cb9-afaa-ea7e-dc16a3f97a91.jpeg" "pattern2" width=40%>

　真ん中の列を中心に線対称に折り返します．これでパターンが完成します．

<img src="https://qiita-image-store.s3.amazonaws.com/0/187083/94912e5a-a865-9a42-7690-5471353a5852.jpeg" "pattern3" width=40%>

3. 色の決定  
　ハッシュ値の最後の7文字を使って色を決定します．色はHSL色空間で表現されていて，色相3文字，彩度2文字，輝度2文字で決定しています．

　ex) cbcdbac -> 色相：cbc　彩度：db　輝度：ac

　各16進数を10進数に変換して各パラメータを決定します．色相は0から360の範囲なので，0から4095を0から360の範囲に変換します．彩度と輝度は0から100の範囲です．GitHubでは0から255を0から20の範囲に変換して，それぞれ65,75から減算した値を使用しています．

```math
色相：(cbc)_{10} \times \frac{360}{4095} = 3260 \times \frac{360}{4095} = 287\\
彩度：65 - (db)_{10} \times \frac{20}{255} = 65 - 219 \times \frac{20}{255} = 48\\
輝度：75 - (ac)_{10} \times \frac{20}{255} = 75 - 172 \times \frac{20}{255} = 62
```

　最後にHSL色空間からRGBへ変換します．HSLからRGBへの変換方法は以下のサイトを参考にしました．ぐぐればすぐにわかると思います．
　
　[RGBとHSLの相互変換ツールと変換計算式](https://www.peko-step.com/tool/hslrgb.html) - PEKO STEP

　これで色を決定することができました．Identiconの完成です．

<img src="https://qiita-image-store.s3.amazonaws.com/0/187083/768137a4-7d4a-13b9-77d9-f396cd298d71.jpeg" "pattern4" width=40%>

## 注意
　GitHubは詳しいIdenticon生成アルゴリズムを公開していません．本記事で生成方法として紹介したハッシュアルゴリズムや色の決定方法が実際にGitHubで使用されているアルゴリズムと100%同じかどうか分かりません．あくまでGitHubのIdenticon風の画像を生成する方法としてご理解ください．

## プログラム
　GitHubのIdenticon風の画像を生成するプログラムです．``myid``に任意のIDを書きます．画像の出力はOpenCVを使っています．

```identicons.py
#coding: utf-8

import numpy as np
import hashlib
import cv2

myid = '19229051'

size = 420 #画像のサイズ
pixel = 70 #1ピクセルのサイズ
frame = 35 #余白のサイズ
background = 240 #背景の色(0-255)

def create_pattern(hash_value):
	pat = np.array([int(hash,16)%2 for hash in hash_value[:15]])
	pat = pat.reshape(3,5).T
	pat = np.append(pat[:,-1:0:-1], pat, axis=1)

	return pat

def create_color(hash_value):
	hue = ''
	for i in range(3): hue += hash_value[25+i]
	hue = int(hue,16) / 4095 * 360
	sat = ''
	for i in range(2): sat += hash_value[28+i]
	sat = 65 - int(sat,16) / 255 * 20
	lum = ''
	for i in range(2): lum += hash_value[30+i]
	lum = 75 - int(lum,16) / 255 * 20

	#print('hue =',hue,'lum =',lum,'sat =',sat)

	if lum < 50:
		max = 2.55 * (lum + lum * (sat/100))
		min = 2.55 * (lum - lum * (sat/100))
	elif lum >= 50:
		max = 2.55 * (lum + (100-lum) * (sat/100))
		min = 2.55 * (lum - (100-lum) * (sat/100))

	if 0 <= hue < 60:
		red   = max
		green = (hue/60) * (max-min) + min
		blue  = min
	elif 60 <= hue < 120:
		red   = ((120-hue)/60) * (max-min) + min
		green = max
		blue  = min
	elif 120 <= hue < 180:
		red   = min
		green = max
		blue  = ((hue-120)/60) * (max-min) + min
	elif 180 <= hue < 240:
		red   = min
		green = ((240-hue)/60) * (max-min) + min
		blue  = max
	elif 240 <= hue < 300:
		red   = ((hue-240)/60) * (max-min) + min
		green = min
		blue  = max
	elif 300 <= hue <= 360:
		red   = max
		green = min
		blue  = ((360-hue)/60) * (max-min) + min

	return [red,green,blue]

def create_image(pattern, colors):
	image = np.full([size,size,3],background)

	for i in range(5):
		for j in range(5):
				for k in range(pixel):
					for l in range(pixel):
						if pattern[i][j] == 0:
							image[frame+i*pixel+k][frame+j*pixel+l][0] = colors[2]
							image[frame+i*pixel+k][frame+j*pixel+l][1] = colors[1]
							image[frame+i*pixel+k][frame+j*pixel+l][2] = colors[0]
						elif pattern[i][j] == 1:
							image[frame+i*pixel+k][frame+j*pixel+l][:] = background
	
	return image

if __name__ == '__main__':

	print('ID  =', myid)

	#MD5の計算
	hash_value = hashlib.md5(myid.encode()).hexdigest()
	print('md5 =', hash_value, '\n')

	#パターンの生成
	pat = create_pattern(hash_value)
	print('pattern')
	print(pat, '\n')

	#色の生成
	colors = create_color(hash_value)
	print('RGB colors =', colors, '\n')

	#画像の生成
	image = create_image(pat, colors)
	cv2.imwrite(myid+'.png', image)
	print('Output', myid+'.png')
``` 

```
ID  = 19229051
md5 = 73e516f03ddfd989d9ab2975dcbcdbac 

pattern
[[1 0 1 0 1]
 [1 1 1 1 1]
 [1 0 0 0 1]
 [1 1 1 1 1]
 [0 1 1 1 0]] 

RGB colors = [182.8124831932773, 109.91120588235293, 203.78879411764703] 

Output 19229051.png
```

<img src="https://qiita-image-store.s3.amazonaws.com/0/187083/ab949c75-1d42-044c-9c55-94258d73df8d.png" "Identicon" width=40%>


<a name="初期アイコンの取得"></a>
# プロフィール画像の戻し方
　長々とIdenticonの生成方法を解説しましたが，自分のプロフィール画像を初期のものに戻す方法は簡単です．
　~~``https://github.com//<username>.png``~~(2019/04/04修正) ``https://github.com/identicons/<username>.png``にアクセスすれば自分のIdenticonがあります．これをダウンロードしてプロフィールを変更するだけです．自分で画像を生成する必要はありません．

# おわりに
　いい感じの形と色のIdenticonを生成するユーザーIDになるまで，アカウント作成リセマラとかできるのかな？

<!--
# 参考
* あたがわの日記 - [GitHub Identicons](https://blog.atgw.jp/archives/3025)
-->


# 追記（2018/02/17）
　HSL→RGBに変換する部分を自分で書いたけど，実はライブラリがあった．
　``(r,g,b) = colorsys.hls_to_rgb(h,l,s)``