見出し画像

Koboldcpp用pngメタデータ編集コード公開しました

 GUI付きでKoboldcppにインポート可能なchub.aiPygmalionAIのダウンロードしたキャラクターのPNGファイルのメタデータを編集できて保存ができるコードを作成しました。

 一応、既存のでないかは探したのですが、長期間メンテされてないソフトしかありませんでしたので、Claude3-Opusと相談しながらコードを生成しました。Koboldcppに編集機能がついていれば、こんなコード不要だったのですが…

必須環境

 前提として、Pythonがインストールされている環境である必要があります。
 また、以下のライブラリをインストールしていない場合はインストールする必要があります。

pip install pillow
pip install pyqt5

 pillowはA1111webuiでも使われるpng用のライブラリです。pyqt5はGUI用のライブラリです。

Pythonコード

import sys
import base64
import json
from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog, QTextEdit, QPushButton, QVBoxLayout, QWidget, QLabel, QSpinBox
from PIL import Image, PngImagePlugin

class PNGMetadataEditor(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()
        self.current_file = ""
        self.isDarkMode = False

    def initUI(self):
        self.setWindowTitle("PNG Metadata Editor")
        self.setGeometry(100, 100, 600, 400)

        # Create widgets
        self.textEdit = QTextEdit()
        openButton = QPushButton("Open PNG")
        openButton.clicked.connect(self.openFile)
        saveButton = QPushButton("Save PNG")
        saveButton.clicked.connect(self.saveFile)
        saveAsButton = QPushButton("Save As PNG")
        saveAsButton.clicked.connect(self.saveAsFile)

        # Create font size label and spin box
        fontSizeLabel = QLabel("Font Size:")
        self.fontSizeSpinBox = QSpinBox()
        self.fontSizeSpinBox.setMinimum(6)
        self.fontSizeSpinBox.setMaximum(24)
        self.fontSizeSpinBox.setValue(12)
        self.fontSizeSpinBox.valueChanged.connect(self.changeFontSize)

        # Create dark mode toggle button
        self.darkModeButton = QPushButton("Dark Mode")
        self.darkModeButton.clicked.connect(self.toggleDarkMode)

        # Create layout
        layout = QVBoxLayout()
        layout.addWidget(self.textEdit)
        layout.addWidget(openButton)
        layout.addWidget(saveButton)
        layout.addWidget(saveAsButton)
        layout.addWidget(fontSizeLabel)
        layout.addWidget(self.fontSizeSpinBox)
        layout.addWidget(self.darkModeButton)

        # Create central widget and set layout
        centralWidget = QWidget()
        centralWidget.setLayout(layout)
        self.setCentralWidget(centralWidget)

    def openFile(self):
        fileName, _ = QFileDialog.getOpenFileName(self, "Open PNG File", "", "PNG Files (*.png)")
        if fileName:
            self.current_file = fileName
            with Image.open(fileName) as img:
                metadata = img.text
                json_data = None
                if "chara" in metadata:
                    base64_data = metadata["chara"]
                    json_data = base64.b64decode(base64_data).decode('utf-8')
                elif "data" in metadata:
                    json_data = metadata["data"]
                
                if json_data:
                    self.textEdit.setPlainText(json_data)
                else:
                    self.textEdit.setPlainText("No JSON metadata found in the PNG file.")

    def saveFile(self):
        if self.current_file:
            self.saveMetadata(self.current_file)
        else:
            self.saveAsFile()

    def saveAsFile(self):
        fileName, _ = QFileDialog.getSaveFileName(self, "Save PNG File", "", "PNG Files (*.png)")
        if fileName:
            self.saveMetadata(fileName)

    def saveMetadata(self, fileName):
        try:
            json_data = self.textEdit.toPlainText()
            base64_data = base64.b64encode(json_data.encode('utf-8')).decode('utf-8')
            metadata = PngImagePlugin.PngInfo()
            metadata.add_text("chara", base64_data)
            
            with Image.open(self.current_file) as img:
                img.save(fileName, pnginfo=metadata)
        except json.JSONDecodeError:
            self.textEdit.setPlainText("Invalid JSON format in the text edit.")
        except Exception as e:
            self.textEdit.setPlainText(f"Error saving PNG file: {str(e)}")

    def changeFontSize(self, fontSize):
        font = self.textEdit.font()
        font.setPointSize(fontSize)
        self.textEdit.setFont(font)

    def toggleDarkMode(self):
        self.isDarkMode = not self.isDarkMode
        if self.isDarkMode:
            app.setStyleSheet("""
                QWidget {
                    background-color: #333333;
                    color: #ffffff;
                }
                QTextEdit {
                    background-color: #444444;
                    color: #ffffff;
                }
                QPushButton {
                    background-color: #555555;
                    color: #ffffff;
                }
            """)
        else:
            app.setStyleSheet("")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    editor = PNGMetadataEditor()
    editor.show()
    sys.exit(app.exec_())

 このコード適当なフォルダにPNGMetadataEditor.pyなど適当な名前を付けて保存して、実行すれば以下のような画面が出てきます。

画像
PNGMetadataEditor

 Open PNGで対応したPNGファイルが開けて、JSON形式のメタデータが読み込まれます。
textを直接編集できるので適宜変更してください。ただし、書式を誤って編集してもエラーメッセージを出す機能とかはないので、各自自己責任で編集してください(基本的にミスしてもkoboldcppでの読み込み時にErrorが出るだけです)。

 Save PNGで変更した内容が、直接開いたPNGファイルに書き込まれます。

 Save As PNGで名前を変更してのPNGの保存ができます。
Font Sizeの上下矢印ボタンか、直接入力で6-24の間でフォントサイズを変更できます。

        self.fontSizeSpinBox.setValue(12)

 この部分の数値を書き換えれば、起動時のデフォルトのフォントサイズを変更も可能です。
Dark Modeを押すと、背景が黒く文字が白く出来ます。もう一回押すともとに戻ります。

 何か問題があれば、ツイッターやディスコ(@willlion)までご連絡くださいm(_ _)m
 自分はSillyTavernを使用して(node.jsのインストールが必要)、koboldcppをAPIとして使用していて直接色々編集できるので、基本的に今後のメンテの予定はありませんので、予めご了承ください。また、自由に改変再配布をしてもらって構いませんが、ここにオリジナルがあることを明記してください。

 表紙画像はStableCascadeで「png metadata editing code for Koboldcpp published, masterpiece, best quality」という、表題英訳をそのまま突っ込んでできた画像です。最初は男性の顔のドアップで、集合体恐怖症の人にはアウトなブツブツがある顔だったので2枚目に生成した画像です。

いいなと思ったら応援しよう!

コメント

コメントするには、 ログイン または 会員登録 をお願いします。
Koboldcpp用pngメタデータ編集コード公開しました|ailora will
word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word

mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1