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

488 lines
16 KiB
Python
Raw Normal View History

2021-03-08 11:51:01 +01:00
"""SkeletonApp."""
2021-03-11 02:14:37 +01:00
# from PyQt6 import Qt
2021-03-08 11:51:01 +01:00
from PyQt6 import QtGui
from PyQt6 import QtCore
from PyQt6 import QtWidgets
2021-03-11 02:14:37 +01:00
from PyQt6.QtWidgets import QApplication, QSizePolicy, QDialog, QMenu
2021-03-08 11:51:01 +01:00
from PyQt6.QtWidgets import QMainWindow
from PyQt6.QtWidgets import QWidget
2021-03-11 02:14:37 +01:00
2021-03-08 11:51:01 +01:00
# include <QToolBar>
from PyQt6.QtWidgets import QToolBar
# include <QIcon>
2021-03-11 02:14:37 +01:00
from PyQt6.QtGui import QIcon, QPalette, QGuiApplication, QImage, QPixmap, qRed, qGreen, qBlue, qAlpha, qRgba, QAction, QKeySequence, qRgb
2021-03-08 11:51:01 +01:00
# include <QClipboard>
from PyQt6.QtGui import QClipboard
# include <QColorSpace>
from PyQt6.QtGui import QColorSpace
# include <QDir>
2021-03-11 02:14:37 +01:00
from PyQt6.QtCore import QDir, QTranslator, Qt
2021-03-08 11:51:01 +01:00
# 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."""
2021-03-11 02:14:37 +01:00
firstDialog = None
2021-03-08 11:51:01 +01:00
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)
2021-03-11 02:14:37 +01:00
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):
2021-03-08 11:51:01 +01:00
"""Load file."""
2021-03-11 02:14:37 +01:00
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:
2021-03-11 02:19:17 +01:00
self.nbRealChannels = int(self.bit_planes / 8)
2021-03-11 02:14:37 +01:00
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)