Skip to content

Commit

Permalink
Merge pull request miguel-martinr#115 from miguel-martinr/bilinear
Browse files Browse the repository at this point in the history
Bilinear interpolation added.
  • Loading branch information
miguel-martinr authored Jan 6, 2022
2 parents 099b20a + 70ab986 commit 6877f27
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 18 deletions.
1 change: 1 addition & 0 deletions src/pycture/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def add_editor(self, image: QImage = None, name: str = "", editor: Editor = None
name = name + "+" + extension

if editor:
editor.setWindowTitle(name)
self.editors[name] = editor
else:
self.editors[name] = Editor(self, image, name)
Expand Down
32 changes: 31 additions & 1 deletion src/pycture/commands/edit_commands/interpolation.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@

from PyQt5.QtGui import QImage
from math import floor, sqrt
import numpy as np

from pycture.editor.image.pixel import Pixel

def nearest_neighbor_interpolation(image: QImage, point: (float, float)):
_x, _y = point
Expand All @@ -11,12 +14,39 @@ def nearest_neighbor_interpolation(image: QImage, point: (float, float)):
Y = floor(_y)
Y_one = min(Y + 1, image.height() - 1)


# top left top right bottom left bottom right
corners = ((X, Y_one), (X_one, Y_one), (X, Y), (X_one, Y))
corners = ((X, Y), (X_one, Y), (X, Y_one), (X_one, Y_one))

distances = [sqrt((x - _x)**2 + (y - _y)**2)
for x, y in corners]
nearest_neighbor = corners[distances.index(min(distances))]
return image.pixel(nearest_neighbor[0], nearest_neighbor[1])


def bilinear_interpolation(image: QImage, point: (float, float)):
_x, _y = point

X = floor(_x)
X_one = min(X + 1, image.width() - 1)

Y = floor(_y)
Y_one = min(Y + 1, image.height() - 1)

# Get (R, G, B) values from pixels
A = np.array(tuple(image.pixel(X, Y).to_bytes(4, 'big')[1:])) # top left
B = np.array(tuple(image.pixel(X_one, Y).to_bytes(4, 'big')[1:])) # top right
C = np.array(tuple(image.pixel(X, Y_one).to_bytes(4, 'big')[1:])) # bottom left
D = np.array(tuple(image.pixel(X_one, Y_one).to_bytes(4, 'big')[1:])) # bottom right

p = _x - X
q = _y - Y

Q = A + (B - A) * p
R = C + (D - C) * p

P = [round(val) for val in (Q + (R - Q) * q)]
P_bytes = bytes([0xff, *P]) # \ff\rr\gg\bb
return int.from_bytes(P_bytes, 'big') # 0xffrrggbb


14 changes: 10 additions & 4 deletions src/pycture/commands/edit_commands/rotate.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from PyQt5.QtWidgets import QWidget, QMainWindow
from ..command import Command
from .interpolation import nearest_neighbor_interpolation
from .interpolation import bilinear_interpolation, nearest_neighbor_interpolation
from pycture.dialogs import RotateDialog
from pycture.editor import Editor

Expand All @@ -10,20 +10,26 @@ def __init__(self, parent: QWidget):
super().__init__(parent, "Rotate")
self.interpolation_techniques = {
"Nearest neighbour": nearest_neighbor_interpolation,
"Bilinear": bilinear_interpolation,
}

def apply_rotation(self, editor_title, interpolation_technique, angle):
image, title = self.get_active_image_and_title(self.main_window)
editor = self.main_window.editors.get(editor_title)
image = editor.get_image()
title = editor.windowTitle()

rotated_image = image.rotate(
angle, self.interpolation_techniques[interpolation_technique])

str_angle = str(angle).replace(".", "'")
self.main_window.add_editor(editor=Editor(
self.main_window, rotated_image, title + f' rotated {angle}º'))
self.main_window, rotated_image, title + f' rotated {str_angle}º'))

def execute(self, main_window: QMainWindow):
# Open dialog
# Connect dialog button to rotate function
self.main_window = main_window
dialog = RotateDialog(main_window, main_window.get_editor_list())
dialog = RotateDialog(main_window, main_window.get_editor_list(), list(self.interpolation_techniques.keys()))
dialog.set_editor(main_window.get_active_editor_name())
dialog.set_interpolation_technique(
list(self.interpolation_techniques.keys())[0])
Expand Down
11 changes: 8 additions & 3 deletions src/pycture/commands/edit_commands/scale.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from PyQt5.QtWidgets import QWidget, QMainWindow
from ..command import Command
from .interpolation import nearest_neighbor_interpolation
from .interpolation import bilinear_interpolation, nearest_neighbor_interpolation
from pycture.dialogs import ScaleDialog
from pycture.editor import Editor

Expand All @@ -10,13 +10,14 @@ def __init__(self, parent: QWidget):
super().__init__(parent, "Scale")
self.interpolation_techniques = {
"Nearest neighbour": nearest_neighbor_interpolation,
"Bilinear": bilinear_interpolation,
}

def execute(self, main_window: QMainWindow):
# Open dialog
# Connect dialog button to rotate function
self.main_window = main_window
dialog = ScaleDialog(main_window, main_window.get_editor_list())
dialog = ScaleDialog(main_window, main_window.get_editor_list(), list(self.interpolation_techniques.keys()))
dialog.set_editor(main_window.get_active_editor_name())
dialog.set_interpolation_technique(
list(self.interpolation_techniques.keys())[0])
Expand All @@ -26,7 +27,11 @@ def execute(self, main_window: QMainWindow):
def apply_scale(self,
editor_title: str, interpolation_name: str, new_size: (int, int)
):
image, title = self.get_active_image_and_title(self.main_window)
editor = self.main_window.get_editor(editor_title)
image = editor.get_image()
title = editor.windowTitle()


interpolation_technique = self.interpolation_techniques[interpolation_name]
scaled_image = image.scale(new_size, interpolation_technique)
self.main_window.add_editor(editor=Editor(
Expand Down
12 changes: 6 additions & 6 deletions src/pycture/dialogs/rotate_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,29 @@


class RotateDialog(QDialog):
# img interpolation size(int, int)
applied = Signal(str, str, tuple)
# img interpolation angle
applied = Signal(str, str, float)

def __init__(self, parent: QMainWindow, editors: List[str], angle_limit = 180):
def __init__(self, parent: QMainWindow, editors: List[str], interpolation_techniques: List[str], angle_limit = 180):
super().__init__(parent, Qt.WindowType.Window)
self.setWindowTitle("Scale")
self.layout = QVBoxLayout()
self.layout.setSizeConstraint(QLayout.SetFixedSize)
self.setLayout(self.layout)
self.angle_limit = angle_limit

self.setup(editors)
self.setup(editors, interpolation_techniques)

self.show()

def setup(self, editors: List[str]):
def setup(self, editors: List[str], interpolation_techniques: List[str]):
layout = QVBoxLayout()
self.layout.addLayout(layout)

self.editors_dropdown = DropdownList(self, editors)
layout.addWidget(self.editors_dropdown)

self.interpolation_dropdown = DropdownList(self, ["Nearest neighbour"])
self.interpolation_dropdown = DropdownList(self, interpolation_techniques)
layout.addWidget(self.interpolation_dropdown)


Expand Down
8 changes: 4 additions & 4 deletions src/pycture/dialogs/scale_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,23 @@ class ScaleDialog(QDialog):
# img interpolation size(int, int)
applied = Signal(str, str, tuple)

def __init__(self, parent: QMainWindow, editors: List[str]):
def __init__(self, parent: QMainWindow, editors: List[str], interpolation_techniques: List[str]):
super().__init__(parent, Qt.WindowType.Window)
self.setWindowTitle("Scale")
self.layout = QVBoxLayout()
self.layout.setSizeConstraint(QLayout.SetFixedSize)
self.setLayout(self.layout)
self.setup(editors)
self.setup(editors, interpolation_techniques)
self.show()

def setup(self, editors: List[str]):
def setup(self, editors: List[str], interpolation_techniques: List[str]):
layout = QVBoxLayout()
self.layout.addLayout(layout)

self.editors_dropdown = DropdownList(self, editors)
layout.addWidget(self.editors_dropdown)

self.interpolation_dropdown = DropdownList(self, ["Nearest neighbour"])
self.interpolation_dropdown = DropdownList(self, interpolation_techniques)
layout.addWidget(self.interpolation_dropdown)

self.width = QLineEdit("1", self)
Expand Down

0 comments on commit 6877f27

Please sign in to comment.