initial commit

This commit is contained in:
Cyberes 2022-11-13 16:17:15 -07:00
parent 70ea0fbb98
commit ef4a563c4e
7 changed files with 630 additions and 2 deletions

3
.gitignore vendored
View File

@ -1,3 +1,5 @@
.idea/
# ---> Python
# Byte-compiled / optimized / DLL files
__pycache__/
@ -137,4 +139,3 @@ dmypy.json
# Cython debug symbols
cython_debug/

204
Bulk Image Tagger qt5.ui Normal file
View File

@ -0,0 +1,204 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>632</width>
<height>447</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Bulk Image Tagger</string>
</property>
<property name="animated">
<bool>false</bool>
</property>
<property name="documentMode">
<bool>false</bool>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QGraphicsView" name="graphicsView">
<property name="geometry">
<rect>
<x>10</x>
<y>11</y>
<width>611</width>
<height>311</height>
</rect>
</property>
</widget>
<widget class="QComboBox" name="comboBox">
<property name="geometry">
<rect>
<x>10</x>
<y>370</y>
<width>181</width>
<height>25</height>
</rect>
</property>
<item>
<property name="text">
<string>Premade Prompts</string>
</property>
</item>
<item>
<property name="text">
<string>example1</string>
</property>
</item>
</widget>
<widget class="QPushButton" name="pushButton">
<property name="geometry">
<rect>
<x>530</x>
<y>370</y>
<width>89</width>
<height>25</height>
</rect>
</property>
<property name="text">
<string>Save</string>
</property>
</widget>
<widget class="QPushButton" name="pushButton_2">
<property name="geometry">
<rect>
<x>400</x>
<y>370</y>
<width>31</width>
<height>25</height>
</rect>
</property>
<property name="text">
<string>→</string>
</property>
</widget>
<widget class="QPushButton" name="pushButton_3">
<property name="geometry">
<rect>
<x>360</x>
<y>370</y>
<width>31</width>
<height>25</height>
</rect>
</property>
<property name="text">
<string>←</string>
</property>
</widget>
<widget class="QPushButton" name="pushButton_4">
<property name="geometry">
<rect>
<x>440</x>
<y>370</y>
<width>89</width>
<height>25</height>
</rect>
</property>
<property name="text">
<string>Delete</string>
</property>
</widget>
<widget class="QLineEdit" name="lineEdit">
<property name="geometry">
<rect>
<x>10</x>
<y>330</y>
<width>611</width>
<height>31</height>
</rect>
</property>
<property name="placeholderText">
<string>Enter prompt here</string>
</property>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>632</width>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionOpen"/>
<addaction name="actionQuit"/>
<addaction name="separator"/>
<addaction name="actionSkip"/>
<addaction name="actionNext"/>
<addaction name="actionDelete"/>
<addaction name="actionSave"/>
</widget>
<addaction name="menuFile"/>
</widget>
<widget class="QStatusBar" name="statusBar">
<property name="sizeGripEnabled">
<bool>false</bool>
</property>
</widget>
<action name="actionQuit">
<property name="text">
<string>Quit</string>
</property>
<property name="shortcut">
<string>Ctrl+Q</string>
</property>
</action>
<action name="actionSave">
<property name="text">
<string>&amp;Save</string>
</property>
<property name="shortcut">
<string>Ctrl+S</string>
</property>
</action>
<action name="actionSkip">
<property name="text">
<string>Skip</string>
</property>
<property name="shortcut">
<string>Ctrl+B</string>
</property>
</action>
<action name="actionOpen">
<property name="text">
<string>Open Folder</string>
</property>
<property name="shortcut">
<string>Ctrl+O</string>
</property>
</action>
<action name="actionNext">
<property name="text">
<string>&amp;Next</string>
</property>
<property name="shortcut">
<string>Ctrl+N</string>
</property>
</action>
<action name="actionDelete">
<property name="text">
<string>&amp;Delete</string>
</property>
<property name="shortcut">
<string>Del</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -1,3 +1,15 @@
# bulk-image-tagger
Workflow for tagging thousands of images.
_Workflow for tagging thousands of images_
**Use**
```bash
pip install PyQt5 opencv-python-headless numpy
./launch.sh
```
**Develop**
```bash
sudo apt install qttools5-dev-tools
pip install PyQt5 opencv-python-headless numpy
```

22
application/app.py Normal file
View File

@ -0,0 +1,22 @@
import sys
from PyQt5 import QtGui
from PyQt5.QtWidgets import QApplication, QWidget, QInputDialog, QLineEdit, QFileDialog, QMainWindow
from main_window import Ui_MainWindow
class Window(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
if __name__ == "__main__":
app = QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec())

281
application/main_window.py Normal file
View File

@ -0,0 +1,281 @@
# -*- coding: utf-8 -*-
import os
from glob import glob
from io import BytesIO
import cv2
import numpy as np
from PIL import Image
from PIL.ImageQt import ImageQt
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QFileDialog, QLabel
# Form implementation generated from reading ui file 'Bulk Image Tagger qt5.ui'
#
# Created by: PyQt5 UI code generator 5.14.1
def get_folder(folder):
glob_pattern = sorted(glob(os.path.join(folder, '*')), key=os.path.getctime)
out = []
for i in range(len(glob_pattern)):
if os.path.splitext(os.path.basename(glob_pattern[i]))[-1] in ['.jpg', '.jpeg', '.png']:
out.append(glob_pattern[i])
return out
def resizeAndPad(img, size, padColor=0):
h, w = img.shape[:2]
sh, sw = size
# interpolation method
if h > sh or w > sw: # shrinking image
interp = cv2.INTER_AREA
else: # stretching image
interp = cv2.INTER_CUBIC
# aspect ratio of image
aspect = w / h # if on Python 2, you might need to cast as a float: float(w)/h
# compute scaling and pad sizing
if aspect > 1: # horizontal image
new_w = sw
new_h = np.round(new_w / aspect).astype(int)
pad_vert = (sh - new_h) / 2
pad_top, pad_bot = np.floor(pad_vert).astype(int), np.ceil(pad_vert).astype(int)
pad_left, pad_right = 0, 0
elif aspect < 1: # vertical image
new_h = sh
new_w = np.round(new_h * aspect).astype(int)
pad_horz = (sw - new_w) / 2
pad_left, pad_right = np.floor(pad_horz).astype(int), np.ceil(pad_horz).astype(int)
pad_top, pad_bot = 0, 0
else: # square image
new_h, new_w = sh, sw
pad_left, pad_right, pad_top, pad_bot = 0, 0, 0, 0
# set pad color
if len(img.shape) == 3 and not isinstance(padColor, (list, tuple, np.ndarray)): # color image but only one color provided
padColor = [padColor] * 3
# scale and pad
scaled_img = cv2.resize(img, (new_w, new_h), interpolation=interp)
scaled_img = cv2.copyMakeBorder(scaled_img, pad_top, pad_bot, pad_left, pad_right, borderType=cv2.BORDER_CONSTANT, value=padColor)
return scaled_img
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
self.folder_contents = None
self.folder_contents_i = None
MainWindow.setObjectName("MainWindow")
MainWindow.setFixedSize(632, 447)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth())
MainWindow.setSizePolicy(sizePolicy)
MainWindow.setAnimated(False)
MainWindow.setDocumentMode(False)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
# self.graphicsView = QtWidgets.QGraphicsView(self.centralwidget)
# self.graphicsView.setGeometry(QtCore.QRect(10, 11, 611, 311))
# self.graphicsView.setObjectName("graphicsView")
self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit.setGeometry(QtCore.QRect(10, 330, 611, 31))
self.lineEdit.setObjectName("lineEdit")
self.lineEdit.returnPressed.connect(self.write_txtbox)
self.lineEdit.setEnabled(False)
self.comboBox = QtWidgets.QComboBox(self.centralwidget)
self.comboBox.setGeometry(QtCore.QRect(10, 370, 181, 25))
self.comboBox.setObjectName("comboBox")
self.comboBox.addItem("")
self.comboBox.addItem("")
self.comboBox.setEnabled(False)
self.save_button = QtWidgets.QPushButton(self.centralwidget)
self.save_button.setGeometry(QtCore.QRect(530, 370, 89, 25))
self.save_button.setObjectName("save_button")
self.save_button.clicked.connect(self.write_txtbox)
self.save_button.setEnabled(False)
self.next_button = QtWidgets.QPushButton(self.centralwidget)
self.next_button.setGeometry(QtCore.QRect(400, 370, 31, 25))
self.next_button.setObjectName("next_button")
self.next_button.clicked.connect(self.next_image)
self.next_button.setEnabled(False)
self.last_button = QtWidgets.QPushButton(self.centralwidget)
self.last_button.setGeometry(QtCore.QRect(360, 370, 31, 25))
self.last_button.setObjectName("last_button")
self.last_button.clicked.connect(self.prev_image)
self.last_button.setEnabled(False)
self.delete_button = QtWidgets.QPushButton(self.centralwidget)
self.delete_button.setGeometry(QtCore.QRect(440, 370, 89, 25))
self.delete_button.setObjectName("delete_button")
self.delete_button.clicked.connect(self.delete_image)
self.delete_button.setEnabled(False)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 632, 22))
self.menubar.setObjectName("menubar")
self.menuFile = QtWidgets.QMenu(self.menubar)
self.actionQuit = QtWidgets.QAction(MainWindow)
MainWindow.setMenuBar(self.menubar)
self.statusBar = QtWidgets.QStatusBar(MainWindow)
self.statusBar.setSizeGripEnabled(False)
self.statusBar.setObjectName("statusBar")
MainWindow.setStatusBar(self.statusBar)
self.actionQuit.setObjectName("actionQuit")
self.actionQuit.triggered.connect(self.close)
self.actionSave = QtWidgets.QAction(MainWindow)
self.actionSave.setObjectName("actionSave")
self.actionSave.triggered.connect(self.write_txtbox)
self.actionNext = QtWidgets.QAction(MainWindow)
self.actionNext.setObjectName("actionNext")
self.actionNext.triggered.connect(self.prev_image)
self.actionOpen = QtWidgets.QAction(MainWindow)
self.actionOpen.setObjectName("actionOpen")
self.actionOpen.triggered.connect(self.folder_open)
self.actionNext = QtWidgets.QAction(MainWindow)
self.actionNext.setObjectName("actionNext")
self.actionNext.triggered.connect(self.next_image)
self.actionDelete = QtWidgets.QAction(MainWindow)
self.actionDelete.setObjectName("actionDelete")
self.actionDelete.triggered.connect(self.delete_image)
self.menuFile.addAction(self.actionOpen)
self.menuFile.addAction(self.actionQuit)
self.menuFile.addSeparator()
self.menuFile.addAction(self.actionNext)
self.menuFile.addAction(self.actionNext)
self.menuFile.addAction(self.actionDelete)
self.menuFile.addAction(self.actionSave)
self.menubar.addAction(self.menuFile.menuAction())
self.label = QLabel(self)
self.label.setGeometry(QtCore.QRect(10, 30, 611, 311))
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "Bulk Image Tagger"))
self.lineEdit.setPlaceholderText(_translate("MainWindow", "Enter prompt here"))
self.comboBox.setItemText(0, _translate("MainWindow", "Premade Prompts"))
self.comboBox.setItemText(1, _translate("MainWindow", "example1"))
self.save_button.setText(_translate("MainWindow", "Save"))
self.next_button.setText(_translate("MainWindow", ""))
self.last_button.setText(_translate("MainWindow", ""))
self.delete_button.setText(_translate("MainWindow", "Delete"))
self.menuFile.setTitle(_translate("MainWindow", "File"))
self.actionQuit.setText(_translate("MainWindow", "Quit"))
self.actionQuit.setShortcut(_translate("MainWindow", "Ctrl+Q"))
self.actionSave.setText(_translate("MainWindow", "&Save"))
self.actionSave.setShortcut(_translate("MainWindow", "Ctrl+S"))
self.actionNext.setText(_translate("MainWindow", "Previous"))
self.actionNext.setShortcut(_translate("MainWindow", "Ctrl+B"))
self.actionOpen.setText(_translate("MainWindow", "Open Folder"))
self.actionOpen.setShortcut(_translate("MainWindow", "Ctrl+O"))
self.actionNext.setText(_translate("MainWindow", "&Next"))
self.actionNext.setShortcut(_translate("MainWindow", "Ctrl+N"))
self.actionDelete.setText(_translate("MainWindow", "&Delete"))
self.actionDelete.setShortcut(_translate("MainWindow", "Ctrl+D"))
def folder_open(self):
self.user_dir = QFileDialog.getExistingDirectory(
self, "Open Directory",
"",
QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks,
)
self.folder_contents = get_folder(self.user_dir)
self.folder_contents_i = 0
self.last_button.setEnabled(False)
if len(self.folder_contents) > 0:
self.load_image(self.folder_contents[self.folder_contents_i])
self.enable(True)
else:
self.statusBar.showMessage('No images found!')
def load_image(self, image_path):
self.lineEdit.clear()
if os.path.isfile(image_path):
im = cv2.imread(image_path) # square image
im_resize = resizeAndPad(im, (311, 311), 311)
is_success, im_buf_arr = cv2.imencode(".png", im_resize)
im = Image.open(BytesIO(im_buf_arr.tobytes()))
im.tobytes('raw', 'RGB')
qim = ImageQt(im)
pixmap = QPixmap.fromImage(qim)
self.label.setPixmap(pixmap)
self.label.show()
self.statusBar.showMessage(os.path.split(self.folder_contents[self.folder_contents_i])[-1])
def next_image(self):
if self.folder_contents is not None:
if self.folder_contents_i + 1 < len(self.folder_contents):
self.folder_contents_i += 1
self.load_image(self.folder_contents[self.folder_contents_i])
else:
self.next_button.setEnabled(False)
if self.folder_contents_i - 1 < len(self.folder_contents):
self.last_button.setEnabled(True)
def prev_image(self):
if self.folder_contents is not None:
if self.folder_contents_i - 1 >= 0:
self.folder_contents_i -= 1
self.load_image(self.folder_contents[self.folder_contents_i])
elif self.folder_contents_i - 1 < len(self.folder_contents):
self.last_button.setEnabled(False)
if self.folder_contents_i + 1 < len(self.folder_contents):
self.next_button.setEnabled(True)
def write_txtbox(self):
if self.folder_contents is not None:
textbox_content = self.lineEdit.text()
if len(textbox_content) > 0:
f = open(f'{self.folder_contents[self.folder_contents_i]}.txt', 'w')
f.write(textbox_content)
f.close()
self.lineEdit.clear()
self.next_image()
def delete_image(self):
if self.folder_contents is not None and len(self.folder_contents) > 0:
self.label.hide()
try:
os.remove(self.folder_contents[self.folder_contents_i])
except:
print(self.folder_contents, self.folder_contents_i)
if os.path.exists(f'{self.folder_contents[self.folder_contents_i]}.txt'):
os.remove(f'{self.folder_contents[self.folder_contents_i]}.txt')
self.statusBar.showMessage(f'Deleted {os.path.split(self.folder_contents[self.folder_contents_i])[-1]}')
del self.folder_contents[self.folder_contents_i]
self.folder_contents_i -= 1
if len(self.folder_contents) > 0:
self.next_image()
else:
self.enable(False)
def enable(self, action):
self.save_button.setEnabled(action)
self.next_button.setEnabled(action)
self.last_button.setEnabled(action)
self.delete_button.setEnabled(action)
self.lineEdit.setEnabled(action)
self.comboBox.setEnabled(action)

2
launch.sh Executable file
View File

@ -0,0 +1,2 @@
#!/bin/bash
python3 application/app.py

106
main_window.py Normal file
View File

@ -0,0 +1,106 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'Bulk Image Tagger qt5.ui'
#
# Created by: PyQt5 UI code generator 5.14.1
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(632, 447)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth())
MainWindow.setSizePolicy(sizePolicy)
MainWindow.setAnimated(False)
MainWindow.setDocumentMode(False)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.graphicsView = QtWidgets.QGraphicsView(self.centralwidget)
self.graphicsView.setGeometry(QtCore.QRect(10, 11, 611, 311))
self.graphicsView.setObjectName("graphicsView")
self.comboBox = QtWidgets.QComboBox(self.centralwidget)
self.comboBox.setGeometry(QtCore.QRect(10, 370, 181, 25))
self.comboBox.setObjectName("comboBox")
self.comboBox.addItem("")
self.comboBox.addItem("")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(530, 370, 89, 25))
self.pushButton.setObjectName("pushButton")
self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_2.setGeometry(QtCore.QRect(400, 370, 31, 25))
self.pushButton_2.setObjectName("pushButton_2")
self.pushButton_3 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_3.setGeometry(QtCore.QRect(360, 370, 31, 25))
self.pushButton_3.setObjectName("pushButton_3")
self.pushButton_4 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_4.setGeometry(QtCore.QRect(440, 370, 89, 25))
self.pushButton_4.setObjectName("pushButton_4")
self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit.setGeometry(QtCore.QRect(10, 330, 611, 31))
self.lineEdit.setObjectName("lineEdit")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 632, 22))
self.menubar.setObjectName("menubar")
self.menuFile = QtWidgets.QMenu(self.menubar)
self.menuFile.setObjectName("menuFile")
MainWindow.setMenuBar(self.menubar)
self.statusBar = QtWidgets.QStatusBar(MainWindow)
self.statusBar.setSizeGripEnabled(False)
self.statusBar.setObjectName("statusBar")
MainWindow.setStatusBar(self.statusBar)
self.actionQuit = QtWidgets.QAction(MainWindow)
self.actionQuit.setObjectName("actionQuit")
self.actionSave = QtWidgets.QAction(MainWindow)
self.actionSave.setObjectName("actionSave")
self.actionSkip = QtWidgets.QAction(MainWindow)
self.actionSkip.setObjectName("actionSkip")
self.actionOpen = QtWidgets.QAction(MainWindow)
self.actionOpen.setObjectName("actionOpen")
self.actionNext = QtWidgets.QAction(MainWindow)
self.actionNext.setObjectName("actionNext")
self.actionDelete = QtWidgets.QAction(MainWindow)
self.actionDelete.setObjectName("actionDelete")
self.menuFile.addAction(self.actionOpen)
self.menuFile.addAction(self.actionQuit)
self.menuFile.addSeparator()
self.menuFile.addAction(self.actionSkip)
self.menuFile.addAction(self.actionNext)
self.menuFile.addAction(self.actionDelete)
self.menuFile.addAction(self.actionSave)
self.menubar.addAction(self.menuFile.menuAction())
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "Bulk Image Tagger"))
self.comboBox.setItemText(0, _translate("MainWindow", "Premade Prompts"))
self.comboBox.setItemText(1, _translate("MainWindow", "example1"))
self.pushButton.setText(_translate("MainWindow", "Save"))
self.pushButton_2.setText(_translate("MainWindow", ""))
self.pushButton_3.setText(_translate("MainWindow", ""))
self.pushButton_4.setText(_translate("MainWindow", "Delete"))
self.lineEdit.setPlaceholderText(_translate("MainWindow", "Enter prompt here"))
self.menuFile.setTitle(_translate("MainWindow", "File"))
self.actionQuit.setText(_translate("MainWindow", "Quit"))
self.actionQuit.setShortcut(_translate("MainWindow", "Ctrl+Q"))
self.actionSave.setText(_translate("MainWindow", "&Save"))
self.actionSave.setShortcut(_translate("MainWindow", "Ctrl+S"))
self.actionSkip.setText(_translate("MainWindow", "Skip"))
self.actionSkip.setShortcut(_translate("MainWindow", "Ctrl+B"))
self.actionOpen.setText(_translate("MainWindow", "Open Folder"))
self.actionOpen.setShortcut(_translate("MainWindow", "Ctrl+O"))
self.actionNext.setText(_translate("MainWindow", "&Next"))
self.actionNext.setShortcut(_translate("MainWindow", "Ctrl+N"))
self.actionDelete.setText(_translate("MainWindow", "&Delete"))
self.actionDelete.setShortcut(_translate("MainWindow", "Ctrl+D"))