我正在尝试编写一个Python程序,将选定的“图像处理”操作应用于加载的图像,而不使用cv2库中的现成函数。下面是图像旋转函数:
# * Python version: 3.10.11 (tags/v3.10.11:7d4cc5a, Apr 5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)]
# * OpenCV version: 4.9.0
# * Numpy version: 1.26.4
# * PyQt5 version: 5.15.10
import sys
from PyQt5.QtWidgets import QApplication, QTextEdit, QWidget, QVBoxLayout, QPushButton, QComboBox, QLabel, QFileDialog, QInputDialog
from PyQt5.QtGui import QPixmap, QImage, QColor
import cv2
import numpy as np
red_color = QColor(204, 0, 0)
green_color = QColor(0, 153, 0)
blue_color = QColor(0, 102, 255)
black_color = QColor(0, 0, 0)
class App(QWidget):
def __init__(self):
super().__init__()
self.initUI()
self.image = None
def initUI(self):
self.layout = QVBoxLayout()
self.terminal_codes = QTextEdit(self)
self.terminal_codes.setReadOnly(True)
self.layout.addWidget(self.terminal_codes)
self.terminal_codes.setFixedSize(400,150)
self.upload_button = QPushButton('Upload Image', self)
self.upload_button.clicked.connect(self.upload_image)
self.layout.addWidget(self.upload_button)
self.operation_combo = QComboBox(self)
self.operation_combo.addItem('Select Operation')
self.operation_combo.addItem('Image Rotation')
self.layout.addWidget(self.operation_combo)
self.apply_button = QPushButton('Apply and Download', self)
self.apply_button.clicked.connect(self.apply_operation)
self.layout.addWidget(self.apply_button)
self.setLayout(self.layout)
self.show()
self.terminal_codes.setTextColor(green_color)
self.terminal_codes.append("Program is ready.")
self.operation_combo.currentIndexChanged.connect(self.update_terminal_codes)
def update_terminal_codes(self, index):
# Update terminal codes function
def upload_image(self):
# Upload image function
def apply_operation(self):
operation = self.operation_combo.currentText()
if operation == 'Image Rotation':
angle, ok = QInputDialog.getDouble(self, 'Image Rotation', 'Enter angle:')
if ok:
rotated_image = self.rotate_image(self.image, angle)
self.download_image(rotated_image)
self.terminal_codes.clear()
self.terminal_codes.setTextColor(green_color)
self.terminal_codes.append("Rotation angle:{}\nImage Rotation operation was applied.".format(angle))
def download_image(self, image):
# Download image function
def rotate_image(self, image, angle):
(h, w) = image.shape[:2]
center = (w // 2, h // 2)
rotated = np.zeros((h, w, 3), dtype=np.uint8)
for i in range(h):
for j in range(w):
new_x = (j - center[0]) * np.cos(np.deg2rad(angle)) + (i - center[1]) * np.sin(np.deg2rad(angle)) + center[0]
new_y = -(j - center[0]) * np.sin(np.deg2rad(angle)) + (i - center[1]) * np.cos(np.deg2rad(angle)) + center[1]
if new_x >= 0 and new_x < h and new_y >= 0 and new_y < w:
x0 = int(np.floor(new_x))
y0 = int(np.floor(new_y))
x1 = int(np.ceil(new_x))
y1 = int(np.ceil(new_y))
if x0 < 0:
x0 = 0
if y0 < 0:
y0 = 0
if x1 >= w:
x1 = w - 1
if y1 >= h:
y1 = h - 1
a = new_x - x0
b = new_y - y0
c = 1 - a
d = 1 - b
for channel in range(3):
rotated[i, j, channel] = (c * d * image[y0, x0, channel] + a * d * image[y0, x1, channel] + c * b * image[y1, x0, channel] + a * b * image[y1, x1, channel])
return rotated
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
rotate_image函数的工作原理如下:
h
、w
和 center
,我们计算图像的高度、宽度和中心。rotated = np.zeros((h, w, 3), dtype=np.uint8)
我们创建一个空矩阵来放置结果图像。new_x
和 new_y
,我们计算旋转后每个像素的新位置。new_x >= 0 and new_x < h and new_y >= 0 and new_y < w
,我们检查新的像素位置是否在图像边界内。如果不是,我们忽略该像素。如果满足条件,我们就进行剩下的操作。我把剩下的操作口授给人工智能,因为我不知道该怎么做。 AI的解释如下:“这部分函数执行双线性插值来计算新的像素值。它找到距离新位置最近的四个像素,并将新的像素值计算为这四个像素的加权平均值。 ”我的问题是我无法显示整个返回的图像,因为查询
new_x >= 0 and new_x < h and new_y >= 0 and new_y < w
忽略了边界之外的像素。结果图像:
我想我需要重写代码
rotated = np.zeros((h, w, 3), dtype=np.uint8)
来解决这个问题。但我不知道如何以数学方式表达旋转图像适合的矩阵。我是说:
上图中,黑色边框代表图像旋转后需要适应的空间。
由于我的数学知识不够,我不知道如何使用图像的旋转角度、高度、宽度(旋转图像的边界框)来计算该图像可以拟合的零矩阵。我需要这方面的帮助。
这是有效的。
code00.py:
#!/usr/bin/env python
import sys
import cv2
import numpy as np
# @TODO - cfati: Method from question
def rotate_image(image, angle):
(h, w) = image.shape[:2]
center = (w // 2, h // 2)
rotated = np.zeros((h, w, 3), dtype=np.uint8)
for i in range(h):
for j in range(w):
new_x = (j - center[0]) * np.cos(np.deg2rad(angle)) + (i - center[1]) * np.sin(np.deg2rad(angle)) + center[0]
new_y = -(j - center[0]) * np.sin(np.deg2rad(angle)) + (i - center[1]) * np.cos(np.deg2rad(angle)) + center[1]
if new_x >= 0 and new_x < h and new_y >= 0 and new_y < w:
x0 = int(np.floor(new_x))
y0 = int(np.floor(new_y))
x1 = int(np.ceil(new_x))
y1 = int(np.ceil(new_y))
if x0 < 0:
x0 = 0
if y0 < 0:
y0 = 0
if x1 >= w:
x1 = w - 1
if y1 >= h:
y1 = h - 1
a = new_x - x0
b = new_y - y0
c = 1 - a
d = 1 - b
for channel in range(3):
rotated[i, j, channel] = (c * d * image[y0, x0, channel] + a * d * image[y0, x1, channel] + c * b * image[y1, x0, channel] + a * b * image[y1, x1, channel])
return rotated
def rotate_point(x, y, xc, yc, cos_, sin_):
return (x - xc) * cos_ - (y - yc) * sin_, (y - yc) * cos_ + (x - xc) * sin_
def rotate(img, alpha, original_size=False):
h0, w0 = img.shape[:2]
xc0 = w0 / 2
yc0 = h0 / 2
if original_size:
w1, h1 = w0, h0
xc1, yc1 = xc0, yc0
else:
cos = np.cos(np.deg2rad(alpha))
sin = np.sin(np.deg2rad(alpha))
xtl, ytl = rotate_point(0, 0, xc0, yc0, cos, sin)
xtr, ytr = rotate_point(w0, 0, xc0, yc0, cos, sin)
xbl, ybl = rotate_point(0, h0, xc0, yc0, cos, sin)
xbr, ybr = rotate_point(w0, h0, xc0, yc0, cos, sin)
xs = (xtl, xtr, xbl, xbr)
ys = (ytl, ytr, ybl, ybr)
w1, h1 = round(max(xs) - min(xs)), round(max(ys) - min(ys))
xc1, yc1 = w1 / 2, h1 / 2
cosr = np.cos(np.deg2rad(-alpha))
sinr = np.sin(np.deg2rad(-alpha))
ret = np.zeros((h1, w1, img.shape[2]), dtype=img.dtype)
for y1 in range(h1):
for x1 in range(w1):
x0, y0 = rotate_point(x1, y1, xc1, yc1, cosr, sinr)
x0 = round(x0 + xc0)
y0 = round(y0 + yc0)
if 0 <= x0 < w0 and 0 <= y0 < h0:
ret[y1, x1] = img[y0, x0]
return ret
def main(*argv):
orig = cv2.imread("img.png")
angle = int(input("Enter angle (degrees): "))
cv2.imshow(f"Original: {orig.shape[1]}x{orig.shape[0]}", orig)
# Original function raises IndexError
#rot0 = rotate_image(orig, angle)
#cv2.imshow(f"Rotated question ({angle}): {rot0.shape[1]}x{rot0.shape[0]}", rot0)
rot1 = rotate(orig, angle, original_size=True)
cv2.imshow(f"Rotated ({angle}, same size): {rot1.shape[1]}x{rot1.shape[0]}", rot1)
rot2 = rotate(orig, angle, original_size=False)
cv2.imshow(f"Rotated ({angle}, enlarged): {rot2.shape[1]}x{rot2.shape[0]}", rot2)
print("Press a key on any drawing window to exit...")
cv2.waitKey(delay=0)
if __name__ == "__main__":
print(
"Python {:s} {:03d}bit on {:s}\n".format(
" ".join(elem.strip() for elem in sys.version.split("\n")),
64 if sys.maxsize > 0x100000000 else 32,
sys.platform,
)
)
rc = main(*sys.argv[1:])
print("\nDone.\n")
sys.exit(rc)
注释:
正如代码中所指定的,问题中的旋转功能不起作用(至少对于该图像)
实现仅使用简单的转换(没有什么聪明之处)
在性能方面,我仅“缓存”三角函数结果(以避免为每个点计算它们)。有一些可以改进的地方:
摆脱rotate_point(“内联”其代码)
使用(更快)NumPy例程
输出: