【Python】経過時間をhh:mm:ssフォーマットで表示する

Deep Learningの学習工程のように、処理時間が長い場合に、定期的に下図のようにログを表示したかったのですが、timeモジュールのtime()関数で得られた秒数の差を 時:分:秒hh:mm:ss のフォーマットで表示する方法がみつからない。。

deletetimeモジュールを使うと、近いところまでは出来るのですが、時の部分が1桁になり、細かい調整ができません。。

import datetime
print(datetime.timedelta(seconds=12345))
# 3:25:45

細かい事を気にしなければdatetimeを使うのも良いかと思いますが、結局、秒数を hh:mm:ssフォーマットの文字列を返す関数を作ってみました。

def elapsed_time_str(seconds):
    """秒をhh:mm:ss形式の文字列で返す

    Parameters
    ----------
    seconds : float
        表示する秒数

    Returns
    -------
    str
        hh:mm:ss形式の文字列
    """
    seconds = int(seconds + 0.5)    # 秒数を四捨五入
    h = seconds // 3600             # 時の取得
    m = (seconds - h * 3600) // 60  # 分の取得
    s = seconds - h * 3600 - m * 60 # 秒の取得

    return f"{h:02}:{m:02}:{s:02}"  # hh:mm:ss形式の文字列で返す

使い方は、こんな感じです。

start = time.time()
time.sleep(3)
print(elapsed_time_str(time.time() - start))
# 00:00:03

print(elapsed_time_str(12345))
# 03:25:45

【Microsoft Edge】お気に入りバーを常に表示する

Microsoft Edgeのお気に入りバーは、デフォルトでは、新しいタブを表示したときのみ表示され、ページ内のリンクをクリックするとお気に入りバーが非表示になってしまいます。

 

 

私はこれまで、Google Chromeを使っていてChromeに慣れているとEdgeでもお気に入りバーは常に表示しておきたい!

ということで、お気に入りバーを常に表示する方法は以下の通りです。

 

  1. (お気に入り)をクリック
  2. (その他のオプション)をクリック
  3. お気に入りバーの表示 にマウスポインタを合わせる
  4. 常に をクリック

 

これで、お気に入りバーは常に表示されるようになります。

 

【Windows11】ウィンドウ外側の影をなくす方法

Windows11ではウィンドウの外側に影が付いて表示されるようになりました。

これは、なんとなく雰囲気はよくなったものの、マニュアルやブログなどの作成時に、ウィンドウのスクリーンショットを撮るときは、邪魔となるケースもあります。

そこで、この影を表示させない方法を紹介します。

ウィンドウ外側の影を消す設定方法

ます、ウィンドウのスタートボタンを右クリックし、表示されたメニューの中からシステムを選択します。

 

表示されたウィンドウの右側にあるシステムの詳細設定をクリックします。

 

次にパフォーマンスの部分にある設定ボタンをクリックします。

 

すると、ウィンドウの下に影を表示するという項目があるので、ここのチェックを外し、適用ボタンをクリックします。

 

これで、設定は完了です。

影があるときと、影が無いときを比較すると、

影あり

影なし

 

個人的にはブログを書くときに影があると邪魔な場合が多いので、影はない方がいいですね。

また、スクリーンショットをウィンドウに合わせて撮影できるアプリなどもありますが、Windows11ではウィンドウの角が丸くなっているので、影があるとスクリーンショット画像の角が少し黒くなってしまうため、やっぱり影は邪魔です。

 

ちなみに、スクリーンショットを撮影するプログラムに Screenpressoというプログラムがあります。
私の中ではマニュアルやブログを書くときには必須のアプリになっていて、おススメです。

無料の画面キャプチャソフト『Screenpresso』

【OpenCV/Python】画像ファイルの読み込み、表示

OpenCVでBmpやJpegなどの画像ファイルを開くには、imread関数を用います。

開いた画像データはimshow関数でウィンドウ付きで画像を表示します。

実際に画像が表示されるのは、waitKey関数が呼ばれたタイミングとなります。

以下に最もシンプルなサンプルを示します。

import cv2

# 画像ファイルの読み込み(カラー画像(3チャンネル)として読み込まれる)
img = cv2.imread("Mandrill.bmp")

# 画像の表示
cv2.imshow("Image", img)

# キー入力待ち(ここで画像が表示される)
cv2.waitKey()

(実行結果)

ここに示したサンプルでは、画像データは必ず8bit x 3ch のデータとなり、表示されたウィンドウはリサイズすることができません。

そのようにしないためには、各関数のオプションを設定します。

詳細は、各関数ごとに説明します。

imread()

bmp, jpeg, png, tiff などの画像ファイルを開きます。

cv2.imread( filename[, flags] ) -> retval
引数 説明
filename 画像のファイル名を指定します。
ただし、日本語は使えません。
flags ファイルを開く際に、カラーデータとして開くか?モノクロデータとして開くか?などを指定します。
指定しない場合、カラーデータ(BGRの3チャンネル)として画像ファイルを開きます。
cv2.IMREAD_UNCHANGED 画像ファイルのフォーマットのまま、画像ファイルを開きます。
cv2.IMREAD_GRAYSCALE   グレースケールに変換して画像ファイルを開きます。
cv2.IMREAD_COLOR          カラーデータ(BGRの3チャンネル)に変換して画像ファイルを開きます。
cv2.IMREAD_ANYDEPTH    画像ファイルのビット深度のまま画像ファイルを開きます。
cv2.IMREAD_ANYCOLOR    画像ファイルの色(チャンネル数)のまま画像ファイルを開きます。(参考)ImreadModes
戻り値 画像の輝度値がnumpyのndarray配列に格納されて返されます。
画像データは画像の左上から格納されています。
グレースケールの場合、[高さ, 幅]の二次元配列になります。
カラーの場合、[高さ, 幅, 色]の三次元配列になります。色の順番は、B,G,R,B,G,R・・・の順で格納されています。

画像ファイル名に日本語ファイルを使いたい場合は、NumPyやPillowを使って日本語ファイルを読み込む事も可能です。詳細は以下のページを参照ください。

日本語の画像ファイル読込・保存

また、imread()関数では、画像ファイルが見つからない場合など、画像ファイル読み込み時にエラーが発生しがちです。その場合の対応は以下のページを参照ください。

imreadで画像ファイルが読み込めないときの対応

 

(参考)

https://docs.opencv.org/4.5.5/d4/da8/group__imgcodecs.html#ga288b8b3da0892bd651fce07b3bbd3a56

imshow()

画像をウィンドウ付きで表示します。
実際に画面に画像が表示されるのは、cv2.waitKey()関数が呼ばれたタイミングで表示されます。

cv2.imshow( winname, mat ) -> None
引数 説明
winname 表示するウィンドウのタイトルを指定します。
namedWindow()関数で作成したウィンドウと同じwinnameを指定することで、画像を表示するウィンドウを切り替えることもできます。
winnameで指定したウィンドウが無い場合、新しいウィンドウで画像を表示します。
日本語を指定すると文字化けします。
mat 表示する画像データを指定します。
戻り値 なし

 

(参考)

https://docs.opencv.org/4.5.5/d7/dfc/group__highgui.html#ga453d42fe4cb60e5723281a89973ee563

waitKey()

指定時間(mSec)分、キー入力を待ちます。

OpenCVで生成したウィンドウのキー入力イベント待ちとなります。

引数 説明
delay キー入力までの待ち時間をmSec単位で指定します。
値を省略、もしくは 0以下 を指定した場合、無限待ちとなります。
戻り値 入力したキーコードが戻されます。

(参考)

https://docs.opencv.org/4.5.5/d7/dfc/group__highgui.html#ga5628525ad33f52eab17feebcfba38bd7

namedWindow()

imshow()関数で表示するためのウィンドウを生成します。

cv2.namedWindow( winname[, flags] ) -> None
引数 説明
winname 表示するウィンドウのタイトルを指定します。
日本語を指定すると文字化けします。
flags ウィンドウのリサイズに関する設定を指定します。
cv2.WINDOW_NORMAL リサイズ可能なウィンドウを生成します。
表示されている画像はウィンドウサイズに合わせてリサイズされます。
cv2.WINDOW_AUTOSIZE 画像のサイズに合わせてウィンドウサイズを調整します。
ウィンドウのリサイズはできません。
他に cv2.WINDOW_FREERATIO, cv2.WINDOW_KEEPRATIO, cv2.WINDOW_GUI_NORMAL, cv2.WINDOW_GUI_EXPANDED の設定がありますが、少なくともWindows環境ではcv2.WINDOW_NORMALと同じ
(参考)WindowFlags
戻り値 なし

生成したウィンドウはdestroyWindow()関数で指定したウィンドウを削除するか、destroyAllWindows()関数で全てのウィンドウを削除するようにします。

(参考)

https://docs.opencv.org/4.5.5/d7/dfc/group__highgui.html#ga5afdf8410934fd099df85c75b2e0888b

destroyWindow()

ウィンドウタイトル名を指定してウィンドウを閉じます。

cv2.destroyWindow( winname ) -> None
引数 説明
winname 閉じるウィンドウのタイトルを指定します。
戻り値 なし

(参考)

https://docs.opencv.org/4.5.5/d7/dfc/group__highgui.html#ga851ccdd6961022d1d5b4c4f255dbab34

destroyAllWindows()

表示されているウィンドウ全てを閉じます。

cv2.destroyAllWindows() -> None

(参考)

https://docs.opencv.org/4.5.5/d7/dfc/group__highgui.html#ga6b7fc1c1a8960438156912027b38f481

各種設定を行ったサンプルプログラム

import cv2

# 画像ファイルの読み込み(ファイルフォーマットのまま読み込み)
img1 = cv2.imread("Mandrill.bmp", cv2.IMREAD_UNCHANGED)
img2 = cv2.imread("Text.bmp", cv2.IMREAD_UNCHANGED)

# リサイズ可能なウィンドウの作成
cv2.namedWindow("Image1", cv2.WINDOW_NORMAL)
cv2.namedWindow("Image2", cv2.WINDOW_NORMAL)

# 画像の表示
cv2.imshow("Image1", img1)
cv2.imshow("Image2", img2)

# キー入力待ち(ここで画像が表示される)
cv2.waitKey()

# すべて全てのウィンドウを削除
cv2.destroyAllWindows()

(実行結果)

【C#】各種メモリの最大値(2GB制限)

最近のPCではOSは64ビットで、搭載メモリも8GBぐらいは普通にあるので、C#のプログラムでもメモリを4GBぐらいは普通に確保できそうですが、実際には2BGぐらいで頭打ちになります。

おそらくメモリサイズ(要素数)を計算するときにint型で計算していてint型の最大値(2,147,483,647)を超える事でエラーになる場合が多そうです。

そこで、試しにメモリ確保の処理(配列、Bitmapクラス、Marshal.AllocCoTaskMem)の最大値が、どの程度まで確保できるのか?を確認してみました。

評価環境

  • Windows11 Home 64bit
  • 搭載メモリ 32GB
  • Visual Studio 2019
  • C#(.NET Core 3.1)

 

配列の最大値

よく使うbyte, int, float, doubleの型で試してみたところ、以下の要素数が最大となりました。

var byteArr = new byte[2147483591];
var intArr= new int[2146435071];
var floatArr= new float[2146435071];
var doubleArr= new double[2146435071];

配列の最大値に関しては、メモリのサイズというよりも、要素数のint型の最大値制限になっているようです。

.NET Frameworkの場合、デフォルトでは最大サイズが異なるようです。
詳細は、下記ページを参照ください。

https://docs.microsoft.com/ja-jp/dotnet/framework/configure-apps/file-schema/runtime/gcallowverylargeobjects-element?redirectedfrom=MSDN

Bitmapクラスの最大値

Bitmapクラスの確保には、幅、高さ、PixelFormatの組み合わせが、いろいろできてしまうため、幅の値は固定して、高さを変えながらエラーが出るまで最大の高さを確認しました。

以下が、PixelFormatをFormat8bppIndexed, Format24bppRgb, Format32bppArgbで確認したときの最大のサイズとなります。

var bmp8 = new Bitmap(1024 * 3, 699049, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
var bmp24 = new Bitmap(1024, 699049, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
var bmp32 = new Bitmap(1024, 524287, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

Bitmapクラスに関しては、 幅 x 高さ x 画素のバイト数(1, 3, 4) の値がint型の最大値に引っかかっているようです。

 

AllocCoTaskMemの最大値

あまり使う機会はありませんが、メモリ確保で使われるAllocCoTaskMemについても調べてみました。

var ptr = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(2147483647);

こちらは、AllocCoTaskMemの引数がint型のため、最大値を超えるとビルド時にエラーとなります。

 

まとめ

C#のメモリの最大値について調べてみましたが、おおむね2GBの制限があります。

メモリのサイズというより、要素数のint型の最大値(2,147,483,647)を超える事によりエラーになっているようです。

今時のPCで、2GBしかメモリを確保できないだなんて。。

1つのプロセス(実行しているプログラム)で2GB以下のメモリを複数確保する事は可能です。

どうしても2GB以上のメモリを確保したい場合は、メモリを分割して処理するか、C言語ライブラリを作成して、メモリ管理をライブラリ側で行う事で可能になります。
ただし、この時にも、うっかりint型で width * height * ch みたいな計算をしてしまうと、int型の最大値を超えてしまうので、要素数やメモリサイズを計算するときの型には注意が必要です。

 

参考

https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/builtin-types/integral-numeric-types

 

【OpenCV/Python】ドキュメントの場所

OpenCVの公式ホームページは以下の場所になります。

Home

 

Pythonに関する情報は、まだ少ない気もしますが、各関数のマニュアルは下記のページから参照します。

https://docs.opencv.org/4.5.5/

 

各関数の引数や戻り値は、使用するOpenCVのバージョンによっても異なる場合があるので、参照する前にバージョンを使用するバージョンに合わせてください。

 

調べたい関数名が分かっている場合は、右上の検索ボックスに関数名を入力すると、関数候補が表示されるので、その関数をクリックします。

 

Pythonに関する情報は、ちょっと少ない気もしますが、引数の数や順番などは確認できます。

 

日本語のドキュメントが見たい場合には、最近の更新がありませんが、もともとあったOpenCV-Pythonのチュートリアルのページ(現在は閉鎖されています)を翻訳してくれたページがあるので、こちら↓が参考になります。

http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_tutorials.html

こちらのページは情報が古くなっているので、本家のページと合わせて参照すると良いかと思います。

【Windows11】最近使用したファイルをすべて削除する方法

エクスプローラーでクイックアクセスに表示される最近使用したファイルは、完全にプライベートで使うPCの場合はあってもいいのですが、共有PCだったり、エクスプローラの画面をプロジェクタに表示する場合など、表示させたくない場合もあります。

その時に、ファイル一つ一つを消すには、消したいファイルを右クリックし、クイックアクセスから削除をクリックすれば最近使用したファイルから削除することができますが、消しても消しても古いファイルが表示され、すべてを消すのは大変です。

そこで、最近使用したファイルをすべて削除するには、エクスプローラーの右上に表示されている(もっと見る)をクリックし、オプションを選択します。

表示されたウィンドウのプライバシー消去をクリックします。

これで、最近使用したファイル内のファイル一覧がすべて削除されます。(ファイルの実態は残っています。)

そもそも最近使用したファイルを表示させたくない場合は、プライバシー内の最近使ったファイルをクイックアクセスに表示するのチェックを外します。

 

ただ、私のPC(Windows11 Home 21H2)ではエクスプローラのオプションを表示するとエクスプローラが何も操作できなくなりました。

しょうがないので、新規にエクスプローラを立ち上げ、もう一度オプションの表示をすると、オプションの画面を操作することができるようになりました。

Pythonプログラムのexe化

Pythonで作ったプログラム(*.pyファイル)は、Pythonの実行環境と、使用したモジュールがインストールされた環境でないと実行できないため、他のPCでプログラムを実行するのが面倒だったりもします。

PyInstallerというのを使うと、Pythonプログラムをexeにして実行することが可能になります。

私自身は、普段からVisual Studio2019を使って開発しているので、Visual StudioからPyInstallerを使う方法を紹介します。(おそらくVisual Studio2017以降であれば同様の操作で出来ると思います。)

Python環境の作成

Visual StudioのソリューションエクスプローラーPython環境を右クリックし、環境を追加をクリックします。

表示された画面の仮想環境を選択し、名前の部分に適当な名前を付け(今回はexeにしました)、ベースインタープリターの部分で、もともと使っていたPythonの環境(バージョン)を選択し、右下の作成をクリックします。

すると、今回作成した環境(exe)が追加されます。

ただし、パッケージ(PyPI)の部分を見ると、必要なモジュールが何もインストールされていない状態になります。

今回の例では、exe化しようとしているPythonファイル(*.py)は、pillowとnumpyを使用するため、別途、作成したPython環境(exe)に必要なモジュールをインストールする必要があります。

モジュールのインストールはPyPIとインストールされたパッケージの検索の部分に必要なモジュール名を入力すると、次のコマンドを実行する:pip install Pillow が表示されるので、この部分をクリックして、モジュールをインストールします。

各自、必要なモジュールは異なりますので、この状態で、Pythonのプログラムが実行できるか?確認しておいてください。

PyInstallerのインストール

exe化するためのPyInstallerも他のモジュールと同様にインストールします。

PyPIとインストールされたパッケージの検索の部分にpyinstallerと入力し、次のコマンドを実行する:pip install pyinstaller が表示されるので、この部分をクリックして、PyInstallerをインストールします。

私の場合、最終的に、このよう↓になります。

exe化の実行

PyInstallerを使ってexe化するには、コマンドプロンプトでコマンドを入力する必要があるので、コマンドプロンプトを表示します。

プロジェクトの名前の部分を右クリックし、表示されたメニューのここでコマンドプロントを開くをクリックします。

すると、プロジェクトのフォルダがコマンドプロンプト上に表示されます。

ここに、以下のコマンドを入力します。

pyinstaller [pyファイル名]

私の場合は以下のようにしました。

このコマンドを実行すると dist というフォルダが作成されます。

distフォルダの中にさらにプロジェクト名のフォルダが作成され、さらに、この中に目的とするexeファイルが作成されています。

他のPCでexeファイルを実行するには、exeファイルと同一階層にあるファイルすべてが必要になります。

ファイルがいくつも出来てしまうのは、少々扱いが面倒なので、必要なファイルも含めてexeファイル一つにすることもonefileオプションを設定することで可能です。

pyinstaller [pyファイル名] --onefile

また、プログラム起動時に表示されるコマンドプロンプトのウィンドウ↓

を表示させたくない場合は、windowedオプションを指定します。

pyinstaller [pyファイル名] --onefile --windowed 

私の場合の例↓

onefileオプションを使うと distフォルダ内にexeファイルが一つだけ作成されるので、このexeファイルを別のPCに持っていくだけで、Pythonや各種モジュールをインストールすることなく、実行することができます。

ただし、使用するモジュールからさらにdllを参照している場合などは、dllのインストールが必要になるかも?しれません。

参考

https://pyinstaller.readthedocs.io/en/stable/usage.html

【Python/Pillow(PIL)】JPEG画像の品質を指定して保存する

JPEG画像の保存では、品質を指定することで、画像のファイルサイズが変わります。

品質を良くすると、画像はキレイになりますが、ファイルサイズが大きくなります。

逆に品質を下げると、画像にノイズが乗りますが、ファイルサイズが小さくなります。

JPEG画像の品質を下げたときに乗る代表的なノイズとして、ブロックノイズモスキートノイズというものがあります。

品質を指定してJPEGファイルを保存するときの構文は以下の通りです。

Image.save(jpegfilename, quality = 75)

となります。

品質の値(quality)は初期値が75で指定可能なのは、0(低品質)~95(高品質)です。

サンプル

from PIL import Image

# 画像を開く
img = Image.open("Mandrill.bmp")

# JPEGで品質を指定して画像を保存する
img.save("Mandrill_q10.jpg", quality = 10)
img.save("Mandrill_q50.jpg", quality = 50)
img.save("Mandrill_q75.jpg", quality = 75)
img.save("Mandrill_q95.jpg", quality = 95)

# 画像を開く
img = Image.open("ImagingSolution.bmp")

# JPEGで品質を指定して画像を保存する
img.save("ImagingSolution_q10.jpg", quality = 10)
img.save("ImagingSolution_q50.jpg", quality = 50)
img.save("ImagingSolution_q75.jpg", quality = 75)
img.save("ImagingSolution_q95.jpg", quality = 95)

入力画像

評価用に読み込む画像は非圧縮のビットマップファイル(*.bmp)にしました。

一つは一般的な画像としてマンドリルの画像と、もう一つはモノクロでエッジの強い文字の画像にしました。

Mandrill.bmp
256×256画素24bitカラー
196,662バイト(非圧縮)
ImagingSolution.bmp
256×256画素8bitグレースケール
66,614バイト(非圧縮)

ファイルサイズの比較

quality 画像 サイズ(バイト)
10 4,465
50 13,189
75 19,908
95 47,073
10 3,594
50 6,629
75 8,487
95 15,452

等倍の画像で見ると qualityが50以上であれば、見た目に差はさほど感じられないでしょうか?

qualityが75と95とで比較すると、特に文字の画像を見ると、ほんの少しだけエッジがくっきり見えますが、ファイルサイズが倍近く異なります。

ノイズの比較

画像の中心付近を拡大して、ノイズを確認してみます。

quality = 10

quality = 50

quality = 75

quality = 95

quality = 10

quality = 50

quality = 75

quality = 95

マンドリルの画像ではquality = 50ぐらいまでが、四角いマス状のノイズ(ブロックノイズ)が確認できます。

文字の画像は、明暗の差が大きく、エッジが強い画像の代表例として用いたのですが、黒い周りにゴミのように見えるモスキートノイズが確認できます。

 

ノイズの出方やファイルサイズの両方を考えると、qualityの初期値である 75 ぐらいがバランスが良さそうです。

参考

https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.save

https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html

【Python/Pillow(PIL)】画像の上下左右反転、90°ごとの回転

Pillowで画像(画像データ)を画像の上下左右反転、90°ごとの回転をさせるには、Imageクラスのtransposeメソッドを用います。

これとは別にImageOpsモジュールmirror関数(左右反転)flip関数(上下反転)があるので、これは後半で説明します。

構文

Image.transpose(method)

パラメータ

引数名 説明
method 上下左右反転、90°ごとの回転の種類を以下の中から指定します。
Image.FLIP_LEFT_RIGHT
Image.FLIP_FLIP_TOP_BOTTOM
Image.FLIP_ROTATE_90
Image.FLIP_ROTATE_180
Image.FLIP_ROTATE_270
Image.FLIP_TRANSPOSE
Image.FLIP_TRANSVERSE
戻り値 上下左右反転、回転した画像(PIL.Image)

サンプル

from PIL import Image

# 画像を開く
img = Image.open("image.jpg")

# 左右上下反転、90度ごとの回転
img_transpose = img.transpose(Image.FLIP_LEFT_RIGHT)

# 画像の保存
img_transpose.save("image_transpose_FLIP_LEFT_RIGHT.jpg")

(実行結果)

元画像 左右反転(Image.FLIP_LEFT_RIGHT)

上下左右、回転の種類

methodに指定する種類は以下の通りです。

Image.FLIP_LEFT_RIGHT Image.FLIP_TOP_BOTTOM
Image.ROTATE_180 Image.ROTATE_90
Image.ROTATE_270 元画像
Image.TRANSPOSE Image.TRANSVERSE

ImageOpsモジュールのmirror(左右反転)とflip(上下反転)

ImageOpsモジュールにも、左右反転(mirror)と上下反転(flip)の関数があります。

こちらの方が関数名的には覚えやすいでしょうか?

 

(サンプル)

from PIL import Image
from PIL import ImageOps

# 画像を開く
img = Image.open("image.jpg")

# 左右反転
img_mirror = ImageOps.mirror(img)
# 画像の保存
img_mirror.save("image_mirror.jpg")

# 上下反転
img_flip = ImageOps.flip(img)
# 画像の保存
img_flip.save("image_flip.jpg")

(実行結果)

ImageOps.mirror() ImageOps.flip()

参考

https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.transpose

https://pillow.readthedocs.io/en/stable/reference/ImageOps.html#PIL.ImageOps.flip

【Python/Pillow(PIL)】画像の回転

【Python/Pillow(PIL)】画像の回転

Pillowで画像(画像データ)を拡大/縮小するには、Imageクラスのrotateメソッドを用います。

構文

Image.rotate(angle, resample=0, expand=0, center=None, translate=None, fillcolor=None)

パラメータ

引数名 説明
angle 回転角度を度数(°)で指定します。
resample 回転時の補間方法を指定します。
Image.NEAREST
Image.BILINEAR
Image.BICUBIC
(参考)画像のリサイズ、補間指定
expand Trueのとき、画像が回転しても画像全体が表示されるよう画像サイズを拡張します。
False(初期値)のとき、画像が回転するしたときにはみ出した部分が切り取られます。
ただし、画像回転の中心は画像の中心で、平行移動が無い事を想定しています。
center 回転の中心座標を(x, y)のタプルで指定します。
初期値:画像の中心
translate 回転後の移動量を(Tx, Ty)のタプルで指定します。
fillcolor 画像の外側の色を指定します。
初期値:黒
戻り値 回転した画像(PIL.Image)

サンプル

from PIL import Image

# 画像を開く
img = Image.open("image.bmp")

# 画像を回転する
img_rotate = img.rotate(30)

# 回転した画像の保存
img_rotate.save("image_rotate.bmp")

(実行結果)

元画像 回転画像

画像がはみ出さないようにする

expand = True に設定することで、画像がはみ出さないよう、画像サイズを自動で拡張し調整します。

(例)

img_rotate = img.rotate(30, expand = True)

(結果)

画像の外側の色の指定

画像の外側の色をfillcolorに指定します。

(例)

img_rotate = img.rotate(30, fillcolor = "Blue")

(結果)

参考

https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.rotate

https://pillow.readthedocs.io/en/stable/handbook/concepts.html#concept-filters

 

【Python/Pillow(PIL)】画像のリサイズ、補間指定

Pillowで画像(画像データ)を拡大/縮小するには、Imageクラスのresizeメソッドを用います。

構文

Image.resize(size, resample=None, box=None, reducing_gap=None)

パラメータ

引数名 説明
size リサイズ後の大きさを (幅, 高さ)のタプルで指定します。
resample リサイズ時の補間方法を指定します。
Image.NEAREST
Image.BOX
Image.BILINEAR
Image.HAMMING
Image.BICUBIC
Image.LANCZOS
画像の mode が 1, P や I;16などのビット指定モードのときは、Image.NEAREST が初期値
その他の場合、Image.BICUBICが初期値
box 画像を拡大/縮小する領域を(左, 上, 右, 下)の座標のタプルで指定します。
初期値は(0, 0, 画像の幅, 画像の高さ)の画像全体
cropメソッドの処理と同じ
reducing_gap リサイズ時の最適化(詳細わからず。。)
初期値: None (最適化なし)
戻り値 リサイズされた画像(PIL.Image)

サンプル

from PIL import Image

# 画像を開く
img = Image.open("Parrots.bmp")

# 画像を拡大/縮小する
img_resize = img.resize((64, 64))

# 切り抜いた画像の保存
img_resize.save("Parrots_resize.bmp")

(実行結果)

元画像 縮小画像

resample(補間方法)について

resampleの設定は、主に画像を拡大するときに画像の画素と画素の間の輝度値を求める事になるのですが、この時の求め方の方法を指定します。

この画素間の輝度値を求める方法は一般に補間と言いますが、補間については、下記のページを参照ください。

画素の補間(Nearest neighbor,Bilinear,Bicubic)の計算方法

Pillowでは、補間方法にImage.NEAREST, Image.BOX, Image.BILINEAR, Image.HAMMING,  Image.BICUBIC, Image.LANCZOSの方法を指定する事ができますが、

NEAREST→BOX→BILINEAR→HAMMING→BICUBIC→LANCZOS

の順で、キレイにリサイズすることができますが、処理時間は逆に遅くなるため、用途に応じてresampleの設定を行ってください。

個人的には、画像処理後の1画素1画素の輝度値を見せたいときはNEAREST、とりあえずのBILINEARという使い分けが多いです。

下図のように部分的に画像を拡大したときに、resampleの設定を変えると、どのようになるか?見てみたいと思います。

 

NEAREST BOX BILINEAR
HAMMING BICUBIC LANCZOS

拡大の時のキレイさは、NEAREST, BOX → HAMMING → BILEAR → BICUBIC → LANCZOS の順ですかね?

参考

https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.resize

https://pillow.readthedocs.io/en/stable/handbook/concepts.html#concept-filters

【Python/Pillow(PIL)】画像の一部を切り抜く

Pillowで画像の一部を切り抜くには、Imageクラスのcropメソッドを用います。

書式は

Image.crop(box=None)
引数 説明
box 切り抜く領域を(左, 上, 右, 下)の座標のタプルで指定します。

 

(サンプルプログラム)

from PIL import Image

# 画像を開く
img = Image.open("Parrots.bmp")

# 画像を切り抜く
img_roi = img.crop((146, 81, 253, 183)) # (left, upper, right, lower)

# 切り抜いた画像の保存
img_roi.save("Parrots_roi.bmp")

(実行結果)

元画像 切り抜き画像

(補足)指定座標について

画像を切り抜く座標の定義ですが、画像の幅がWidth、画像の高さがHeightとすると、
画像の左上が(0, 0)、画像の右下が(Width-1, Height-1)となります。

実際に切り抜かれる画像の領域は、左上の座標(left, upper) を含み、右下の座標(right, lower) の1画素内側の領域が切り抜かれます。

参考

https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.crop

【OpenCV/Python】imreadで画像ファイルが読み込めないときの対応

opencv-pythonのimreadで画像を開き、以下のようなフィルタ処理を行うプログラムを実行すると、エラーが表示される場合があります。

import cv2

# OpenCVで画像ファイルを開く
img = cv2.imread("Mandrill.bmp", cv2.IMREAD_UNCHANGED)

# ガウシアンフィルタ
dst = cv2.GaussianBlur(img, (3, 3), 0)

cv2.imshow("Image", dst)
cv2.waitKey(0)

エラー内容

OpenCV(4.5.5) D:\a\opencv-python\opencv-python\opencv\modules\imgproc\src\smooth.dispatch.cpp:617: error: (-215:Assertion failed) !_src.empty() in function ‘cv::GaussianBlur’

 

OpenCVの場合、エラー内容の最後の方を見ると、だいたいエラーの原因がわかる場合が多いのですが、今回のエラーの内容の場合

  !_src.empty() in function ‘cv::GaussianBlur’

の部分が大事です。

直訳的に言うと、

  ’cv::GaussianBlur’関数の入力画像(src)は空(empty)ではいけません。

と言われています。

入力画像が空になる原因は imread()関数で画像ファイルが読み込めない場合がほとんどなのですが、読み込めない原因は、主に以下の2つです。

 

●画像ファイルがみつからない。(カレントディレクトリに画像ファイルが無い)

●ファイル名、ディレクトリ名に日本語が含まれている

 

画像ファイルがみつからない場合

imread()関数で、ファイル名の部分に相対パスでファイル名を指定すると、カレントディレクトリにあるファイルを探しに行くため、画像ファイルがカレントディレクトリに無いと、画像ファイルを開く事ができません。

【対策】

①ファイル名を相対パスではなく、絶対パス(フルパス)で指定する。

(例)

img = cv2.imread(r"C:\temp\Mandrill.bmp", cv2.IMREAD_UNCHANGED)

 

②カレントディレクトリを確認し、カレントディレクトリにファイルを置く。

カレントディレクトリは以下のようにすると、確認ができます。

import os

# カレントディレクトリの表示
print(os.getcwd())

ファイル名、ディレクトリ名に日本語が含まれている場合

OpenCVでは日本語を扱う事ができません。

そのため、画像ファイル名においても日本語が含まれるとimread関数で画像ファイルの読み込みに失敗します。

日本語ファイル名の画像を開くには、OpenCVではなく、PillowやNumPyを使って画像ファイルを開き、OpenCVの画像形式である ndarray へ変換する事ができます。

Pillowで画像ファイルを開いた方が、さまざまな画像ファイルフォーマットに対応しているので、汎用性が高いと思います。

(参考)

【OpenCV/Python】日本語の画像ファイル読込・保存

【Windows11】マウスポインタ、ダブルクリックなどの設定

Windows11より前のWindowsではマウスの設定に関しては、コントロールパネルよりマウスの設定を行いましたが、Windows11でも同様の設定が出来ますが、別途、Windows11の設定から行うマウスの設定が追加されています。

 

Windows11での設定は、スタートボタンを右クリックし、設定をクリックします。

左側のBluetoothとデバイスを選択し、右側のマウスをクリックします。

すると、マウスの設定画面が開きます。

マウスの主ボタン

従来の主と副のボタンを切り替えると同じ機能ですが、通常のクリック(右クリックではなく)のボタンを設定します。通常はです。

マウスポインターの速度

マウスポインタの移動速度を設定します。

マウスホイールを回転させてスクロールする

マウスホイールを回転させたとき、複数行ずつ もしくは 1画面ずつ から選択します。

一度にスクロールする行数

マウスホイールを回転させたときにスクロールする行数を指定します。

ホバーしたときに非アクティブウィンドウをスクロールする

非アクティブウィンドウ(選択されていないウィンドウ)上で、マウスホイールを回転させたときに画面をスクロールするか?を設定します。

マウスの追加設定

従来のマウスの設定と同様に、マウスポインタの種類やダブルクリックの速度などを設定します。

上記のWindows11の設定と被る設定も多いのですが、最後に変更した設定が有効になります。

マウスポインター

マウスポインターのサイズを色を設定します。

マウスポインタのサイズが設定できますが、スクリーンキャプチャしたときのポインタの大きさは、キャプチャソフトによっては繁栄されない場合があります。

【Windows11】電源プラン(スリープ/シャットダウンの時間)の設定

Windowsでは、昔から一定時間で、モニタの電源を切ったり、スリープにしたり、シャットダウンしたりと出来ますが、Windows11においても同様の設定が出来ますが、Windows11からの設定方法と従来と同様の設定方法が混在している状況です。

Windows11での電源プランの設定では、設定出来る項目が少ないため、従来の方法も併せて設定する必要がありそうです。

Windows11からの電源オプション設定

スタートボタンを右クリックし、電源オプションをクリックします。

すると、画面とスリープ電源モードの設定ができます。

画面とスリープでは

●電源接続時に、次の時間が経過した後に画面の電源を切る

●電源接続時に、次の時間が経過した後にデバイスをスリープ状態にする

の状態にするまでの時間が設定できます。

電源モードでは、トップクラスの電力効率、バランス、最適なパフォーマンス の設定ができます。

 

従来の電源オプション設定

従来と同様の電源オプション設定はコントロールパネルから設定を行います。

コントロールパネルの表示はWindows11では少々わかりづらくなっているので、こちらを参照ください。

【Windows11】コントロールパネルの表示

コントロールパネルの表示は右上の表示方法の設定で変わってきますが、ここでは表示方法をカテゴリに設定した場合で説明します。

ハードウェアとサウンドをクリックします。

次に電源オプションをクリックします。

すると電源プランの バランス と 省電力 の切り替えができます。

さらに上の画面のプラン設定の変更をクリックすると、さらなる詳細の電源の設定ができます。

電源オプションをクリック後、最初に表示された画面の電源ボタンの動作を選択するをクリックすると

電源プランの作成をクリックすると

ディスプレイの電源を切る時間の指定コンピューターがスリープ状態になる時間を変更は、どちらも同じ画面ですが、以下の画面となります。