Python boot camp by Dr.Angela day18

#Mission>> Turtle Graphics 1  ~Dashed line~
以下のような点線を作成するプログラム。
![dashed_line.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/975449/1075876d-93d7-a198-15c6-1a2ffd701c50.png)

```python:turtle_dashedline.py
from turtle import Turtle,Screen

t = Turtle()
t.home()
for i in range(15):
    t.forward(5)
    t.penup()          #penを上げて描線しないようにして
    t.forward(5)       #距離5を進む
    t.pendown()        #またpenを下ろして描線できるようにしておく

my_screen = Screen()
print(my_screen.canvheight)
my_screen.exitonclick()  
```


#Mission>> Turtle Graphics 2  ~Polygon~
以下の図形を出力するプログラム。今後のために、色の名前ではなく、3原色RGBを指定するようにした。
![shapes.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/975449/0ef02575-db04-fddb-82c3-e717df3d397b.png)


```python:turtle_shapes.py
from turtle import Turtle, Screen
import random

t = Turtle()
s = Screen()
s.colormode(255)
colors = list(range(255))

t.home()
for i in range(3,12):
    angle = 180 - (((i - 2) * 180) / i)   #角度計算して指定
    r = random.choice(colors)             #〇角形が変わるたびに色変更する
    g = random.choice(colors)
    b = random.choice(colors)
    t.pencolor(r,g,b)
    for m in range(i):              #〇角形の〇辺分、角度変えて進むを繰り返す
        t.left(angle)
        t.forward(50)

s.exitonclick()                     #スクリーンのどこかをクリックで終了する
```
↓↓ 関数使って修正してみた。こっちの方がすっきりして分かりやすかったりするか・・・？
s.colormade(255),color_list = ..., t.home()は__関数の外で定義した方が、処理に負担がかからず__よかったりするのかなー？と思ったが、__見やすさとしては関数の中に入れて__しまった方が分かりやすい気もする。どちらが良いのだろうか・・・？

```python:turtle_shapes_2.py
from turtle import Turtle, Screen
import random

t = Turtle()
s = Screen()

def put_colors():     #色指定
    s.colormode(255)
    color_list = list(range(255))
    return random.choice(color_list),random.choice(color_list),random.choice(color_list)

def draw_shapes(i):   #図形描く
    t.home()
    angle = 180 - (((i - 2) * 180) / i)
    r,g,b = put_colors()
    t.pencolor(r,g,b)
    for m in range(i):
        t.left(angle)
        t.forward(50)

for i in range(3,12):      #三角形から十二角形まで繰り返す
    draw_shapes(i)
    
s.exitonclick()  
```
#Mission>> Turtle Graphics 3 ~Spirogragh~
以下の幾何学模様を描くようにプログラム。
![spirograph.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/975449/80a7b486-07bd-83fa-0f1c-66ff91241044.png)

```python:turtle_spirogragh.py
from turtle import Turtle, Screen
import random

t = Turtle()
s = Screen()

t.home()
s.colormode(255)
color_list = list(range(255))

def random_color():
    r = random.choice(color_list)
    g = random.choice(color_list)
    b = random.choice(color_list)
    return r,g,b

times = 30               #何回円を描くか

for i in range(times):
    color_set = random_color()
    t.speed(0)
    #s.delay(0)          #delay(0)にすると高速すぎてアニメーション消失するのでコメントアウトした
    t.pencolor(color_set)
    t.circle(50)
    t.right(360/times)   #何回円を描くかによって角度変化決定すれば二重になぞらずに終了

print(s.canvheight)
s.exitonclick()  
```
#タプル (=Tuples)
データ型の一種。以下のように表記される。リストと異なり、"[  ]"ではなく"( )"が用いられる。リストと異なる点---> 一度格納したら変更できない！(=immutable)

#最初のimport系の書き方---お作法---
__1. from turtle import Turtle__
from モジュール名 import モジュールの中の名前

__2. from turtle import  *__
* : なんでも対象にするワイルドカード

__3. import turtle as t__
import モジュールの中の名前 as エイリアス(alias)
importしてくるモジュール名が非常に長い時、別名を指定して設定が可能。

※2のように書かれることも多いが、pythonでは書いている途中に、「この変数はどこから来たのだろう？」となってreally confusingするので非推奨！


#Mission>> モナリザを点描するプログラム
Turtle graphicsを学んだので、より面白くするために特定の画像から座標と色を抽出してドットでオリジナル画像を再現するプログラムを作成することにした。
[__完成！！__](#解像度変更でどう変わるか)

###1. 指定したカラー画像を読み込む
__OpenCV__というライブラリを使用する

>画像ファイルを読み込むには cv2.imread() という関数を使います。画像ファイルが作業ディレクトリ内に保存されている場合はファイル名のみを指定し、そうでない場合は絶対パスもしくは適切な相対パスで指定しなければいけません.。第2引数は画像の読み込み方法を指定するためのフラグです。
    cv2.IMREAD_COLOR : カラー画像として読み込む．画像の透明度は無視される．デフォルト値
    cv2.IMREAD_GRAYSCALE : グレースケール画像として読み込む
    cv2.IMREAD_UNCHANGED : アルファチャンネルも含めた画像として読み込む
>
引用：http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_gui/py_image_display/py_image_display.html

-> https://note.nkmk.me/python-opencv-imread-imwrite/
-> https://qiita.com/matsu_mh/items/54b09273aef79ae027bc


モナリザプログラムに取り組む前に・・・
###__★グレースケールプログラム__
同じディレクトリに存在する画像(fashion1.jpg)を指定し、サイズを表示させる＆グレースケールとして取り込むプログラム。※同じディレクトリ以外の階層にある画像は絶対パスorカレントディレクトリからの相対パスで指定が可能。__ESCキー__で保存せずに終了、__sキー__でカレントディレクトリにグレースケールの画像を"gray_fashion.png"(ここは各自変更可能)として保存する。

```python:grayscale.py
import cv2
    
img = cv2.imread("fashion1.jpg", 0)    
#第一引数に指定する画像ファイル名orパス
#第二引数をcv2.IMREAD_GRAYSCALE or 0 でグレースケールに、cv.IMREAD_UNCHANGED でカラー画像に
h,w = img.shape()                      #画像の大きさ(今回はグレースケールなので2つの返り値)
print(f"height: {h}\nwidth: {w}")      #カラーの場合、channel(=3:BGR)も含めた3つの返り値

cv2.imshow('image',img)                #ウィンドウ表示する際のバーに表示する画像名
k = cv2.waitKey(0) & 0xFF

if k == 27:             # wait for ESC key to exit
    cv2.destroyAllWindows()
elif k == ord('s'):     # wait for 's' key to save and exit
    cv2.imwrite('gray_fashion1.png',img)    #保存するファイル名orパス
    cv2.destroyAllWindows()
```

###2. 画像の座標範囲と各座標に対応するカラー要素(BGR)を抽出する
★多次元配列 --> 使わなかったので無視してOK
-> http://taustation.com/python3-2d-array-adding-row-column/#i-5
-> https://www.javadrive.jp/python/list/index15.html
-> https://nekoyukimmm.hatenablog.com/entry/2016/02/26/091217

```python:monalisa_test.py
def image_component():
    import cv2
    img = cv2.imread("fashion1.jpg", cv2.IMREAD_UNCHANGED)
    h,w,c = img.shape
    print(f"height: {h}\nwidth: {w}\nchannel: {c}")
    pos_rgb = img[h-1,w-1]   #指定した座標のBGRを抽出
    print(pos_rgb)

#TO DO: 二次元配列として[x,y]:[r,g,b]を格納したい
#--> 結局二次元配列使用しなかった( ;∀;)
image_component()
```
__★各座標とBGRの抽出__
各x座標(1～h)に対し、y座標(1～w)を繰り返し割り当てるので、for文のネストを使用。
※OpenCVでは画像の縦横を自動的に判定して読み込んでくれるはずなのだが、なぜかいつも横向きに出力されるため、画像読み込み後90°回転させている関係で、x座標とy座標が逆であることに注意。

```python3:
for x in range(1,h+1,10):       #解像度変更する場合、ここの第三引数(増分指定)を変更する
    for y in range(1,w+1,10):
        position = [x,y]        #全座標の抽出完了
        pos_rgb = img[x-1,y-1]  #各座標に対してRGBを抽出
```

__★OpenCVのBGR配列__
-> https://teratail.com/questions/249839
-> https://note.nkmk.me/python-opencv-pillow-image-size/
OpenCVで抽出したカラーはBGR出力なので、turtleの.colorに引数指定する際、RGB出力に変換している

```python: 
b = pos_rgb[0]
g = pos_rgb[1]
r = pos_rgb[2]
print(f"x:{x}, y:{y}, RGB:{pos_rgb}")    #ここまでOpenCVのBGR順

~~~~~中略~~~~~
dot.color((r,g,b),(r,g,b))      #b,g,rではなくr,g,bで引数指定すること
```        

★色に関して
-> Turtle color：https://cs111.wellesley.edu/labs/lab01/colors
抽出したBGRをTurtle colorと結び付けるのが大変そうな気がする(´;ω;｀)
→ と思ったけど、turtle colorはRGBで指定が可能だったので問題なかった。
-> trinketカラーチャート：https://trinket.io/docs/colors
-> 色分析サイト：https://www.color-site.com/image_pickers

###3. 出力スクリーンの調整とアニメーション速度の調整
__＜使用したメソッド一覧＞__ ※引数に何が入るのかはドキュメント参照のこと。
-> https://docs.python.org/ja/3/library/turtle.html
dot.shape("shape_name") / screen.colormode(0 or 255) / screen.setup(x,y) / screen.screensize(x,y) / screen.delay(0-10) / screen.tracer(x,y)
使用したメソッドの一部は日本語訳されていないものだった。
色はどんな色でも三原色と呼ばれるRed/Blue/Green(頭文字をとってRGB)で表現される。各要素はmax255までが範囲なので、screen.colormodeは0(モノクロ)or255(全色)の2択である。
他-> https://yutarine.blogspot.com/2019/09/python-turtle-graphics-window-settings.html

###4. 2で抽出した各座標に対応するRGBをTurtle colorに渡してstampアニメーションの挙動決定
一応完成した。1日足らずでできたので、進歩進歩(*'▽')

```python3:monalisa_1.py
from turtle import Turtle, Screen
import cv2

dot =Turtle()
screen = Screen()

def image_component():
    import cv2
    img = cv2.imread("monalisa.jpg", cv2.IMREAD_UNCHANGED)  #第一引数に画像ファイル名指定
    trans_img = img.transpose(1,0,2)[:,::-1]
    h,w,c = trans_img.shape
    print(f"height: {h}\nwidth: {w}\nchannel: {c}")
    pos_rgb = trans_img[h-1,w-1]   #指定した座標のBGRを抽出
    print(pos_rgb)
    return trans_img, h, w

#dot.home()
dot.shape("circle")
img, h, w = image_component()
screen.colormode(255)
screen.setup(0.95,0.95)         #PCの画面の縦横を1,1(ピクセル指定も可)として比率で表示するスクリーンサイズ決定
screen.screensize(1000,1000)    #描画できるスクリーンサイズを指定
screen.delay(0)                 #.speed(10)より高速にしたいのでアニメーションのdelayを0に指定
screen.tracer(1000,0)           #もっと高速にしたいので1000ごとにまとめて処理するように設定

for x in range(1,h+1,10):       #解像度変更する場合、ここの第三引数(増分指定)を変更する
    for y in range(1,w+1,10):
        position = [x,y]        #全座標の抽出完了
        pos_rgb = img[x-1,y-1]
        b = pos_rgb[0]          #OpenCV-> BGR出力, turtle-> RGB出力なので注意!!
        g = pos_rgb[1]
        r = pos_rgb[2]
        print(f"x:{x}, y:{y}, RGB:{pos_rgb}")
        
        dot.speed(10)    #fastest = 0でアニメーションなしになる-> ならねーじゃん！
        dot.penup()
        dot.setpos(x-400,y-300)     #点描開始位置を(0,0)からずらす
        dot.color((r,g,b),(r,g,b))
        dot.shapesize(0.5,0.5)      #スタンプサイズ変更(×0.5), 解像度変更に伴い、ここも変更する必要が出てくる
        dot.stamp()
        dot.penup()                 #1行終わったらペン上げて移動

print(screen.canvheight)
screen.exitonclick()  
```
より__分かりやすく修正したコード__がこちら __↓↓__

```python:monalisa_2.py
from turtle import Turtle, Screen
import cv2
#from screenshot import ScreenShot 

dot =Turtle()
screen = Screen()
#screenshot = ScreenShot()

def image_component():
    import cv2
    img = cv2.imread("monalisa.jpg", cv2.IMREAD_UNCHANGED)   #第一引数を指定のファイル名にすればカラーならなんでも行ける
    trans_img = img.transpose(1,0,2)[:,::-1]    #カラー画像読み込むとなぜか横向きになるので90度回転させる
    h,w,c = trans_img.shape                     #モノクロ画像の場合は返り値2つなので変更すること
    print(f"height: {h}\nwidth: {w}\nchannel: {c}")
    pos_rgb = trans_img[h-1,w-1]   #指定した座標のBGRを抽出
    print(pos_rgb)
    return trans_img, h, w

def screen_settings():
    screen.colormode(255)     #スクリーンに出力するカラーモードの選択(0:モノクロ or 255:カラー)
    screen.setup(0.6,0.8)     #PCの画面の縦,横を1,1(ピクセル指定も可)として比率でスクリーンサイズ決定(※今回は90度回転させているので横,縦になっている)
    screen.screensize(1000,1000)      #描画できる範囲を指定(スクロールして表示できる範囲)
    screen.delay(0)                   #speed.(10)より高速にしたいのでアニメーションのdelayを0に指定
    screen.tracer(2000,0)             #もっと高速にしたいので100ごとにまとめて処理するように設定
    screen.title("Pixelization App")  #window title bar

def draw_pixel():
    dot.shape("circle")
    dot.speed(10)               # 0 = "fastest"でアニメーションなしになる---> ならなかった！
    dot.penup()
    dot.setpos(x-100,y-100)     #点描開始位置を(0,0)からずらす-->(x-400,y-300)
    dot.color((r,g,b),(r,g,b))
    dot.shapesize(0.2,0.2)      #スタンプサイズ変更(オリジナルを1,1として比率変更)
    dot.stamp()
    dot.penup()                 #1行終わったらペン上げて移動


screen_settings()
img, h, w = image_component()

for x in range(1,h+1,5):
    for y in range(1,w+1,5):
        position = [x,y]        #全座標の抽出完了
        pos_rgb = img[x-1,y-1]
        b = pos_rgb[0]          #OpenCV-> BGR出力, turtle-> RGB出力なので注意!!
        g = pos_rgb[1]
        r = pos_rgb[2]
        print(f"x:{x}, y:{y}, RGB:{r,g,b}")
        draw_pixel()

print(screen.canvheight)
#screen.listen()
screen.exitonclick()  
```
__＜今後付け足したい機能＞__
完成したピクセル画像に対して、
・"s" 押下でスクリーンショットとして保存 --> pillowが有効説
・"Esc" 押下でプログラム終了

###解像度変更でどう変わるか
写真引用→ http://www.kabegamilink.com/act/0705/03414.html
あまりに大きいサイズの写真でrunすると、時間がかかりすぎるので、下記サイトで400px or 600px位に縮小することをお勧めする。→ https://resizer.myct.jp/

__★sample1： 増分10, stamp.shapesize(0.5,0.5)で行った場合__
![mozaic_monalisa_!.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/975449/c02e60fb-35b4-caf0-4efc-ba6f02f2ee70.png)
__★sample2： 増分5, stamp.shapesize(0.5,0.5)で行った場合__
増分に応じてスタンプの大きさも変更しないと、こんな感じで被ってしまいうまく表示できない。
![mozaic_mosalisa_2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/975449/8ed615d3-22a5-d901-43fa-6365cf78888b.png)
__★sample3： 増分2, stamp.shapesize(0.2,0.2)で行った場合__
![mozaic_monalisa_3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/975449/5cdd83dd-947f-4775-fea8-56c418def173.png)

__！！作成したコードはこれからclassなど使用して修正していく。__！！__

###最後の晩餐 (おまけ)
![mozaic_last_dinner.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/975449/bf453115-1024-12b2-5580-d8d06da87cce.png)

これから作りたいもの
#Mission>> Pixelization Application
これで簡単にモザイク処理を施すことができる！SNSに上げる際の強力なツール。
-> https://note.nkmk.me/python-opencv-face-detection-haar-cascade/
-> https://note.nkmk.me/python-opencv-mosaic/