শর্ট-টাইম ফুরিয়ার ট্রান্সফর্ম (STFT) ও স্পেকট্রোগ্রাম
এই পাঠে যা শিখবেন
- কেন সাধারণ DFT সময়ে পরিবর্তনশীল সিগন্যালের জন্য অপর্যাপ্ত
- STFT-এর সংজ্ঞা — উইন্ডো + স্লাইডিং-ফ্রেম DFT-এর সমন্বয়
- Python-এ হাতে-লেখা STFT দিয়ে একটি টেক্সট-ভিত্তিক স্পেকট্রোগ্রাম তৈরি করা
- সময়-রেজোলিউশন বনাম ফ্রিকোয়েন্সি-রেজোলিউশনের মধ্যকার মৌলিক ট্রেড-অফ
১ · কেন সাধারণ DFT যথেষ্ট নয়
M২২-এর DFT একটি সম্পূর্ণ সিগন্যালকে একটি একক ফ্রিকোয়েন্সি-স্পেকট্রামে রূপান্তর করে — এটি বলে দেয় সিগন্যালে কোন কোন ফ্রিকোয়েন্সি আছে, কিন্তু কখন তা ঘটেছে সে তথ্য হারিয়ে যায় (গাণিতিকভাবে, DFT সূত্রের সামেশনে সময়-তথ্য সম্পূর্ণ "মিশে" যায়)। বাস্তব সিগন্যাল — কথা বলা, গান, একটি যন্ত্র চালু-বন্ধ হওয়া — প্রায় সবসময় সময়ের সাথে সাথে ফ্রিকোয়েন্সি-বিষয়বস্তু বদলায়।
STFTShort-Time Fourier Transformসিগন্যালকে ছোট ছোট সময়-ফ্রেমে ভাগ করে প্রতিটি ফ্রেমে আলাদাভাবে উইন্ডো-করা DFT প্রয়োগকারী পদ্ধতি, যার ফলাফল সময় ও ফ্রিকোয়েন্সি উভয়ের ফাংশন। এই সমস্যার সমাধান করে সিগন্যালকে ছোট ছোট ফ্রেমে ভাগ করে:
$$X[m, k] = \sum_{n=0}^{L-1} w[n]\, x[n + mH]\, e^{-j2\pi kn/L}$$এখানে $L$ ফ্রেমের দৈর্ঘ্য, $H$ হপ-সাইজ (একটি ফ্রেম থেকে পরেরটিতে কত স্যাম্পল এগোনো হয়), $w[n]$ M২৪-এর মতো একটি উইন্ডো ফাংশন (Hamming/Hann), আর $m$ ফ্রেম-ইনডেক্স (সময়)। প্রতিটি $m$-এর জন্য একটি সম্পূর্ণ DFT বের হয় — ফলাফল একটি ২D গ্রিড: সময় ($m$) বনাম ফ্রিকোয়েন্সি ($k$)। এই গ্রিডের ম্যাগনিটিউড ছবি আকারে দেখানো হলে তাকে বলে স্পেকট্রোগ্রাম।
প্রতিটি ফ্রেমকে সরাসরি কেটে নিলে ফ্রেমের প্রান্তে হঠাৎ অসংলগ্নতা তৈরি হয়ে M২৪-এর স্পেকট্রাল লিকেজ বাড়ে — Hamming/Hann উইন্ডো প্রান্তের দিকে সিগন্যাল মসৃণভাবে শূন্যের কাছে নামিয়ে আনে।
হপ-সাইজ ফ্রেম-দৈর্ঘ্যের চেয়ে ছোট হলে ফ্রেমগুলো ওভারল্যাপ করে, যা সময়-অক্ষে মসৃণতর ট্রানজিশন দেয় — নিচের ডেমোতে সরলতার জন্য হপ = ফ্রেম-দৈর্ঘ্য (নন-ওভারল্যাপিং) ব্যবহার করা হয়েছে।
L৪৪-এর Welch's method ঠিক একই "সিগন্যাল ভাগ করো, প্রতিটি অংশে উইন্ডো + DFT নাও" কাঠামো ব্যবহার করে — পার্থক্য শুধু Welch গড় নেয় একটি স্থির স্পেকট্রামের জন্য, STFT প্রতিটি ফ্রেম আলাদা রাখে।
২ · একটি সত্যিকারের ডেমো — ফ্রিকোয়েন্সি-পরিবর্তন শনাক্তকরণ
নিচের কোডে একটি ৬৪-স্যাম্পলের সিগন্যাল তৈরি করা হয়েছে ($f_s=64$ Hz) — প্রথম ৩২ স্যাম্পল একটি $4$ Hz সাইন-তরঙ্গ, শেষ ৩২ স্যাম্পল একটি $12$ Hz সাইন-তরঙ্গ (দুটো অংশ সরাসরি জোড়া দেওয়া)। ফ্রেম-দৈর্ঘ্য $L=16$, হপ $H=16$ (নন-ওভারল্যাপিং, তাই মোট ৪টি ফ্রেম), প্রতিটি ফ্রেমে Hamming উইন্ডো প্রয়োগ করে M৫-এর হাতে-লেখা DFT দিয়ে ম্যাগনিটিউড স্পেকট্রাম বের করা হয়েছে — কোনো ইমেজ লাইব্রেরি ছাড়াই একটি টেক্সট-টেবিল হিসেবে "স্পেকট্রোগ্রাম" প্রিন্ট করা হয়েছে।
import math, cmath
def dft(x):
N = len(x)
X = []
for k in range(N):
s = 0 + 0j
for n in range(N):
s += x[n] * cmath.exp(-2j * math.pi * k * n / N)
X.append(s)
return X
def hamming(N):
return [0.54 - 0.46 * math.cos(2 * math.pi * n / (N - 1)) for n in range(N)]
fs = 64.0
f_low = 4.0
f_high = 12.0
half = 32
low_seg = [math.sin(2 * math.pi * f_low * n / fs) for n in range(half)]
high_seg = [math.sin(2 * math.pi * f_high * n / fs) for n in range(half)]
x = low_seg + high_seg # প্রথম অর্ধেক নিম্ন-ফ্রিকোয়েন্সি, দ্বিতীয় অর্ধেক উচ্চ-ফ্রিকোয়েন্সি
N_total = len(x)
frame_size = 16
hop = 16
win = hamming(frame_size)
frame_starts = []
start = 0
while start + frame_size <= N_total:
frame_starts.append(start)
start += hop
n_bins = frame_size // 2 + 1 # 0 থেকে Nyquist পর্যন্ত বিন
table = []
for fstart in frame_starts:
seg = x[fstart:fstart + frame_size]
wseg = [seg[i] * win[i] for i in range(frame_size)]
X = dft(wseg)
table.append([abs(X[k]) for k in range(n_bins)])
header = "frame | " + " | ".join(f"{k*fs/frame_size:>5.0f}Hz" for k in range(n_bins))
print(header)
for i, mags in enumerate(table):
row = f"{i:>5} | " + " | ".join(f"{m:>6.2f}" for m in mags)
print(row)
print()
for i, mags in enumerate(table):
peak_bin = max(range(n_bins), key=lambda k: mags[k])
peak_freq = peak_bin * fs / frame_size
s0, s1 = frame_starts[i], frame_starts[i] + frame_size - 1
print(f"frame {i} (samples {s0}-{s1}): পিক bin {peak_bin} (={peak_freq} Hz), ম্যাগনিটিউড {mags[peak_bin]:.2f}")
৩ · সময়-ফ্রিকোয়েন্সি ট্রেড-অফ
STFT-এর একটি মৌলিক সীমাবদ্ধতা আছে — ফ্রেম-দৈর্ঘ্য $L$ একসাথে সময়-রেজোলিউশন ও ফ্রিকোয়েন্সি-রেজোলিউশন উভয়ই নিয়ন্ত্রণ করে, কিন্তু বিপরীত দিকে:
ভালো সময়-রেজোলিউশন (ফ্রিকোয়েন্সি-পরিবর্তন দ্রুত ধরা পড়ে) কিন্তু M২৫-এর নিয়ম অনুযায়ী কম স্যাম্পল মানে কম ফ্রিকোয়েন্সি-রেজোলিউশন — বিন-স্পেসিং $f_s/L$ বড় হয়ে যায়।
ভালো ফ্রিকোয়েন্সি-রেজোলিউশন (কাছাকাছি ফ্রিকোয়েন্সি আলাদা করা যায়) কিন্তু একটি ফ্রেমের ভেতরে ফ্রিকোয়েন্সি বদলালে সেই পরিবর্তন ফ্রেমের গড়ে "ঝাপসা" হয়ে যায়।
উপরের ডেমোতে $L=16$ ব্যবহার করায় বিন-স্পেসিং $64/16=4$ Hz — যথেষ্ট বড় ফাঁক, কিন্তু যেহেতু ৪ Hz ও ১২ Hz উভয়ই ঠিক বিনের উপর পড়ে (bin ১ ও bin ৩), তাই এই নির্দিষ্ট উদাহরণে সমস্যা হয়নি। বাস্তব অ্যাপ্লিকেশনে (যেমন অডিও স্পেকট্রোগ্রাম, M৫২-এ বিস্তারিত) এই ট্রেড-অফ বিবেচনা করে $L$ বেছে নেওয়া হয়।
STFT সিগন্যালকে ছোট ছোট ফ্রেমে ভাগ করে প্রতিটিতে M২৪-এর উইন্ডো ও M৫-M৬-এর DFT প্রয়োগ করে সময় ও ফ্রিকোয়েন্সি উভয়ের একটি যৌথ চিত্র দেয় — উপরের ডেমো দেখাল এটি সত্যিকারের ফ্রিকোয়েন্সি-পরিবর্তন (৪ Hz থেকে ১২ Hz) স্পষ্টভাবে ধরতে পারে। তবে ফ্রেম-দৈর্ঘ্যের পছন্দ সময়-রেজোলিউশন ও ফ্রিকোয়েন্সি-রেজোলিউশনের মধ্যে একটি অনিবার্য আপস। পরের পাঠে (L৪৬) আমরা একটি ভিন্ন ট্রান্সফর্ম — DCT — দেখব, যা কমপ্রেশনের জন্য বিশেষভাবে উপযোগী কারণ এটি এনার্জি খুব কম কয়েকটি কোয়েফিসিয়েন্টে কেন্দ্রীভূত করে।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ যদি পুরো ৬৪-স্যাম্পল সিগন্যালের উপর একটিমাত্র সাধারণ ৬৪-বিন্দুর DFT নেওয়া হতো (STFT ছাড়া), তাহলে স্পেকট্রামে কী দেখা যেত?
স্পেকট্রামে ৪ Hz আর ১২ Hz — দুটো বিনেই পিক দেখা যেত (প্রতিটি সিগন্যালের অর্ধেক অংশ, তাই মোট এনার্জির প্রায় সমান ভাগ), কিন্তু কোনটা "আগে" আর কোনটা "পরে" ঘটেছে তার কোনো তথ্য থাকত না — DFT শুধু বলত "এই দুটো ফ্রিকোয়েন্সি সিগন্যালে আছে", সময়ের ক্রম সম্পূর্ণ হারিয়ে যেত। উপরের STFT ডেমো ঠিক এই সময়-তথ্যটাই পুনরুদ্ধার করে দেখায়।
প্র ০২ ফ্রেম ০ আর ফ্রেম ১-এর ম্যাগনিটিউড টেবিল হুবহু একই (৪.০১, ১.৯২, ...)। এটি কি প্রত্যাশিত?
হ্যাঁ, সম্পূর্ণ প্রত্যাশিত। যেহেতু পুরো নিম্ন-ফ্রিকোয়েন্সি অংশ (samples ০-৩১) একটি বিশুদ্ধ, ধ্রুবক অ্যামপ্লিটিউডের ৪ Hz সাইন-তরঙ্গ, এবং ফ্রেম ০ (samples ০-১৫) ও ফ্রেম ১ (samples ১৬-৩১) উভয়েই এই একই সিগন্যালের সমান-দৈর্ঘ্যের অংশ, তাদের উইন্ডো-করা DFT ফলাফল হুবহু এক হওয়ারই কথা (সিগন্যাল প্রতি ১৬ স্যাম্পলে পর্যায়বৃত্ত, কারণ $f_{low}=4$ Hz আর $f_s/f_{low}=16$)।
প্র ০৩ যদি ফ্রেম-দৈর্ঘ্য $L=64$ (পুরো সিগন্যাল একটি ফ্রেমে) করা হতো, STFT কি তখনও ফ্রিকোয়েন্সি-পরিবর্তন দেখাতে পারত?
না। $L=64$ মানে মাত্র একটি ফ্রেম থাকবে — এটি কার্যত সাধারণ DFT-তে পরিণত হয়ে যাবে (প্র ০১-এর উত্তরের মতো), সময়-রেজোলিউশন সম্পূর্ণ হারিয়ে যাবে। এটাই ৩ নং অংশের ট্রেড-অফের একটি চরম উদাহরণ — সবচেয়ে বড় সম্ভাব্য ফ্রেম সবচেয়ে ভালো ফ্রিকোয়েন্সি-রেজোলিউশন দেয় কিন্তু শূন্য সময়-রেজোলিউশন।
অনুশীলন
-
চিন্তা করুন: উপরের কোডে
frame_size = 16-কেframe_size = 8-এ বদলালে (hop-ও ৮ করে), মোট কতগুলো ফ্রেম তৈরি হবে বলে আপনার ধারণা? আর বিন-স্পেসিং কত হবে ($f_s/L$ সূত্র ব্যবহার করে)?মোট স্যাম্পল ৬৪, frame_size ও hop উভয়ই ৮ হলে $64/8=8$টি ফ্রেম তৈরি হবে (আগের ৪টির দ্বিগুণ, ভালো সময়-রেজোলিউশন)। বিন-স্পেসিং হবে $f_s/L = 64/8 = 8$ Hz — আগের ৪ Hz-এর চেয়ে বেশি বিস্তৃত (কম ফ্রিকোয়েন্সি-রেজোলিউশন), ঠিক ৩ নং অংশের ট্রেড-অফ অনুযায়ী।
-
পরীক্ষা করুন: কোড সেলে
frame_sizeওhopউভয়ই ৮-এ পরিবর্তন করে Run চেপে দেখুন — ফ্রিকোয়েন্সি-পরিবর্তন এখনও স্পষ্ট ধরা পড়ে কি না, আর ফ্রেম-সংখ্যা আপনার হিসেবের সাথে মেলে কি না।রান করলে ৮টি ফ্রেম দেখাবে (frame ০-৩ নিম্ন-ফ্রিকোয়েন্সি অংশে, frame ৪-৭ উচ্চ-ফ্রিকোয়েন্সি অংশে), আর পরিবর্তনটি frame ৩ ও frame ৪-এর মাঝে এখনও স্পষ্ট দেখাবে — বরং আরও নিখুঁতভাবে, কারণ ছোট ফ্রেম সময়ের সাথে দ্রুত পরিবর্তন আরও সূক্ষ্মভাবে ধরতে পারে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- ডিসক্রিট কোসাইন ট্রান্সফর্ম (DCT) ও কমপ্রেশন পরবর্তী পাঠ DFT-এর একটি আত্মীয় ট্রান্সফর্ম, যা এনার্জি অনেক কম কয়েকটি কোয়েফিসিয়েন্টে কেন্দ্রীভূত করে — JPEG/MP3-এর মতো কমপ্রেশন স্ট্যান্ডার্ডের ভিত্তি।
- অডিও প্রসেসিং-এ DSP M১২ STFT-স্পেকট্রোগ্রাম বাস্তব অডিও বিশ্লেষণের সবচেয়ে সাধারণ টুল — এই পাঠের কৌশল সরাসরি সেখানে প্রয়োগ হবে।
- Math for AI & ML কোর্স সহোদর কোর্স কমপ্লেক্স সংখ্যা ও ট্রিগোনোমেট্রির ভিত্তি — এই পাঠের DFT/উইন্ডো গণনার পটভূমি।