पाइथन में चयनित ग्रिड क्षेत्र के बाहरी किनारे को समोच्च करने का तरीका

Aug 18 2020

मेरे पास निम्नलिखित कोड हैं:

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)

जवाब

1 JohanC Aug 18 2020 at 05:18

यह पोस्ट इस तरह की लाइनों को खींचने का एक तरीका दिखाता है। चूंकि यह वर्तमान के अनुकूल होने के लिए सीधा नहीं है 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()

mathfux Aug 18 2020 at 10:48

मैं 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()

यह जाँच की जा रही कोशिकाओं का एक चित्रण है: