วิธีการสร้างขอบนอกของขอบเขตกริดที่เลือกใน Python
ฉันมีรหัสต่อไปนี้:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-np.pi/2, np.pi/2, 30)
y = np.linspace(-np.pi/2, np.pi/2, 30)
x,y = np.meshgrid(x,y)
z = np.sin(x**2+y**2)[:-1,:-1]
fig,ax = plt.subplots()
ax.pcolormesh(x,y,z)
ซึ่งให้ภาพนี้:

ตอนนี้สมมติว่าฉันต้องการเน้นขอบบางช่องตาราง:
highlight = (z > 0.9)
ฉันสามารถใช้ฟังก์ชันเส้นโครงร่างได้ แต่จะส่งผลให้เส้นโครงร่าง "เรียบ" ฉันแค่ต้องการเน้นที่ขอบของพื้นที่ตามขอบของกล่องตาราง
สิ่งที่ใกล้เคียงที่สุดที่ฉันมาคือการเพิ่มสิ่งนี้:
highlight = np.ma.masked_less(highlight, 1)
ax.pcolormesh(x, y, highlight, facecolor = 'None', edgecolors = 'w')
ซึ่งให้พล็อตนี้:

ซึ่งใกล้เคียง แต่สิ่งที่ฉันต้องการจริงๆคือเน้นเฉพาะขอบด้านนอกและด้านในของ "โดนัท" เท่านั้น
โดยพื้นฐานแล้วฉันกำลังมองหาไฮบริดของฟังก์ชันรูปร่างและ pcolormesh ซึ่งเป็นสิ่งที่เป็นไปตามรูปร่างของค่าบางอย่าง แต่ตามถังขยะแบบกริดใน "ขั้นตอน" แทนที่จะเชื่อมต่อแบบจุดต่อจุด มันสมเหตุสมผลหรือไม่?
หมายเหตุด้านข้าง: ในอาร์กิวเมนต์ pcolormesh ฉันมีedgecolors = 'w'
แต่ขอบยังคงเป็นสีน้ำเงิน เกิดอะไรขึ้นที่นั่น?
แก้ไข:คำตอบเริ่มต้นของ JohanC โดยใช้ add_iso_line () ใช้ได้กับคำถามตามที่วางไว้ อย่างไรก็ตามข้อมูลจริงที่ฉันใช้เป็นเส้นตาราง x, y ที่ผิดปกติมากซึ่งไม่สามารถแปลงเป็น 1D ได้ (ตามความจำเป็นสำหรับadd_iso_line()
.
ฉันใช้ข้อมูลที่แปลงจากพิกัดเชิงขั้ว (rho, phi) เป็นคาร์ทีเซียน (x, y) โซลูชัน 2D ที่วางโดย JohanC ดูเหมือนจะไม่ทำงานในกรณีต่อไปนี้:
import numpy as np
import matplotlib.pyplot as plt
from scipy import ndimage
def pol2cart(rho, phi):
x = rho * np.cos(phi)
y = rho * np.sin(phi)
return(x, y)
phi = np.linspace(0,2*np.pi,30)
rho = np.linspace(0,2,30)
pp, rr = np.meshgrid(phi,rho)
xx,yy = pol2cart(rr, pp)
z = np.sin(xx**2 + yy**2)
scale = 5
zz = ndimage.zoom(z, scale, order=0)
fig,ax = plt.subplots()
ax.pcolormesh(xx,yy,z[:-1, :-1])
xlim = ax.get_xlim()
ylim = ax.get_ylim()
xmin, xmax = xx.min(), xx.max()
ymin, ymax = yy.min(), yy.max()
ax.contour(np.linspace(xmin,xmax, zz.shape[1]) + (xmax-xmin)/z.shape[1]/2,
np.linspace(ymin,ymax, zz.shape[0]) + (ymax-ymin)/z.shape[0]/2,
np.where(zz < 0.9, 0, 1), levels=[0.5], colors='red')
ax.set_xlim(*xlim)
ax.set_ylim(*ylim)

คำตอบ
โพสต์นี้แสดงวิธีการวาดเส้นดังกล่าว เนื่องจากไม่ตรงไปตรงมาที่จะปรับให้เข้ากับปัจจุบันpcolormesh
รหัสต่อไปนี้จึงแสดงให้เห็นถึงการปรับตัวที่เป็นไปได้ โปรดทราบว่าเวอร์ชัน 2d ของ x และ y ถูกเปลี่ยนชื่อแล้วเนื่องจากเวอร์ชัน 1d จำเป็นสำหรับส่วนของบรรทัด
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
x = np.linspace(-np.pi / 2, np.pi / 2, 30)
y = np.linspace(-np.pi / 2, np.pi / 2, 30)
xx, yy = np.meshgrid(x, y)
z = np.sin(xx ** 2 + yy ** 2)[:-1, :-1]
fig, ax = plt.subplots()
ax.pcolormesh(x, y, z)
def add_iso_line(ax, value, color):
v = np.diff(z > value, axis=1)
h = np.diff(z > value, axis=0)
l = np.argwhere(v.T)
vlines = np.array(list(zip(np.stack((x[l[:, 0] + 1], y[l[:, 1]])).T,
np.stack((x[l[:, 0] + 1], y[l[:, 1] + 1])).T)))
l = np.argwhere(h.T)
hlines = np.array(list(zip(np.stack((x[l[:, 0]], y[l[:, 1] + 1])).T,
np.stack((x[l[:, 0] + 1], y[l[:, 1] + 1])).T)))
lines = np.vstack((vlines, hlines))
ax.add_collection(LineCollection(lines, lw=1, colors=color))
add_iso_line(ax, 0.9, 'r')
plt.show()

นี่คือการปรับเปลี่ยนคำตอบที่สองซึ่งสามารถใช้ได้กับอาร์เรย์ 2d เท่านั้น:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from scipy import ndimage
x = np.linspace(-np.pi / 2, np.pi / 2, 30)
y = np.linspace(-np.pi / 2, np.pi / 2, 30)
x, y = np.meshgrid(x, y)
z = np.sin(x ** 2 + y ** 2)
scale = 5
zz = ndimage.zoom(z, scale, order=0)
fig, ax = plt.subplots()
ax.pcolormesh(x, y, z[:-1, :-1] )
xlim = ax.get_xlim()
ylim = ax.get_ylim()
xmin, xmax = x.min(), x.max()
ymin, ymax = y.min(), y.max()
ax.contour(np.linspace(xmin,xmax, zz.shape[1]) + (xmax-xmin)/z.shape[1]/2,
np.linspace(ymin,ymax, zz.shape[0]) + (ymax-ymin)/z.shape[0]/2,
np.where(zz < 0.9, 0, 1), levels=[0.5], colors='red')
ax.set_xlim(*xlim)
ax.set_ylim(*ylim)
plt.show()

ฉันจะพยายามปรับเปลี่ยนadd_iso_line
วิธีการเพื่อให้ชัดเจนยิ่งขึ้นเพื่อเปิดกว้างสำหรับการเพิ่มประสิทธิภาพ ดังนั้นในตอนแรกมีส่วนที่ต้องทำ:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
x = np.linspace(-np.pi/2, np.pi/2, 30)
y = np.linspace(-np.pi/2, np.pi/2, 30)
x, y = np.meshgrid(x,y)
z = np.sin(x**2+y**2)[:-1,:-1]
fig, ax = plt.subplots()
ax.pcolormesh(x,y,z)
xlim, ylim = ax.get_xlim(), ax.get_ylim()
highlight = (z > 0.9)
ตอนนี้highlight
เป็นอาร์เรย์ไบนารีที่มีลักษณะดังนี้:

add_iso_line
วิธีการ) ดังนั้นเพียงใช้การวนซ้ำง่ายๆ
lines = []
cells = zip(*np.where(highlight))
for x, y in cells:
if x == 0 or highlight[x - 1, y] == 0: lines.append(([x, y], [x, y + 1]))
if x == highlight.shape[0] or highlight[x + 1, y] == 0: lines.append(([x + 1, y], [x + 1, y + 1]))
if y == 0 or highlight[x, y - 1] == 0: lines.append(([x, y], [x + 1, y]))
if y == highlight.shape[1] or highlight[x, y + 1] == 0: lines.append(([x, y + 1], [x + 1, y + 1]))
และในที่สุดฉันก็ปรับขนาดและจัดกึ่งกลางของเส้นเพื่อให้พอดีกับ pcolormesh:
lines = (np.array(lines) / highlight.shape - [0.5, 0.5]) * [xlim[1] - xlim[0], ylim[1] - ylim[0]]
ax.add_collection(LineCollection(lines, colors='r'))
plt.show()
สรุปได้ว่านี่คล้ายกับโซลูชันของ JohanC มากและโดยทั่วไปช้ากว่า โชคดีที่เราสามารถลดจำนวนลงได้มากcells
โดยการดึงรูปทรงโดยใช้python-opencv
แพ็คเกจเท่านั้น
import cv2
highlight = highlight.astype(np.uint8)
contours, hierarchy = cv2.findContours(highlight, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cells = np.vstack(contours).squeeze()
นี่คือภาพประกอบของเซลล์ที่กำลังตรวจสอบ:
