[C++] 19 FLTK:setTextとgetTextに相当する機能

FLTKでJava・SwingのsetTextとgetTextに相当する機能を探すのに少々時間がかかりました。

Swing : setText FLTK : value(const char *)
Swing : getText FLTK : value()

value関数が両方の機能を担っています。Fl_Inputの文字列を全削除する時はvalue(“”)と書きます。value()では消去ではなくコピーするので要注意です。こういった仕組みは軽量高速化のために余計な関数を設定しないという思想の表れと理解しました。

一方、Qtは速さを犠牲にしてsetTextやgetTextを導入しています。JavaやPythonからプログラミングの世界に入った方々はQtに親しみを覚え、FLTKには違和感しかないでしょう。

私もvalueの機能を知った時は脱力しましたが、一貫した設計思想に感銘を受けました。

ところで、新興言語のRustがFLTKを積極的に活用しているようでそちらに興味が向きはじめました。

C++版が完成したら次は学習を兼ねたRust版作成を課題候補にしておきます。

<関連する関数>

void inspect(void){
    output_line->insert("inspect\n");
    const char *path = input_line->value();
    output_line->insert(path);
    output_line->insert("\n");
}
void resize(void){
    output_line->insert("resize\n");
}
void icns(void){
    output_line->insert("icns\n");
}

void execute_cb(Fl_Widget*, void*) {
    onoff_inspect = inspect_rbtn->value(); 
    onoff_resize = resize_rbtn->value();
    onoff_icns = icns_rbtn->value();

    if (onoff_inspect == 1){
        inspect();
    } else if (onoff_resize == 1){
        resize();
    } else {
        icns();
    }
}
void clear_cb(Fl_Widget*, void*) {
    input_line->value("");
}

[C++] 18 FLTK:ドラッグ&ドロップ

[M1 Mac, Big Sur 11.6.5, FLTK 1.3.8]

一つの山場と予想されたドラッグ&ドロップですが、これについては有益なネット情報がありましたので拝借しました。

この40行のコードを書こうと思ったら今の私なら半日仕事です。とても助かりました。参考コードでauto型の存在を初めて知りました(C++11から採用)。Javaもvar型で型推論できますが、使ったことはありませんでした。

Fl_InputとセットになったFL_Box継承のBoxクラスは他のウィジェットが見えるように invisible(FL_NO_BOX)にするなど私なりに少し工夫を入れています。

ここまででappファイルのサイズはたったの942KBです。この分だと最終的には2,3MB以内に収まる感じがします。PyQt6版の100分の1です。

期待通りの爆速軽量アプリに仕上がりそうです。

<関連する関数とクラス>
class Box : Fl_Box {
  Fl_Input* input_line;
  public:
    Box(int, int, int, int, Fl_Input*);
  private:
    auto handle(int) -> int override;
};

Box::Box(int x, int y, int width, int height, Fl_Input* input) : Fl_Box(FL_NO_BOX, x, y, width, height, "") {
   this->input_line = input;
}

auto Box::handle(int event) -> int {
   switch (event) {
     case FL_DND_DRAG:
     case FL_DND_ENTER:
     case FL_DND_RELEASE:
     return 1;

     case FL_PASTE:
       input_line->value(Fl::event_text());
     return 1;

     default:
     return Fl_Box::handle(event);
  }
}

<mainクラスの一部>
input_line = new Fl_Input(50,10,220,25,"");

Box *box = new Box(0, 0, 360,220, input_line);

参考サイト

[C++] 17 FLTK:バージョン1.3.8のビルド&インストール

[M1 Mac, Big Sur 11.6.5, FLTK 1.3.8(24/2/13現在 1.3.9)]

HomebrewからApple Silicon用ビルド済みバイナリが配布されていますが、自分でビルドしたかったのでGitHubにあるバージョン1.4.0のREADME.macOS.mdを読みながら試してみました。

1.4.0ではビルドはできたもののインストールに失敗、安定版の1.3.8ではうまくいきました。

これでFLTKを自分の好きなように改変することも可能になりました。

<FLTK 1.3.8のインストール方法>
事前にCMakeなど必要なツールをインストールしておく。

1.公式サイトからバージョン1.3.8のtar.gzファイルをダウンロードする。

2.ファイルを解凍してfltkフォルダ内にディレクトリを作成し、cmakeコマンドを実行する。

mkdir build
cd build
mkdir Makefile
cd Makefile
cmake -G "Unix Makefiles" \
    -D OPTION_USE_SYSTEM_LIBJPEG=Off \
    -D OPTION_USE_SYSTEM_ZLIB=Off \
    -D OPTION_USE_SYSTEM_LIBPNG=Off \
    -D OPTION_USE_THREADS=ON \
    ../..

2024/2/13 オプション追加:
-D OPTION_USE_THREADS=ONによりマルチスレッドサポートを有効にする。Fl::lockなどが使えるようになる。

3.ビルドする。

make

4.デモアプリを起動する(省略可)。

open bin/test/demo.app

5.インストールする。[/usr/local/bin]

sudo make install

[C++] 16 FLTK:Callback

[M1 Mac, Big Sur 11.6.5, FLTK 1.3.8]

C++のCallback(JavaでいうところのActionListener)がうまくいかずに難航しています。

とりあえずヘッダファイルは書かずにソースファイルだけで簡単なCallbackができるようにしました。下記コードでは実行ボタンを押すとラジオボタン3個のbool値がFl_Multiline_Outputに出力されます。

あとFl_Multiline_Outputへの追加出力がaddやappendでは出来ません。該当するメンバ関数がなかなか見つかりませんでした。Fl_Input_という目立たない上位クラスにinsertを見つけた時は若干イラッとしました。

ユーザーが発信しているネット情報は今回の内容についてはほぼ皆無でした。ドキュメントやソースコード、公式サイト、開発当事者のサンプルコード公開サイトなどを参考に自力での解決が求められます。どうしても困ったら公式サイトの掲示板に投稿するのもありでしょう。

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Tabs.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Widget.H>
#include <FL/Fl_Tooltip.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Input.H>
#include <FL/Fl_Round_Button.H>
#include <FL/Fl_Radio_Round_Button.H>
#include <FL/Fl_Hor_Slider.H>
#include <FL/Fl_Multiline_Output.H>
#include "FL/Enumerations.H"

Fl_Window *window;
Fl_Box *file;
Fl_Input *input;
Fl_Group *radio_btns;
Fl_Multiline_Output *output;
Fl_Radio_Round_Button *inspect;
Fl_Radio_Round_Button *resize;
Fl_Radio_Round_Button *icns;
Fl_Box *width_label;
Fl_Input *width;
Fl_Box *height_label;
Fl_Input *height;
Fl_Button *execution;
Fl_Button *clear;

int onoff_inspect;
int onoff_resize;
int onoff_icns;

void execute(void){
    onoff_inspect = inspect->value(); 
    onoff_resize = resize->value();
    onoff_icns = icns->value();

    std::string tmp = std::to_string(onoff_inspect);
    char const *num_char = tmp.c_str();
    std::string tmp2 = std::to_string(onoff_resize);
    char const *num_char2 = tmp2.c_str();
    std::string tmp3 = std::to_string(onoff_icns);
    char const *num_char3 = tmp3.c_str();
    output->insert(num_char);
    output->insert(num_char2);
    output->insert(num_char3);
    output->insert("\n");
}

void test(Fl_Widget*,void*) {
    execute(); 
}

int main(int argc, char **argv) {
    window = new Fl_Window(100,100,360,220,"IMAGE INSPECTOR");
    window->color(fl_rgb_color(112,128,144));
    
    // file
    file = new Fl_Box(15,15,35,16,"File");
    file->labelsize(14);
    file->labelcolor(fl_rgb_color(255,239,213));
    file->align(Fl_Align(FL_ALIGN_INSIDE|FL_ALIGN_LEFT));

    input = new Fl_Input(50,10,220,25,"");

    radio_btns = new Fl_Group(50,40,90,70,"");{
        radio_btns->labelsize(12);
        // Inspect
        inspect = new Fl_Radio_Round_Button(50,40,90,20,"Inspect");
        inspect->labelcolor(fl_rgb_color(255,239,213));
        inspect->setonly();
        
        // Resize
        resize = new Fl_Radio_Round_Button(50,65,90,20,"Resize");
        resize->labelcolor(fl_rgb_color(255,239,213));
        
        // icns作成
        icns = new Fl_Radio_Round_Button(50,90,90,20,"icns作成");
        icns->labelcolor(fl_rgb_color(255,239,213));
        
    }
    radio_btns->end();

    width_label = new Fl_Box(135,70,15,10,"W");
    width_label->labelsize(12);
    width_label->labelcolor(fl_rgb_color(255,239,213));
    width_label->align(Fl_Align(FL_ALIGN_INSIDE|FL_ALIGN_LEFT));
    
    width = new Fl_Input(155,65,45,20,"");

    height_label = new Fl_Box(205,70,15,10,"H");
    height_label->labelcolor(fl_rgb_color(255,239,213));
    height_label->labelsize(12);
    height_label->align(Fl_Align(FL_ALIGN_INSIDE|FL_ALIGN_LEFT));
    
    height = new Fl_Input(220,65,45,20,"");

    execution = new Fl_Button(290,10,50,30,"実行");
    execution->color(fl_rgb_color(112,128,144));
    execution->labelcolor(fl_rgb_color(255,239,213));
    execution->labelsize(14);
    execution->callback(test);
    execution->when(FL_WHEN_RELEASE); // 省略可

    clear = new Fl_Button(290,50,50,30,"クリア");
    clear->color(fl_rgb_color(112,128,144));
    clear->labelcolor(fl_rgb_color(255,239,213));
    clear->labelsize(14);

    output = new Fl_Multiline_Output(50,115,240,100,"");

    window->end();
    window->show(argc, argv);
    return Fl::run();
}

[C++] 15 FLTK:Fl_Radio_Round_Button

[M1 Mac, Big Sur 11.6.5, FLTK 1.3.8]

PyQt6アプリのGUI画面を移植しました。

Fl_Round_Buttonはラジオボタン機能を持っていないただの丸いボタンだったので、Fl_Radio_Round_Buttonに変更しました。

ガワだけというのもありますが、爆速な起動に改めて感動しました。こうじゃないとC++を扱う意味がありません。

FLTKのあまりの癖の強さにGTKへの移行を考えたのですが、メンバ関数名の余計な変更など向こうの方がやりたい放題でした。やっぱりこちらのお世話になります。

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Tabs.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Widget.H>
#include <FL/Fl_Tooltip.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Input.H>
#include <FL/Fl_Radio_Round_Button.H>
#include <FL/Fl_Hor_Slider.H>
#include <FL/Fl_Multiline_Output.H>

int main(int argc, char **argv) {
    Fl_Window *window = new Fl_Window(100,100,360,220,"IMAGE INSPECTOR");
    window->color(fl_rgb_color(112,128,144));
    
    // File
    Fl_Box *file = new Fl_Box(15,15,35,16,"File");
    file->labelsize(14);
    file->labelcolor(fl_rgb_color(255,239,213));
    file->align(Fl_Align(FL_ALIGN_INSIDE|FL_ALIGN_LEFT));

    Fl_Input *file_input = new Fl_Input(50,10,220,25,"");

    Fl_Group *radio_btns = new Fl_Group(50,40,90,70,"");{
        radio_btns->labelsize(12);
        // Inspect
        Fl_Radio_Round_Button *inspect = new Fl_Radio_Round_Button(50,40,90,20,"Inspect");
        inspect->labelcolor(fl_rgb_color(255,239,213));
        inspect->setonly();
        // Resize
        Fl_Radio_Round_Button *resize = new Fl_Radio_Round_Button(50,65,90,20,"Resize");
        resize->labelcolor(fl_rgb_color(255,239,213));
        // icns作成
        Fl_Radio_Round_Button *icns = new Fl_Radio_Round_Button(50,90,90,20,"icns作成");
        icns->labelcolor(fl_rgb_color(255,239,213));
    }
    radio_btns->end();

    Fl_Box *width_label = new Fl_Box(135,70,15,10,"W");
    width_label->labelsize(12);
    width_label->labelcolor(fl_rgb_color(255,239,213));
    width_label->align(Fl_Align(FL_ALIGN_INSIDE|FL_ALIGN_LEFT));
    
    Fl_Input *width = new Fl_Input(155,65,45,20,"");

    Fl_Box *height_label = new Fl_Box(205,70,15,10,"H");
    height_label->labelcolor(fl_rgb_color(255,239,213));
    height_label->labelsize(12);
    height_label->align(Fl_Align(FL_ALIGN_INSIDE|FL_ALIGN_LEFT));
    
    Fl_Input *height = new Fl_Input(220,65,45,20,"");

    Fl_Button *execution = new Fl_Button(290,10,50,30,"実行");
    execution->color(fl_rgb_color(112,128,144));
    execution->labelcolor(fl_rgb_color(255,239,213));
    execution->labelsize(14);

    Fl_Button *clear = new Fl_Button(290,50,50,30,"クリア");
    clear->color(fl_rgb_color(112,128,144));
    clear->labelcolor(fl_rgb_color(255,239,213));
    clear->labelsize(14);

    Fl_Multiline_Output *output = new Fl_Multiline_Output(50,115,240,100,"");

    window->end();
    window->show(argc, argv);
    return Fl::run();
}
Fl_Radio_Round_Button
Fl_Round_Buttonではグループ化できず

[C++] 14 FLTK:Fl_Round_Button

[M1 Mac, Big Sur 11.6.5, FLTK 1.3.8]

PyQt6アプリのQt5への移植はシグナル/スロットの接続がうまくいかず断念しました。

Electron(JavaScript)といいQt5といいアプリ内通信に苦手意識があります。シグナルがどう伝わっていくのか、printコマンドを随所に挟んで解明していくというような手段を会得しないとどうしようもないです。

ところでQt Creatorは便利ですが想像以上の重さでした。特にクリーンの遅さには閉口しましたね。また他のIDE以上に書かされている感が強く、結局馴染めなかったです。

仕方なく9日ぶりにFLTKに戻ってきて、こちらに移植しはじめました。しかし思うのですが、FLTKのラジオボタンが四角いというのは色々尋常ではないです。FLTKが芸術作品であれば何も言いませんが、ユーザーが活用するためのものではないんですかね。

面倒でもC言語で一から開発する方が精神衛生的に良いのではないか、と思い始めています。メインはJava・Python、余興でC言語というのが私には合っているのかもしれません。

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Tabs.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Widget.H>
#include <FL/Fl_Tooltip.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Input.H>
#include <FL/Fl_Round_Button.H>
#include <FL/Fl_Hor_Slider.H>

int main(int argc, char **argv) {
    Fl_Window *window = new Fl_Window(100,100,360,220,"IMAGE INSPECTOR");
    window->color(fl_rgb_color(112,128,144));

    Fl_Group *area = new Fl_Group(0,0,360,220,"");{
        // file
        Fl_Box *file = new Fl_Box(15,15,35,16,"File");
        file->labelsize(12);
        file->align(Fl_Align(FL_ALIGN_INSIDE|FL_ALIGN_LEFT));
        Fl_Input *file_input = new Fl_Input(50,10,220,25,"");

        // inspect
        Fl_Round_Button *inspect = new Fl_Round_Button(50,40,90,20,"Inspect");
        inspect->labelsize(12);

    }
    area->end();
    
    window->end();
    window->show(argc, argv);
    return Fl::run();
}

[Python]332 画像ファイルの全ピクセル色情報をCSV化

[M1 Mac, Big Sur 11.6.5, Python 3.10.0]

[Python]330に関連して画像ファイルの全ピクセル色情報を取得しCSVファイルとして出力するコードを記録しておきます。

from PIL import Image
import numpy as np
import csv

img_array = np.array(Image.open("test.png"))

# 全ピクセルの色情報を取得
list_rgba = img_array[:, :, (0, 1, 2, 3)]
# list_rgb = img_array[:, :, (0, 1, 2)] jpgの場合

with open("test.csv", 'w') as f:
    writer = csv.writer(f,lineterminator='\n')
    writer.writerows(list_rgba)

[C++] 13 Qt5 GUIアプリ作成 QtWidgets

[M1 Mac, Big Sur 11.6.5]

GUI画面が出来上がりました。基本的にはPyQt6版のコードをコピペしてメソッドのピリオドを->に書き換えただけです。オブジェクト作成の際にnewを付けたり、行の最後にセミコロンを入れるのを忘れがちでした。

次はボタン動作ですが、Qtではシグナル/スロットという仕組みになっています。ざっと説明を読んだものの今ひとつピンとこないです。

#include <QtWidgets/QApplication>
#include <QtWidgets/QLabel>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QButtonGroup>
#include <QtWidgets/QRadioButton>
#include <QtWidgets/QTextEdit>
#include <QtWidgets/QWidget>
#include <QMainWindow>

int main(int argc, char *argv[]){
    QApplication app(argc, argv);

    QMainWindow* mainWin = new QMainWindow();
    mainWin->setGeometry(100,100,360,220);
    mainWin->setWindowTitle("IMAGE INSPECTOR");
    mainWin->setStyleSheet("background: '#708090';");

    QLabel* file = new QLabel(mainWin);
    file->setText("File");
    file->setGeometry(15,15,35,16);
    file->setStyleSheet("foreground: '#FFFAFA';font-size:12px;");

    QLineEdit* input= new QLineEdit(mainWin);
    input->setGeometry(50,10,220,25);
    
    QPushButton* execution = new QPushButton(mainWin);
    execution->setText("実行");
    execution->setGeometry(290,10,50,30);
    execution->setStyleSheet("foreground: '#FFFAFA';font-size:12px;");
    QPushButton::connect(execution, SIGNAL( clicked() ),&app, SLOT(quit()) );

    QPushButton* clear = new QPushButton(mainWin);
    clear->setText("クリア");
    clear->setGeometry(290,50,50,30);
    clear->setStyleSheet("foreground: '#FFFAFA';font-size:12px;");

    QButtonGroup* rbtns = new QButtonGroup(mainWin);
    QRadioButton* inspect = new QRadioButton(mainWin);
    inspect->setText("Inspect");
    inspect->setGeometry(50,40,90,20);
    inspect->setChecked(true);
    rbtns->addButton(inspect);
    
    QRadioButton* resize_img = new QRadioButton(mainWin);
    resize_img->setText("Resize");
    resize_img->setGeometry(50,65,90,20);
    rbtns->addButton(resize_img);

    QLabel* width_label = new QLabel(mainWin);
    width_label->setText("W");
    width_label->setGeometry(135,70,15,10);
    width_label->setStyleSheet("font-size:10px;");
    
    QLineEdit* width= new QLineEdit(mainWin);
    width->setGeometry(155,65,45,20);
    
    QLabel* height_label = new QLabel(mainWin);
    height_label->setText("H");
    height_label->setGeometry(205,70,15,10);
    height_label->setStyleSheet("font-size:10px;");
        
    QLineEdit* height= new QLineEdit(mainWin);
    height->setGeometry(220,65,45,20);
        
    QRadioButton* icns = new QRadioButton(mainWin);
    icns->setText("icns作成");
    icns->setGeometry(50,90,90,20);
    icns->setToolTip("PNG file[2048*2048,72px] required");
    rbtns->addButton(icns);
    
    QTextEdit* output = new QTextEdit(mainWin);
    output->setGeometry(50,115,240,100);
    
    mainWin->show();
    return app.exec();
}

[Python]331 PyQt6 icnsファイル作成コード修正

[M1 Mac, Big Sur 11.6.5, Python 3.10.0]

[Python]329のコードを修正しました。以下の通りになります。

修正前のコードでも動作しますが、jpgファイルへの変換により透過部分が黒くなる上に、不可逆圧縮することで図形周辺画素の色データにズレが生じています。pngのままでも解像度を上げられるのでその方が良いでしょう。

前回330の記事でnumpyを使った透過部分黒色化対策方法を紹介しましたが、pngのままicnsファイルを作れるようになったためnumpyは不要になりました。Qt5への移植もスムーズに進みそうです。

from PIL import Image
import subprocess, os
from PyQt6.QtWidgets import QDialog,QPushButton,QLabel
from PyQt6.QtCore import Qt

class MakeIcns():
    def make(self, filepath0, window):
        img = Image.open(filepath0)
        print(str(img.size))
        if str(img.size)=="(2048, 2048)": # 2048*2048 dpi=72
            filepath1 = ".".join(filepath0.split(".")[:-1]) + "1.png" # 1024*1024 dpi=144
            filepath2 = ".".join(filepath0.split(".")[:-1]) + "2.png" # 512*512 dpi=72
            filedir = "/".join(filepath0.split("/")[:-1]) + "/" + (filepath0.split("/")[-1]).split(".")[-2] + ".iconset/"
            
            os.mkdir(filedir)

            img = Image.open(filepath0)
            
            # 1024*1024 dpi=144
            img_resize2 = img.resize((1024,1024))
            img_resize2.save(filepath1,dpi = (144, 144))
            
            # 512*512 dpi=72
            img_resize = img.resize((512,512))
            img_resize.save(filepath2,dpi = (72, 72))

            pixels = [32, 64, 256, 512, 1024]
            pixels2 = [16, 32, 128, 256, 512]
            filepaths = ['icon_16x16@2x.png','icon_32x32@2x.png','icon_128x128@2x.png','icon_256x256@2x.png','icon_512x512@2x.png']
            filepaths2 = ['icon_16x16.png','icon_32x32.png','icon_128x128.png','icon_256x256.png','icon_512x512.png']

            # dpi=144の各種pngファイル作成
            for pixel,file in zip(pixels,filepaths):
                img = Image.open(filepath1)
                img_resize = img.resize((pixel,pixel))
                img_resize.save(filedir + file, dpi = (144, 144))
                img.close()

            # dpi=72の各種pngファイル作成    
            for pixel,file in zip(pixels2,filepaths2):
                img = Image.open(filepath2)
                img_resize = img.resize((pixel,pixel))
                img_resize.save(filedir + file, dpi = (72, 72))
                img.close()

            # icnsファイル作成
            dir = "/".join(filepath0.split("/")[:-1])
            iconset = (filepath0.split("/")[-1]).split(".")[-2] + ".iconset"
            cmd = f'iconutil -c icns {iconset}'
            subprocess.run(cmd, cwd=dir,shell=True)
            
            os.remove(filepath1)
            os.remove(filepath2)
        else:
            MakeIcns.showDialog(self,window)
            
    def showDialog(self,window):
        dlg = QDialog(window)
        dlg.setFixedSize(250,100)
        label = QLabel('This file is invalid.\nPNG file[2048*2048,72dpi] required',dlg)
        label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        label.move(15,20)
        label.setStyleSheet('font-size:14px')
        btn = QPushButton("OK",dlg)
        btn.move(90,60)
        def action():
            dlg.close()
        btn.released.connect(action)
        dlg.setWindowTitle("Attention")
        dlg.exec()

[Python]330 pngファイル透過部黒色化への対策

[M1 Mac, Big Sur 11.6.5, Python 3.10.0]

pngファイルをjpgファイルに変換すると透過部が黒色になります。pngファイルの色情報から透明度が削除されたためです。

pngファイル RGBA(0, 0, 0, 0) 透明な黒[見た目は無色透明]

jpgファイル RGB(0, 0, 0) = RGBA(0, 0, 0, 255) 不透明な黒

対策としてpngファイルに再変換された画像の黒っぽい画素についてアルファ値を255から0に置き換えました。自分としては会心の出来ですが、コードにしてみるとあっさりしたものです。ちなみに画像はAdobe Illustratorで作ってみたアプリのアイコンです。フォントは自製しました。無料期間が終わったらどうするか悩みどころです。

jpgの場合は黒を白に変換するなどします。下記コード該当部分をnp.put(pixel,0,255)などに置き換えればできるはずです。

ただ上記の方法では元画像の絵の部分に黒が含まれていればそこも透過してしまいます。その場合は元画像の画素でアルファ値が0のものについて行列インデックスを変数化する、あるいは透過部分を絵にはない色に変換しておく、などの処置が必要です。

numpyと同じことがC++のライブラリでできないか、あるいはC++からnumpyを使えないか調査を進めています。

from PIL import Image
import numpy as np

# pngファイルの色情報を読み込む [red, green, blue, alpha]
img_array = np.array(Image.open('ImageInspector1.png'))

# 黒に近い画素のアルファ値を0にして透過させる
for row in img_array:
    for pixel in row:
        if pixel[0] <= 70 and pixel[1] <= 70 and pixel[2] <= 70 :
            np.put(pixel,3,0)
            
img = Image.fromarray(img_array)

file = "ImageInspector2.png"
img.save(file)
対策前
対策後