图像中部分RGB可视化
今天室友有个需求就是模仿下面这张图画个示意图:
大致就是把图像中的一小部分区域的RGB值可视化了一下。他居然不知道该怎么画,我寻思这不直接秒了。
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
class Plotter:
def __init__(self, img):
self.img = img
self.range = None
# 三个图的位置
self.loc = [
[0.01, 0.325, 0.35, 0.35],
[0.375, 0.375, 0.25, 0.25],
[0.71, 0.355, 0.29, 0.29],
]
self.dloc = [0.02, 0.02] # 表格间距
self.facecolor = plt.get_cmap("Accent")(range(3)) # 表格颜色
def plot_img(self, fig):
img_range = self.range
axes = fig.add_axes(self.loc[0])
axes.imshow(self.img, aspect="auto")
axes.add_patch(
plt.Rectangle(
(img_range[0], img_range[2]),
img_range[1] - img_range[0],
img_range[3] - img_range[2],
fill=False,
edgecolor="red",
linewidth=2,
)
)
axes.axis("off")
def plot_table(self, fig, table):
loc = self.loc[2]
dloc = self.dloc
facecolor = self.facecolor
for i in range(table.shape[2]):
axes = fig.add_axes(
[loc[0] - dloc[0] * i, loc[1] - dloc[1] * i, loc[2], loc[3]],
)
axes.table(
cellText=table[:, :, i],
loc="center",
cellColours=np.full(
(table.shape[0], table.shape[1], 4),
facecolor[i],
dtype=self.facecolor[0].dtype,
),
bbox=[0, 0, 1, 1],
)
axes.axis("off")
def plot_range_img(self, fig, range_img):
axes = fig.add_axes(self.loc[1])
axes.imshow(range_img, aspect="auto")
axes.set_xticks([])
axes.set_yticks([])
spines = ["left", "right", "bottom", "top"]
for spine in spines:
axes.spines[spine].set_color("r")
def plot_path(self, axes, x1, x2):
x = np.linspace(x1[0], x2[0], 100)
y = np.linspace(x1[1], x2[1], 100)
axes.set_xlim(0, 1)
axes.set_ylim(0, 1)
axes.plot(x, y, "r--")
def range_points_on_img(self):
img_range = self.range
loc = self.loc[0]
luloc = loc[0] + img_range[0] / self.img.shape[0] * loc[2]
# axes坐标系y轴方向相反
ldloc = loc[1] + (self.img.shape[1] - img_range[2]) / self.img.shape[1] * loc[3]
dx = (img_range[1] - img_range[0]) / self.img.shape[0] * loc[2]
dy = (img_range[3] - img_range[2]) / self.img.shape[1] * loc[3]
return [(luloc + dx, ldloc - dy), (luloc + dx, ldloc)]
def plot_img2range(self, axes):
img_points = self.range_points_on_img()
range_points = [
(self.loc[1][0], self.loc[1][1]),
(self.loc[1][0], self.loc[1][1] + self.loc[1][3]),
]
print(img_points)
self.plot_path(axes, img_points[0], range_points[0])
self.plot_path(axes, img_points[1], range_points[1])
def plot_range2table(self, axes):
range_points = [
(self.loc[1][0] + self.loc[1][2], self.loc[1][1]),
(self.loc[1][0] + self.loc[1][2], self.loc[1][1] + self.loc[1][3]),
]
table_points = [
(self.loc[2][0] - self.dloc[0] * 2, self.loc[2][1] - self.dloc[1] * 2),
(
self.loc[2][0] - self.dloc[0] * 2,
self.loc[2][1] + self.loc[2][3] - self.dloc[1] * 2,
),
]
self.plot_path(axes, range_points[0], table_points[0])
self.plot_path(axes, range_points[1], table_points[1])
def plot_line(self, fig):
axes = fig.add_axes([0, 0, 1, 1])
self.plot_img2range(axes)
self.plot_range2table(axes)
axes.axis("off")
def plot(self, img_range):
self.range = img_range
range_img = self.img[
img_range[0] : img_range[1], img_range[2] : img_range[3], :
]
fig = plt.figure()
self.plot_img(fig)
self.plot_range_img(fig, range_img)
self.plot_table(fig, range_img)
self.plot_line(fig)
plt.show()
if __name__ == "__main__":
img_path = "lena_color_512.tif"
img = cv.imread(img_path, cv.IMREAD_UNCHANGED)
img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
p = Plotter(img)
p.plot([255, 265, 255, 265])
其实就是先画三个主图,一个全部的,一个小范围内的,一个RGB值的表,然后画四根线就完事了。效果如下:
唯一要注意的是 Axes
坐标系和图像的坐标系中Y轴是相反的,然后其他 axes.imshow
和 axes.table
都让它填满整个 Axes
就好了,要不然它给你自适应了,不好算几根连线的位置。
这里写的时候没想好,按 copilot 生成的 img_range
形式写下去了,目前是 [x起点,x终点,y起点,y终点]
的格式,换成 [x起点,y起点,x范围,y范围]
会更统一一点。但也懒得改了,一小时的工作时间白嫖室友一顿必胜客,很舒服。