2025, Nov 01 09:32
ज़ीरो-पैडिंग के बिना ओवरलैप-आधारित 2D Pearson क्रॉस-कोरिलेशन
इमेज/सेंसर डेटा के लिए हर लैग पर वास्तविक ओवरलैप से 2D Pearson सहसंबंध निकालें: correlate2d आधारित तेज़ तरीका, स्थानीय योग/वर्ग-योग से R-मैप, बिना ज़ीरो-पैडिंग.
प्रत्येक ऑफ़सेट पर Pearson सहसंबंध जैसा व्यवहार करने वाला 2D क्रॉस-कोरिलेशन निकालना इमेज विश्लेषण और ग्रिड वाले सेंसर डेटा में अक्सर ज़रूरी होता है। चुनौती यह है कि ज़ीरो पैडिंग से बचें और हर लैग के लिए केवल वास्तविक ओवरलैप का उपयोग करें। नेस्टेड लूप्स के साथ प्रति-लैग pearsonr का सीधा तरीका सही तो है, पर बेहद धीमा पड़ता है। नीचे एक व्यावहारिक तरीका दिया गया है जो तेज़ी से काम करता है और हर शिफ्ट पर ओवरलैप वाले हिस्से के लिए Pearson R की परिभाषा को सटीक रखता है।
समस्या
आप ऐसा 2D कोरिलेशन सरफेस चाहते हैं जिसकी आकृति हर आयाम में (इनपुट का दोगुना − 1) हो, ठीक scipy.signal.correlate2d के full मोड जैसी। लेकिन किनारों पर ज़ीरो जोड़ने की जगह, हर आउटपुट सेल उस ऑफ़सेट पर वास्तव में ओवरलैप होने वाले डेटा से निकला Pearson सहसंबंध गुणांक होना चाहिए। यदि इनपुट का आकार rows × cols है, तो row_lag और col_lag पर ओवरलैप (rows − |row_lag|) × (cols − |col_lag|) होगा। एक सीधा उपाय यह है कि हर लैग पर उचित स्लाइस लें और फ़्लैट किए गए ओवरलैप पर pearsonr कॉल करें, लेकिन यह स्केल नहीं करता।
जो कोड सही लगता है, पर हर लैग पर Pearson नहीं देता
लुभावना तरीका यह है कि दोनों एरेज़ को ग्लोबली z-score करें, एक बार correlate2d चलाएँ और परिणाम को ओवरलैप आकार से भाग दें। यह तेज़ है, लेकिन सामान्यीकरण पूरे एरे पर होता है, ओवरलैप विंडो-वार नहीं; इसलिए गैर-शून्य लैग पर यह Pearson सहसंबंध गुणांक नहीं होता।
import numpy as np
from scipy.signal import correlate2d
def norm_std(arr):
return (arr - np.mean(arr)) / np.std(arr)
def xcorr2d_overlap_scaled(u, v):
assert u.shape == v.shape
assert u.ndim == 2
h, w = u.shape
dr = np.arange(-h + 1, h)
dc = np.arange(-w + 1, w)
ov_h = h - np.abs(dr)
ov_w = w - np.abs(dc)
overlap = ov_h.reshape((-1, 1)) * ov_w.reshape((1, -1))
u0 = norm_std(u)
v0 = norm_std(v)
raw = correlate2d(u0, v0)
return raw / overlap
यह सतह केवल केंद्र में Pearson R से मेल खाती है, जहाँ ओवरलैप पूरा एरे कवर करता है। केंद्र से दूर, Pearson के लिए हर लैग पर ओवरलैपिंग विंडो की स्थानीय औसत और स्थानीय मानक विचलन चाहिए। ग्लोबल z-scoring इसे नहीं पकड़ पाता।
सरल सामान्यीकरण क्यों विफल होता है
किसी दिए गए लैग पर Pearson सहसंबंध को ओवरलैपिंग मानों के कोवेरिएंस को उनके मानक विचलनों के गुणनफल से भाग देकर परिभाषित किया जाता है; दोनों ही आँकड़े उसी ओवरलैप क्षेत्र से निकाले जाते हैं। अंश में गुणनों का योग आता है, जिसमें से योगों के गुणनफल को ओवरलैपिंग तत्वों की संख्या से स्केल कर घटाया जाता है। हर लैग पर हर एरे का स्थानीय वैरिएंस हर विंडो के स्थानीय योग और वर्ग-योग पर निर्भर करता है; यही हर में जाता है। किसी globally normalized correlation को बस ओवरलैप आकार से विभाजित कर देने से ये प्रति-विंडो आँकड़े वापस नहीं मिलते, इसलिए पूर्ण ओवरलैप को छोड़ दें तो परिणाम Pearson R से मेल नहीं खाता।
कन्वोल्यूशन-शैली के समों के साथ तेज़ समाधान
कुंजी यह है कि हर ऑफ़सेट के लिए ओवरलैपिंग तत्वों की संख्या, उस ओवरलैप पर प्रत्येक एरे का योग, उनके वर्ग-योग, और उनके गुणनों का योग निकालें। इन सबको correlate2d से पाया जा सकता है—इनपुट्स को उन्हीं के आकार के ones वाले एरे से कन्वॉल्व करके। इनके सहारे आप एक ही वेक्टराइज़्ड पास में हर लैग के लिए Pearson के अंश और हर बना सकते हैं।
import numpy as np
from scipy.signal import correlate2d
def pearson_xcorr2d_fast(x, y):
assert x.shape == y.shape
x = x.astype(np.float64)
y = y.astype(np.float64)
h, w = x.shape
box = np.ones_like(x)
overlap_count = correlate2d(box, box)
sum_x = correlate2d(x, box)
sum_y = correlate2d(box, y)
sum_xy = correlate2d(x, y)
sum_x2 = correlate2d(x * x, box)
sum_y2 = correlate2d(box, y * y)
num = sum_xy - (sum_x * sum_y) / overlap_count
var_x = sum_x2 - (sum_x ** 2) / overlap_count
var_y = sum_y2 - (sum_y ** 2) / overlap_count
den = np.sqrt(var_x * var_y)
with np.errstate(invalid='ignore', divide='ignore'):
rmap = num / den
rmap[np.isnan(rmap)] = 0
return rmap
आउटपुट का आकार (2*h − 1, 2*w − 1) होता है, ठीक correlate2d के full मोड जैसा। हर सेल में उसी लैग पर दो इनपुट्स के ठीक-ठीक ओवरलैप क्षेत्र का Pearson सहसंबंध गुणांक आता है। जहाँ किसी ओवरलैप में स्थानीय वैरिएंस शून्य हो, मान अपरिभाषित होता है; ऊपर दिए गए कोड में ऐसे प्रविष्टियों को शून्य पर मैप किया गया है।
बीच के समों की जाँच
ओवरलैप-समझ रखने वाले इन समों की जाँच आप किसी भी चुने हुए लैग पर कर सकते हैं। किसी विशेष ऑफ़सेट पर x का योग, उसी ओवरलैप क्षेत्र के उपयुक्त स्लाइस के योग के बराबर निकलता है।
# एक सकारात्मक ऑफ़सेट के लिए उदाहरण जाँच
h, w = x.shape
lag_r, lag_c = 3, 7
r_idx = h - 1 + lag_r
c_idx = w - 1 + lag_c
x_slice = x[lag_r:, lag_c:]
np.allclose(sum_x[r_idx, c_idx], np.sum(x_slice))
यह उस मानसिक मॉडल से मेल खाता है कि ones कर्नेल के साथ correlate2d हर ऑफ़सेट पर ठीक-ठीक ओवरलैप होने वाले बिनों की गिनती और समेकन कर देता है।
यह क्यों मायने रखता है
वास्तविक 2D डेटा में हर लैग पर लूप चलाकर फ्लैट की गई विंडो पर बार-बार Pearson आँकड़े निकालना बेहद धीमा हो जाता है। कन्वोल्यूशन-आधारित तरीका correlate2d का लाभ उठाकर हर ऑफ़सेट के लिए आवश्यक सभी सम एक ही बार में निकाल देता है, जिससे आपको धीमे संदर्भ समाधान के अनुरूप सतह मिलती है। यह ग्लोबल सामान्यीकरण की उस गलती से भी बचाता है जो किनारों के पास मानों को बिगाड़ देती है। ध्यान दें कि sparsity वाले इनपुट में, जहाँ ज़्यादातर ओवरलैप क्षेत्रों में लगभग कोई वैरिएंस नहीं होता, Pearson R अपरिभाषित या शून्य हो सकता है; इसके विपरीत, correlate2d से मिलने वाला कच्चा क्रॉस-कोरिलेशन वैरिएंस से सामान्यीकरण न करने के कारण संरेखण शिखर दिखा सकता है।
मुख्य बातें
जब समानता को हर स्थानिक ऑफ़सेट पर सांख्यिकीय अर्थ में मापना हो, तो ओवरलैप-संवेदी Pearson सहसंबंध का उपयोग करें। ग्लोबल z-scoring के साथ साधारण कोरिलेशन, प्रति-लैग सामान्यीकरण का विकल्प नहीं है। ones एरे के साथ कन्वोल्यूशन की तकनीक सभी लैग्स पर गिनती, योग, वर्ग-योग और क्रॉस-सम कुशलता से देती है, और इन्हीं से सही Pearson R सतह बनती है। सत्यापन के लिए, छोटे इनपुट पर सरल for लूप के साथ बिंदु-वार तुलना करें। यदि आपके डेटा ओवरलैप में अधिकतर स्थिर हैं, तो उन स्थानों पर शून्य या अपरिभाषित सहसंबंध अपेक्षित है—और Pearson के संदर्भ में यही उचित भी है।