पाइथन में चयनित ग्रिड क्षेत्र के बाहरी किनारे को समोच्च करने का तरीका
मेरे पास निम्नलिखित कोड हैं:
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'
, लेकिन किनारों अभी भी नीले रंग के लिए निकलते हैं। वहाँ क्या हो रहा है?
EDIT: add_iso_line () का उपयोग करते हुए जोहान्स का प्रारंभिक उत्तर प्रश्न के लिए काम करता है। हालाँकि, जो वास्तविक डेटा मैं उपयोग कर रहा हूं वह बहुत ही अनियमित x, y ग्रिड है, जिसे 1D में परिवर्तित नहीं किया जा सकता है (जैसा कि आवश्यक है add_iso_line()
।
मैं डेटा का उपयोग कर रहा हूं जिसे ध्रुवीय निर्देशांक (rho, phi) से कार्टेशियन (x, y) में परिवर्तित किया गया है। जोहान द्वारा प्रस्तुत 2 डी समाधान निम्नलिखित मामले के लिए काम नहीं करता है:
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
, निम्नलिखित कोड एक संभावित अनुकूलन प्रदर्शित करता है। ध्यान दें कि x और y के 2d संस्करणों का नाम बदल दिया गया है, क्योंकि लाइन खंडों के लिए 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]))
और, आखिरकार, मैं पीसीओर्मलेश के साथ फिट होने के लिए लाइनों के निर्देशांक का समन्वय और केंद्र करता हूं:
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()
निष्कर्ष में, यह जोहान समाधान के समान है और, सामान्य रूप से, धीमा। सौभाग्य से, हम 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()
यह जाँच की जा रही कोशिकाओं का एक चित्रण है: