यदि एक बिंदु अजगर में बहुभुज के अंदर है तो जाँच का सबसे तेज़ तरीका क्या है


84

मुझे यह देखने के लिए दो मुख्य विधियाँ मिलीं कि क्या कोई बिंदु बहुभुज के अंदर है। एक यहाँ उपयोग की जाने वाली किरण अनुरेखण विधि का उपयोग कर रहा है , जो सबसे अधिक अनुशंसित उत्तर है, दूसरा माटप्लोटलिब का उपयोग कर रहा है path.contains_points(जो मुझे थोड़ा अस्पष्ट लगता है)। मुझे लगातार बहुत सारे पॉइंट्स चेक करने होंगे। क्या किसी को पता है कि क्या इन दोनों में से कोई भी अन्य की तुलना में अधिक अनुशंसा योग्य है या यदि बेहतर तीसरे विकल्प भी हैं?

अपडेट करें:

मैंने दो तरीकों की जाँच की और मैटलोट्लिब बहुत तेज़ दिखता है।

from time import time
import numpy as np
import matplotlib.path as mpltPath

# regular polygon for testing
lenpoly = 100
polygon = [[np.sin(x)+0.5,np.cos(x)+0.5] for x in np.linspace(0,2*np.pi,lenpoly)[:-1]]

# random points set of points to test 
N = 10000
points = zip(np.random.random(N),np.random.random(N))


# Ray tracing
def ray_tracing_method(x,y,poly):

    n = len(poly)
    inside = False

    p1x,p1y = poly[0]
    for i in range(n+1):
        p2x,p2y = poly[i % n]
        if y > min(p1y,p2y):
            if y <= max(p1y,p2y):
                if x <= max(p1x,p2x):
                    if p1y != p2y:
                        xints = (y-p1y)*(p2x-p1x)/(p2y-p1y)+p1x
                    if p1x == p2x or x <= xints:
                        inside = not inside
        p1x,p1y = p2x,p2y

    return inside

start_time = time()
inside1 = [ray_tracing_method(point[0], point[1], polygon) for point in points]
print "Ray Tracing Elapsed time: " + str(time()-start_time)

# Matplotlib mplPath
start_time = time()
path = mpltPath.Path(polygon)
inside2 = path.contains_points(points)
print "Matplotlib contains_points Elapsed time: " + str(time()-start_time)

जो देता है,

Ray Tracing Elapsed time: 0.441395998001
Matplotlib contains_points Elapsed time: 0.00994491577148

समान सापेक्ष अंतर 100 पक्षों के बहुभुज के बजाय एक त्रिकोण का उपयोग करके प्राप्त किया गया था। मैं भी इस तरह की समस्याओं के लिए समर्पित एक पैकेज लग रहा है, क्योंकि मैं भी सुडौल जाँच करेगा


चूंकि matplotlib का कार्यान्वयन C ++ है, आप संभवतः इसे तेज़ होने की उम्मीद कर सकते हैं। यह ध्यान में रखते हुए कि मैटप्लोटलिब का व्यापक रूप से उपयोग किया जाता है और चूंकि यह एक बहुत ही मौलिक कार्य है - यह शायद यह मान लेना भी सुरक्षित है कि यह सही तरीके से काम कर रहा है (भले ही यह "अस्पष्ट" लग सकता है)। पिछले नहीं बल्कि कम से कम: क्यों नहीं बस यह परीक्षण?
सेबस्टियन

मैंने परीक्षण के साथ सवाल अपडेट किया, जैसा कि आपने भविष्यवाणी की थी, मैटलपोटलिब बहुत तेज है। मैं चिंतित था क्योंकि अलग-अलग जगहों पर मैंने जो देखा है, उसमें मैटप्लोटलिब सबसे प्रसिद्ध प्रतिक्रिया नहीं है, और मैं जानना चाहता था कि क्या मैं कुछ (या कुछ बेहतर पैकेज) देख रहा था। इस तरह के एक सरल प्रश्न के लिए भी matplotlib एक बड़ा आदमी लग रहा था ।
रूबेन पेरेस-कार्रास्को

जवाबों:


98

आप सुडौल विचार कर सकते हैं :

from shapely.geometry import Point
from shapely.geometry.polygon import Polygon

point = Point(0.5, 0.5)
polygon = Polygon([(0, 0), (0, 1), (1, 1), (1, 0)])
print(polygon.contains(point))

आपके द्वारा बताए गए तरीकों से मैंने केवल दूसरे का उपयोग किया है path.contains_points, और यह ठीक काम करता है। आपके परीक्षण के लिए आवश्यक सटीकता के आधार पर किसी भी मामले में, मैं बहुभुज के अंदर सभी नोड्स के साथ एक सच्चा बूल ग्रिड बनाने का सुझाव दूंगा जो सच है (यदि नहीं तो गलत है)। यदि आप बहुत सारे बिंदुओं के लिए एक परीक्षण करने जा रहे हैं तो यह तेज़ हो सकता है ( हालाँकि यह ध्यान देता है कि आप "पिक्सेल" सहनशीलता के भीतर एक परीक्षण कर रहे हैं ):

from matplotlib import path
import matplotlib.pyplot as plt
import numpy as np

first = -3
size  = (3-first)/100
xv,yv = np.meshgrid(np.linspace(-3,3,100),np.linspace(-3,3,100))
p = path.Path([(0,0), (0, 1), (1, 1), (1, 0)])  # square with legs length 1 and bottom left corner at the origin
flags = p.contains_points(np.hstack((xv.flatten()[:,np.newaxis],yv.flatten()[:,np.newaxis])))
grid = np.zeros((101,101),dtype='bool')
grid[((xv.flatten()-first)/size).astype('int'),((yv.flatten()-first)/size).astype('int')] = flags

xi,yi = np.random.randint(-300,300,100)/100,np.random.randint(-300,300,100)/100
vflag = grid[((xi-first)/size).astype('int'),((yi-first)/size).astype('int')]
plt.imshow(grid.T,origin='lower',interpolation='nearest',cmap='binary')
plt.scatter(((xi-first)/size).astype('int'),((yi-first)/size).astype('int'),c=vflag,cmap='Greens',s=90)
plt.show()

, परिणाम यह है:

पिक्सेल सहिष्णुता के भीतर बहुभुज के अंदर बिंदु


1
धन्यवाद, उस समय के लिए, मैं matplotlib से चिपके रहूंगा क्योंकि यह कस्टम रे ट्रेसिंग की तुलना में बहुत तेज़ लगता है। फिर भी, मुझे वास्तव में अंतरिक्ष के विवेक का जवाब पसंद है, मुझे भविष्य में इसकी आवश्यकता हो सकती है। मैं भी सुडौलता से जाँच करूँगा क्योंकि यह इस तरह की समस्याओं के लिए समर्पित एक पैकेज दिखता है
रूबेन पेरेस-कैरास्को

18

यदि गति आपकी जरूरत है और अतिरिक्त निर्भरता कोई समस्या नहीं है, तो आप शायद numbaकाफी उपयोगी पाते हैं (अब इसे किसी भी प्लेटफॉर्म पर स्थापित करना बहुत आसान है)। ray_tracingआपके द्वारा प्रस्तावित क्लासिक दृष्टिकोण को डेकोरेटर numbaका उपयोग करके आसानी से पोर्ट किया जा सकता है numba @jitऔर बहुभुज को एक खस्ता सरणी में डाल दिया जा सकता है । कोड की तरह दिखना चाहिए:

@jit(nopython=True)
def ray_tracing(x,y,poly):
    n = len(poly)
    inside = False
    p2x = 0.0
    p2y = 0.0
    xints = 0.0
    p1x,p1y = poly[0]
    for i in range(n+1):
        p2x,p2y = poly[i % n]
        if y > min(p1y,p2y):
            if y <= max(p1y,p2y):
                if x <= max(p1x,p2x):
                    if p1y != p2y:
                        xints = (y-p1y)*(p2x-p1x)/(p2y-p1y)+p1x
                    if p1x == p2x or x <= xints:
                        inside = not inside
        p1x,p1y = p2x,p2y

    return inside

पहला निष्पादन किसी भी बाद की कॉल से थोड़ा अधिक समय लेगा:

%%time
polygon=np.array(polygon)
inside1 = [numba_ray_tracing_method(point[0], point[1], polygon) for 
point in points]

CPU times: user 129 ms, sys: 4.08 ms, total: 133 ms
Wall time: 132 ms

जो, संकलन के बाद घट जाएगा:

CPU times: user 18.7 ms, sys: 320 µs, total: 19.1 ms
Wall time: 18.4 ms

यदि आपको फ़ंक्शन के पहले कॉल में गति की आवश्यकता है तो आप एक मॉड्यूल का उपयोग करके कोड को प्री-कंपाइल कर सकते हैं pycc। फ़ंक्शन को src.py में स्टोर करें जैसे:

from numba import jit
from numba.pycc import CC
cc = CC('nbspatial')


@cc.export('ray_tracing',  'b1(f8, f8, f8[:,:])')
@jit(nopython=True)
def ray_tracing(x,y,poly):
    n = len(poly)
    inside = False
    p2x = 0.0
    p2y = 0.0
    xints = 0.0
    p1x,p1y = poly[0]
    for i in range(n+1):
        p2x,p2y = poly[i % n]
        if y > min(p1y,p2y):
            if y <= max(p1y,p2y):
                if x <= max(p1x,p2x):
                    if p1y != p2y:
                        xints = (y-p1y)*(p2x-p1x)/(p2y-p1y)+p1x
                    if p1x == p2x or x <= xints:
                        inside = not inside
        p1x,p1y = p2x,p2y

    return inside


if __name__ == "__main__":
    cc.compile()

इसे बनाएं python src.pyऔर चलाएं:

import nbspatial

import numpy as np
lenpoly = 100
polygon = [[np.sin(x)+0.5,np.cos(x)+0.5] for x in 
np.linspace(0,2*np.pi,lenpoly)[:-1]]

# random points set of points to test 
N = 10000
# making a list instead of a generator to help debug
points = zip(np.random.random(N),np.random.random(N))

polygon = np.array(polygon)

%%time
result = [nbspatial.ray_tracing(point[0], point[1], polygon) for point in points]

CPU times: user 20.7 ms, sys: 64 µs, total: 20.8 ms
Wall time: 19.9 ms

सुंबा कोड में मैंने इस्तेमाल किया: 'b1 (f8, f8, f8 [:,]])'

संकलन करने के लिए nopython=True, प्रत्येक संस्करण को पहले घोषित करने की आवश्यकता है for loop

पूर्वावलोकन में src कोड लाइन:

@cc.export('ray_tracing' , 'b1(f8, f8, f8[:,:])')

का उपयोग फ़ंक्शन नाम और उसके I / O var प्रकार, बुलियन आउटपुट b1और दो फ़्लोट f8और f8[:,:]इनपुट के रूप में फ़्लोट्स के दो-आयामी सरणी को घोषित करने के लिए किया जाता है ।


11

आपका परीक्षण अच्छा है, लेकिन यह केवल कुछ विशिष्ट स्थिति को मापता है: हमारे पास एक बहुभुज है जिसमें कई कोने हैं, और बहुभुज के भीतर उन्हें जांचने के लिए लंबे समय तक अंक हैं।

इसके अलावा, मुझे लगता है कि आप माप-माप-अंदर-बहुभुज-विधि बनाम किरण-विधि को नहीं माप रहे हैं, बल्कि matplotlib- किसी भी तरह से अनुकूलित-पुनरावृत्ति बनाम सरल-सूची-पुनरावृति

चलो एन स्वतंत्र तुलना करते हैं (बिंदु और बहुभुज के एन जोड़े)?

# ... your code...
lenpoly = 100
polygon = [[np.sin(x)+0.5,np.cos(x)+0.5] for x in np.linspace(0,2*np.pi,lenpoly)[:-1]]

M = 10000
start_time = time()
# Ray tracing
for i in range(M):
    x,y = np.random.random(), np.random.random()
    inside1 = ray_tracing_method(x,y, polygon)
print "Ray Tracing Elapsed time: " + str(time()-start_time)

# Matplotlib mplPath
start_time = time()
for i in range(M):
    x,y = np.random.random(), np.random.random()
    inside2 = path.contains_points([[x,y]])
print "Matplotlib contains_points Elapsed time: " + str(time()-start_time)

परिणाम:

Ray Tracing Elapsed time: 0.548588991165
Matplotlib contains_points Elapsed time: 0.103765010834

Matplotlib अभी भी बहुत बेहतर है, लेकिन 100 गुना बेहतर नहीं है। चलिए अब ज्यादा सरल बहुभुज की कोशिश करते हैं ...

lenpoly = 5
# ... same code

परिणाम:

Ray Tracing Elapsed time: 0.0727779865265
Matplotlib contains_points Elapsed time: 0.105288982391

6

मैं इसे यहाँ छोड़ दूँगा, बस ऊपर दिए गए कोड को फिर से लिखना होगा, शायद कोई इसे उपयोगी समझे:

def ray_tracing_numpy(x,y,poly):
    n = len(poly)
    inside = np.zeros(len(x),np.bool_)
    p2x = 0.0
    p2y = 0.0
    xints = 0.0
    p1x,p1y = poly[0]
    for i in range(n+1):
        p2x,p2y = poly[i % n]
        idx = np.nonzero((y > min(p1y,p2y)) & (y <= max(p1y,p2y)) & (x <= max(p1x,p2x)))[0]
        if p1y != p2y:
            xints = (y[idx]-p1y)*(p2x-p1x)/(p2y-p1y)+p1x
        if p1x == p2x:
            inside[idx] = ~inside[idx]
        else:
            idxx = idx[x[idx] <= xints]
            inside[idxx] = ~inside[idxx]    

        p1x,p1y = p2x,p2y
    return inside    

किरण में लपेटकर

def ray_tracing_mult(x,y,poly):
    return [ray_tracing(xi, yi, poly[:-1,:]) for xi,yi in zip(x,y)]

100000 अंकों पर परीक्षण, परिणाम:

ray_tracing_mult 0:00:00.850656
ray_tracing_numpy 0:00:00.003769

मैं केवल एक पाली और एक एक्स, वाई के लिए सही या गलत कैसे वापस कर सकता हूं?
जसार ओरियन

यदि आप केवल एक पाली कर रहे हैं तो मैं @epifanio समाधान का उपयोग करूंगा। NumPy समाधान बड़े बैचों में गणना के लिए बेहतर है।
कैन हिचबी टार्टानोग्लू
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.