1
0
lpo-image-processing/app/python/skeletonApp/skeletonApp.py

489 lines
16 KiB
Python

"""SkeletonApp."""
# from PyQt6 import Qt
from PyQt6 import QtGui
from PyQt6 import QtCore
from PyQt6 import QtWidgets
from PyQt6.QtWidgets import QApplication, QSizePolicy, QDialog, QMenu
from PyQt6.QtWidgets import QMainWindow
from PyQt6.QtWidgets import QWidget
# include <QToolBar>
from PyQt6.QtWidgets import QToolBar
# include <QIcon>
from PyQt6.QtGui import QIcon, QPalette, QGuiApplication, QImage, QPixmap, qRed, qGreen, qBlue, qAlpha, qRgba, QAction, QKeySequence, qRgb
# include <QClipboard>
from PyQt6.QtGui import QClipboard
# include <QColorSpace>
from PyQt6.QtGui import QColorSpace
# include <QDir>
from PyQt6.QtCore import QDir, QTranslator, Qt
# include <QFileDialog>
from PyQt6.QtWidgets import QFileDialog
# include <QImageReader>
from PyQt6.QtGui import QImageReader
# include <QImageWriter>
from PyQt6.QtGui import QImageWriter
# include <QBuffer>
from PyQt6.QtCore import QBuffer
# include <QLabel>
from PyQt6.QtWidgets import QLabel
# include <QMenuBar>
from PyQt6.QtWidgets import QMenuBar
# include <QMessageBox>
from PyQt6.QtWidgets import QMessageBox
# include <QMimeData>
from PyQt6.QtCore import QMimeData
# include <QPainter>
from PyQt6.QtGui import QPainter
# include <QScreen>
from PyQt6.QtGui import QScreen
# include <QScrollArea>
from PyQt6.QtWidgets import QScrollArea
# include <QScrollBar>
from PyQt6.QtWidgets import QScrollBar
# include <QStandardPaths>
from PyQt6.QtCore import QStandardPaths
# include <QStatusBar>
from PyQt6.QtWidgets import QStatusBar
from io import BytesIO
# include <iostream>
from io import FileIO
# include <fstream>
#
# class ScrollArea:
# """Scroll area."""
# def __init__(self, scroll_area=QScrollArea()):
# pass
#
#
# class ImageLabel:
# """Image label."""
# def __init__(self, image_label=QLabel()):
# pass
class SkeletonApp(QMainWindow):
"""SkeletonApp."""
firstDialog = None
def __init__(self, parent=None):
super(SkeletonApp, self).__init__(parent)
self.image_label = QLabel()
self.image_label.setBackgroundRole(QPalette.ColorRole.Base)
self.image_label.setSizePolicy(QSizePolicy.Policy.Ignored, QSizePolicy.Policy.Ignored)
self.image_label.setScaledContents(True)
self.scroll_area = QScrollArea()
self.scroll_area.setBackgroundRole(QPalette.ColorRole.Dark)
self.scroll_area.setWidget(self.image_label)
self.scroll_area.setVisible(False)
self.setCentralWidget(self.scroll_area)
self.resize(QGuiApplication.primaryScreen().availableSize() * 3 / 5)
self.fileMenu: QMenu = self.menuBar().addMenu(self.tr("&File"))
self.editMenu: QMenu = self.menuBar().addMenu(self.tr("&Edit"))
self.viewMenu: QMenu = self.menuBar().addMenu(self.tr("&View"))
self.helpMenu: QMenu = self.menuBar().addMenu(self.tr("&Help"))
self.filtersMenu: QMenu = self.menuBar().addMenu(self.tr("&Filters"))
self.data = []
self.reader = None
self.pixmap = QPixmap()
self.image = self.pixmap.toImage()
self.bit_planes = 0
self.depth = 0
self.format = None
self.bytes_per_line = 0
self.nbChannels = 1
self.nbRealChannels = 1
self.pixel = None
self.row = []
self.image_width = 0
self.image_height = 0
self.translator = QTranslator(self)
self.scaleFactor = 1.0
self.pictures_locations = None
self.mimeTypeFilters = []
self.dataResult = []
self.createActions()
self.updateActions()
def loadFile(self, file_name: str):
"""Load file."""
self.data.clear()
self.reader = QImageReader(file_name)
self.reader.setAutoTransform(True)
# const
_new_image: QImage = self.reader.read()
if _new_image.isNull():
QMessageBox.information(
self, QGuiApplication.applicationDisplayName(),
self.tr(F"Cannot load {QDir.toNativeSeparators(file_name)}:{self.reader.errorString()}"))
return False
self.filtersMenu.setDisabled(False)
self.setImage(new_image=_new_image)
self.image_width = self.image.width()
self.image_height = self.image.height()
self.bit_planes = int(self.image.bitPlaneCount())
self.depth = int(self.image.depth())
self.format = self.image.format()
print(type(self.format))
self.bytes_per_line = int(self.image.bytesPerLine())
self.nbChannels = 1
if self.depth > 1:
self.nbChannels = self.depth / 8
self.nbRealChannels = 1
if self.bit_planes > 1:
self.nbRealChannels = int(self.bit_planes / 8)
file = open("test_zapisu.txt", 'w')
if file:
for i in range(self.image_height):
row = []
for j in range(self.image_width):
# działa w logice pixel(x,y) - najpierw specyfikuje kolumnę
impix: qRgb = self.image.pixel(j, i)
pixel = []
if self.nbChannels == 1:
pixel = [qRed(impix)]
file.writelines(str(pix) + " " for pix in pixel)
elif self.nbChannels == 3:
pixel = [qRed(impix), qGreen(impix), qBlue(impix)]
file.writelines(str(pix) + " " for pix in pixel)
elif self.nbChannels == 4:
pixel = [qRed(impix), qGreen(impix), qBlue(impix), qAlpha(impix)]
file.writelines(str(pix) + " " for pix in pixel)
file.write("\n")
row.append(pixel)
self.data.append(row)
file.close()
self.setWindowFilePath(file_name)
message = self.tr(f"Opened \"{QDir.toNativeSeparators(file_name)}\", {self.image.width()}x{self.image.height()}, Depth: {self.image.depth()}, "
f"Bit planes: {self.image.bitPlaneCount()}")
self.statusBar().showMessage(message)
return True
def setImage(self, new_image: QImage):
"""Set image."""
self.image = new_image
if self.image.colorSpace().isValid():
self.image.convertToColorSpace(QColorSpace(QColorSpace.NamedColorSpace.SRgb))
self.image_label.setPixmap(QPixmap.fromImage(self.image))
self.scaleFactor = 1.0
self.scroll_area.setVisible(True)
# self.printAct.setEnabled(True)
self.fitToWindowAct.setEnabled(True)
self.updateActions()
if not self.fitToWindowAct.isChecked():
self.image_label.adjustSize()
def scaleImage(self, factor: float):
"""Scale image."""
self.scaleFactor *= factor
self.image_label.resize(self.scaleFactor * self.image_label.pixmap().width(),
self.scaleFactor * self.image_label.pixmap().height())
self.adjustScrollBar(self.scroll_area.horizontalScrollBar(), factor)
self.adjustScrollBar(self.scroll_area.verticalScrollBar(), factor)
self.zoomInAct.setEnabled(self.scaleFactor < 3.0)
self.zoomOutAct.setEnabled(self.scaleFactor > 0.333)
def adjustScrollBar(self, scroll_bar: QScrollBar, factor: float):
"""adjust scroll bar"""
scroll_bar.setValue(int(factor * scroll_bar.value() + ((factor - 1) * scroll_bar.pageStep() / 2)))
def about(self):
"""about"""
QMessageBox.about(self, self.tr("About skeletonApp"),
self.tr("<p>Aplikacja<b> szkieletowa</b> do kursu <i>Przetwarzanie obrazu</i></p>"))
def saveFile(self, file_name: str):
"""Save."""
writer = QImageWriter(file_name)
if not writer.write(self.image):
QMessageBox.information(
self, QGuiApplication.applicationDisplayName(),
self.tr(F"Cannot write {QDir.toNativeSeparators(file_name)}: {writer.errorString()}"))
return False
message = self.tr(f"Wrote \"{QDir.toNativeSeparators(file_name)}\"")
self.statusBar().showMessage(message)
return True
def initializeImageFileDialog(self, dialog, acceptMode):
"""Initialize Image File Dialog."""
self.firstDialog = True
if self.firstDialog:
self.firstDialog = False
self.pictures_locations: list = QStandardPaths.standardLocations(QStandardPaths.StandardLocation.PicturesLocation)
dialog.setDirectory(QDir.currentPath() if not self.pictures_locations else self.pictures_locations[-1])
self.mimeTypeFilters = []
supported_mime_types = None
if acceptMode == QFileDialog.AcceptMode.AcceptOpen:
supported_mime_types = QImageReader.supportedMimeTypes().__str__()
else:
supported_mime_types = QImageWriter.supportedMimeTypes().__str__()
for mime_type_name in supported_mime_types:
self.mimeTypeFilters.append(mime_type_name)
self.mimeTypeFilters.sort()
dialog.setMimeTypeFilters(self.mimeTypeFilters)
dialog.selectMimeTypeFilter("image/x-portable-pixmap")
if acceptMode == QFileDialog.AcceptMode.AcceptSave:
dialog.setDefaultSuffix("ppm")
def open(self):
"""Open file"""
dialog = QFileDialog(self, self.tr("Open File"))
self.initializeImageFileDialog(dialog, QFileDialog.AcceptMode.AcceptOpen)
dialog.exec()
if dialog.accepted:
self.loadFile(dialog.selectedFiles()[0])
def saveAs(self):
"""Save file"""
dialog = QFileDialog(self, self.tr("Save File As"))
self.initializeImageFileDialog(dialog, QFileDialog.AcceptMode.AcceptSave)
dialog.exec()
if dialog.accepted:
self.saveFile(dialog.selectedFiles()[0])
def copy(self):
"""Copy image"""
QGuiApplication.clipboard().setImage(self.image)
def clipboardImage(self) -> QImage:
"""Clipboard Image"""
mimeData: QMimeData = QGuiApplication.clipboard().mimeData()
if mimeData:
if mimeData.hasImage():
image: QImage = QImage(mimeData.imageData())
if not image.isNull():
return image
return QImage()
def paste(self):
"""Paste image."""
newImage: QImage = self.clipboardImage()
if newImage.isNull():
self.statusBar().showMessage(self.tr("No image in clipboard"))
else:
self.setImage(newImage)
self.setWindowFilePath(str(''))
message: str = self.tr(
f"Obtained image from clipboard, {newImage.width()}x{newImage.height()}, Depth: {newImage.depth()}")
self.statusBar().showMessage(message)
def zoomIn(self):
"""Zoom in"""
self.scaleImage(1.25)
def zoomOut(self):
"""Zoom out"""
self.scaleImage(0.8)
def normalSize(self):
"""Normalize Size"""
self.image_label.adjustSize()
self.scaleFactor = 1.0
def fitToWindow(self):
"""Fit to window"""
fit: bool = self.fitToWindowAct.isChecked()
print(fit)
self.scroll_area.setWidgetResizable(fit)
if not fit:
self.normalSize()
self.updateActions()
def applyTestFilter(self):
"""Apply test filter"""
self.dataResult.clear()
for i in range(0, self.height()):
row = [[]]
for j in range(0, self.width()):
pixel = []
for k in range(0, self.nbRealChannels):
print(self.nbChannels)
print(i, j, k)
pixel.append(255 - int(self.data[i][j][k]))
if self.nbRealChannels < self.nbChannels:
pixel.append(255)
row.append(pixel)
self.dataResult.append(row)
imageResult = QImage(width=self.width(), height=self.height(), format=self.image.format())
if self.nbChannels > 1:
for i in range(0, self.image.height()):
for j in range(0, self.image.width()):
imageResult.setPixel(
j, i, qRgba(self.dataResult[i][j][0], self.dataResult[i][j][1],
self.dataResult[i][j][2], self.dataResult[i][j][3]))
else:
if self.image.bitPlaneCount() == 8:
for i in range(0, self.image.height()):
for j in range(0, self.image.width()):
imageResult.setPixel(
j, i, qRgba(self.dataResult[i][j][0], self.dataResult[i][j][0],
self.dataResult[i][j][0], 255))
else:
imageResult.setColorCount(2)
imageResult.setColor(0, qRgba(0, 0, 0, 255))
imageResult.setColor(1, qRgba(255, 0, 0, 0))
for i in range(0, self.image.height()):
for j in range(0, self.image.width()):
if self.dataResult[i][j][0] == 0:
imageResult.setPixel(j, i, 0)
else:
imageResult.setPixel(j, i, 1)
self.setImage(imageResult)
def updateActions(self):
"""update actions"""
self.saveAsAct.setEnabled(not self.image.isNull())
self.copyAct.setEnabled(not self.image.isNull())
self.zoomInAct.setEnabled(not self.fitToWindowAct.isChecked())
self.zoomOutAct.setEnabled(not self.fitToWindowAct.isChecked())
self.normalSizeAct.setEnabled(not self.fitToWindowAct.isChecked())
def createActions(self):
"""Create actions."""
self.openAct = QAction(self.tr("&Open..."), self)
self.openAct.triggered.connect(self.open)
self.openAct.setShortcut(QKeySequence.StandardKey.Open)
self.fileMenu.addAction(self.openAct)
self.saveAsAct = QAction(self.tr("&Save As..."), self)
self.saveAsAct.triggered.connect(self.saveAs)
self.saveAsAct.setShortcut(self.tr("Shift+S"))
self.fileMenu.addAction(self.saveAsAct)
self.fileMenu.addSeparator()
self.exitAct = QAction(self.tr("&Exit"), self)
self.exitAct.triggered.connect(QWidget.close)
self.exitAct.setShortcut(self.tr("Ctrl+Q"))
self.fileMenu.addAction(self.exitAct)
self.copyAct = QAction(self.tr("&Copy"), self)
self.copyAct.triggered.connect(self.copy)
self.copyAct.setShortcut(QKeySequence.StandardKey.Copy)
self.editMenu.addAction(self.copyAct)
self.pasteAct = QAction(self.tr("&Paste"), self)
self.pasteAct.triggered.connect(self.paste)
self.pasteAct.setShortcut(QKeySequence.StandardKey.Paste)
self.editMenu.addAction(self.pasteAct)
self.zoomInAct = QAction(self.tr("&Zoom In (25%)"), self)
self.zoomInAct.triggered.connect(self.zoomIn)
self.zoomInAct.setShortcut(QKeySequence.StandardKey.ZoomIn)
self.viewMenu.addAction(self.zoomInAct)
self.zoomOutAct = QAction(self.tr("&Zoom Out (25%)"), self)
self.zoomOutAct.triggered.connect(self.zoomOut)
self.zoomOutAct.setShortcut(QKeySequence.StandardKey.ZoomOut)
self.viewMenu.addAction(self.zoomOutAct)
self.normalSizeAct = QAction(self.tr("&Normal Size"), self)
self.normalSizeAct.triggered.connect(self.normalSize)
self.normalSizeAct.setShortcut(self.tr("Ctrl+N"))
self.viewMenu.addAction(self.normalSizeAct)
self.fitToWindowAct = QAction(self.tr("&Fit to Window"), self)
self.fitToWindowAct.triggered.connect(self.fitToWindow)
self.fitToWindowAct.setShortcut(self.tr("Ctrl+F"))
self.viewMenu.addAction(self.fitToWindowAct)
self.testFilterAct = QAction(self.tr("&TestFilter"), self)
self.testFilterAct.triggered.connect(self.applyTestFilter)
self.filtersMenu.addAction(self.testFilterAct)
self.aboutAct = QAction(self.tr("&About"), self)
self.aboutAct.triggered.connect(self.about)
self.aboutAct.setShortcut(self.tr("Ctrl+I"))
self.helpMenu.addAction(self.aboutAct)
self.fitToWindowAct.setCheckable(True)
self.filtersMenu.setDisabled(True)
# self.testFilter.setDisabled(True)
self.saveAsAct.setEnabled(False)
self.copyAct.setEnabled(False)
self.zoomInAct.setEnabled(False)
self.zoomOutAct.setEnabled(False)
self.normalSizeAct.setEnabled(False)
self.fitToWindowAct.setEnabled(False)
self.aboutAct.setEnabled(True)