【Python】URLを指定してimread()したい。ついでにpip公開。

:::note info
この記事はOpenCV Advent Calendar 2021の15日目の記事です。
:::

# はじめに

僕の知り得る限り、10年前くらいから同じこと考えている人はチョイチョイいたと思うネタですが👀

やはり、Google ColaboratoryやJupyter Notebook、JupyterLabで適当なテスト画像を使おうと思うと、この手の処理があると便利です(特にColaboratory)

ただ、処理自体をコピペして使いまわすのも若干手間なので、pip登録しました。
Colaboratoryでの使用感は以下のような感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140207/af78eb7f-f21b-5c00-f352-320e9e7ab18e.png)



ただ、この投稿書いている最中にふと「[python-opencv-utils](https://pypi.org/project/python-opencv-utils/)」の存在を思い出して、確認したら案の定「urlread()」ありました、、、😇
あああー、、、
まあいっか、、、👀
ちなみに使用感は以下のような感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140207/2afb1279-313c-9242-ac89-741d5e48e807.png)

# 追記(2022年1月21日現在)
実行時に以下のようなエラーが発生するケースがあります。

```
Error occurred during loading data. Trying to use cache server https://fake-useragent.herokuapp.com/browsers/0.1.11
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/dist-packages/fake_useragent/utils.py", line 154, in load
    for item in get_browsers(verify_ssl=verify_ssl):
  File "/usr/local/lib/python3.7/dist-packages/fake_useragent/utils.py", line 99, in get_browsers
    html = html.split('<table class="w3-table-all notranslate">')[1]
IndexError: list index out of range
```
その場合、もう一度`imread_from_url()`を実行するか
`!pip install imread_from_url==0.1.2`で旧バージョンをインストールしてください

# 実装
実装は以下です。
requestsとPillowに依存しています。

```python
import requests
from io import BytesIO

import cv2
import numpy as np
from PIL import Image


def imread_from_url(url, seek_index=0, debug=False):
    image = None

    # URLから画像を取得
    response = requests.get(url)

    # PILでURLの画像を読み込み
    try:
        image = Image.open(BytesIO(response.content))
    except Exception as e:
        print(e)
        return None

    if debug:
        print(image)
        print(image.size)
        print(dir(image))

    # アニメーションGIF等の複数枚ある画像はシーク
    if 'n_frames' in dir(image):
        if debug:
            print('n_frames', image.n_frames)
            print('seek_index', seek_index)

        if 0 <= seek_index < image.n_frames:
            image.seek(seek_index)
        elif seek_index < 0:
            print('The index when seeking must be a positive integer')
            print('seek_index:', seek_index)
            image.seek(0)
        else:
            print('An index outside the seek range was specified')
            print('n_frames:', image.n_frames)
            print('seek_index:', seek_index)
            image.seek(image.n_frames - 1)
        image = image.convert('RGB')

    # RGB -> BGR
    image = cv2.cvtColor(np.array(image, dtype=np.uint8), cv2.COLOR_RGB2BGR)

    return image
```

urlread()のようにbytearrayにしてimdecode()したほうが短い行数ですっきりするのですが、、、
個人的な都合でアニメーションGIFも読み込みたかったためPillowを経由しています。

```python
import cv2
import numpy as np
from urllib.request import urlopen

def urlread(url, flags=cv2.IMREAD_UNCHANGED):
    response = urlopen(url)
    img = np.asarray(bytearray(response.read()), dtype=np.uint8)
    img = cv2.imdecode(img, flags)
    return img
```

大したソースコードではないですが、ソースコードはGitHubで公開しています。

https://github.com/Kazuhito00/imread_from_url

# 使用例
以降のスクリプトは、[Qiita-AdventCalendar-20211215-OpenCV.ipynb](https://colab.research.google.com/drive/1jcMEOiplBPhoQJrQunOs9Sf-mBsjN-P5?usp=sharing) でも公開しています。

#### ① pip インストール

```bash
pip install imread_from_url
```

#### ② 画像のURLを調べる

例：Chrome上で画像を右クリックして「画像アドレスをコピー」 など
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140207/627894a5-30d4-e8df-4982-e6363a152ae2.png)

#### ③ imread_from_url()でURLを読み込む
読み込んで使用する👾

```python
from imread_from_url import imread_from_url

image = imread_from_url(
    'https://raw.githubusercontent.com/Kazuhito00/Kazuhito00/master/image/icon200.jpg'
)
```

#### その他：アニメーションGIFを読み込む
以下のようなアニメーションGIFを読み込むと、最初のフレームを読み込みます。
※PillowのアニメーションGIF読み込みは現時点では試験的な実装のようです。実写のアニメーションGIFなどを読み込むと画像が崩れる場合があります。
![animegif](https://user-images.githubusercontent.com/37477845/145334396-8995ec38-a886-4641-8636-fc855c1fcc32.gif)

```python
gif_image = imread_from_url(
    'https://user-images.githubusercontent.com/37477845/145334396-8995ec38-a886-4641-8636-fc855c1fcc32.gif' 
)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140207/68889ad5-c8db-e8fc-f718-938bb29510be.png)

デフォルトでは最初のフレーム(0)を読み込みますが、seek_index引数を指定することで指定のフレームを読み込みます。
この時、最大フレーム数より大きい数値を指定すると、最後のフレームを読み込みます。

```python
gif_image = imread_from_url(
    'https://user-images.githubusercontent.com/37477845/145334396-8995ec38-a886-4641-8636-fc855c1fcc32.gif' 
    seek_index=50,
)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140207/72b66d3b-5d20-7fe6-bc3b-57fb2d3a00d3.png)

debug引数にTrueを与えると、Pillowで読み込んだ際のフォーマットや、最大フレーム数、指定フレーム数の情報を表示します。

```python
gif_image = imread_from_url(
    'https://user-images.githubusercontent.com/37477845/145334396-8995ec38-a886-4641-8636-fc855c1fcc32.gif' ,
    seek_index=50,
    debug=True,
)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140207/fc2393a1-c017-ad9f-95fc-8bc885d91d6a.png)

以上。