पायथन वापरून व्हिडिओ कॉम्प्रेशन अल्गोरिदम्स सुरवातीपासून समजून घेण्यासाठी आणि अंमलात आणण्यासाठी एक सर्वसमावेशक मार्गदर्शक. आधुनिक व्हिडिओ कोडेक्समागील सिद्धांत आणि सराव शिका.
पायथनमध्ये व्हिडिओ कोडेक तयार करणे: कॉम्प्रेशन अल्गोरिदम्सचा सखोल अभ्यास
आपल्या आजच्या हायपर-कनेक्टेड जगात, व्हिडिओचे महत्त्व सर्वाधिक आहे. स्ट्रीमिंग सेवा आणि व्हिडिओ कॉन्फरन्सिंगपासून ते सोशल मीडिया फीडपर्यंत, डिजिटल व्हिडिओ इंटरनेट ट्रॅफिकवर वर्चस्व गाजवत आहे. पण हाय-डेफिनिशन चित्रपट सामान्य इंटरनेट कनेक्शनवरून पाठवणे कसे शक्य होते? याचे उत्तर एका आकर्षक आणि गुंतागुंतीच्या क्षेत्रात आहे: व्हिडिओ कॉम्प्रेशन. या तंत्रज्ञानाच्या केंद्रस्थानी व्हिडिओ कोडेक (COder-DECoder) आहे, जो फाईलचा आकार लक्षणीयरीत्या कमी करण्यासाठी आणि दृश्यात्मक गुणवत्ता टिकवून ठेवण्यासाठी डिझाइन केलेल्या अत्याधुनिक अल्गोरिदमचा संच आहे.
जरी H.264, HEVC (H.265) आणि रॉयल्टी-मुक्त AV1 सारखे उद्योग-मानक कोडेक्स अभियांत्रिकीचे अत्यंत गुंतागुंतीचे नमुने असले तरी, त्यांची मूलभूत तत्त्वे समजून घेणे कोणत्याही प्रेरित विकसकासाठी शक्य आहे. हे मार्गदर्शक तुम्हाला व्हिडिओ कॉम्प्रेशनच्या जगात खोलवर घेऊन जाईल. आम्ही फक्त सिद्धांतावर बोलणार नाही; आम्ही पायथन वापरून एक सोपा, शैक्षणिक व्हिडिओ कोडेक सुरवातीपासून तयार करू. आधुनिक व्हिडिओ स्ट्रीमिंग शक्य करणाऱ्या सुंदर कल्पना समजून घेण्याचा हा प्रत्यक्ष अनुभव हा सर्वोत्तम मार्ग आहे.
पायथन का? जरी ही भाषा रिअल-टाइम, उच्च-कार्यक्षमतेच्या व्यावसायिक कोडेकसाठी (जे सामान्यतः C/C++ किंवा असेंब्लीमध्ये लिहिले जातात) वापरली जात नसली तरी, पायथनची सुवाच्यता आणि नम्पी (NumPy), सायपाय (SciPy), आणि ओपनसीव्ही (OpenCV) सारख्या शक्तिशाली लायब्ररीजमुळे ते शिकण्यासाठी, प्रोटोटाइपिंगसाठी आणि संशोधनासाठी एक योग्य वातावरण आहे. तुम्ही लो-लेव्हल मेमरी मॅनेजमेंटमध्ये न अडकता अल्गोरिदमवर लक्ष केंद्रित करू शकता.
व्हिडिओ कॉम्प्रेशनच्या मूलभूत संकल्पना समजून घेणे
कोडची एक ओळ लिहिण्यापूर्वी, आपण काय साध्य करण्याचा प्रयत्न करत आहोत हे समजून घेणे आवश्यक आहे. व्हिडिओ कॉम्प्रेशनचे ध्येय अनावश्यक डेटा काढून टाकणे आहे. एक कच्चा, अनकॉम्प्रेस्ड व्हिडिओ प्रचंड मोठा असतो. 30 फ्रेम्स प्रति सेकंद दराने 1080p व्हिडिओच्या एका मिनिटाचा आकार 7 GB पेक्षा जास्त असू शकतो. या डेटाच्या राक्षसाला काबूत आणण्यासाठी, आपण दोन प्राथमिक प्रकारच्या रिडंडंसीचा (redundancy) फायदा घेतो.
कॉम्प्रेशनचे दोन स्तंभ: स्पेशियल आणि टेम्पोरल रिडंडंसी
- स्पेशियल (इंट्रा-फ्रेम) रिडंडंसी: ही एकाच फ्रेममधील रिडंडंसी आहे. निळ्या आकाशाच्या मोठ्या भागाचा किंवा पांढऱ्या भिंतीचा विचार करा. त्या भागातील प्रत्येक पिक्सेलसाठी रंगाचे मूल्य साठवण्याऐवजी, आपण ते अधिक कार्यक्षमतेने वर्णन करू शकतो. हेच तत्त्व जेपीईजी (JPEG) सारख्या इमेज कॉम्प्रेशन फॉरमॅटमागे आहे.
- टेम्पोरल (इंटर-फ्रेम) रिडंडंसी: ही एकमेकांशेजारील फ्रेम्समधील रिडंडंसी आहे. बहुतेक व्हिडिओमध्ये, एका फ्रेममधून दुसऱ्या फ्रेममध्ये दृश्य पूर्णपणे बदलत नाही. उदाहरणार्थ, स्थिर पार्श्वभूमीसमोर बोलणारी व्यक्ती, यामध्ये प्रचंड प्रमाणात टेम्पोरल रिडंडंसी असते. पार्श्वभूमी तशीच राहते; प्रतिमेचा फक्त एक छोटासा भाग (व्यक्तीचा चेहरा आणि शरीर) हलतो. व्हिडिओ कॉम्प्रेशनमध्ये हा सर्वात मोठा स्रोत आहे.
मुख्य फ्रेम प्रकार: आय-फ्रेम्स, पी-फ्रेम्स, आणि बी-फ्रेम्स
टेम्पोरल रिडंडंसीचा फायदा घेण्यासाठी, कोडेक्स प्रत्येक फ्रेमला समान मानत नाहीत. ते त्यांना वेगवेगळ्या प्रकारांमध्ये वर्गीकृत करतात, ज्यामुळे ग्रुप ऑफ पिक्चर्स (GOP) नावाची एक क्रमवारी तयार होते.
- आय-फ्रेम (इंट्रा-कोडेड फ्रेम): आय-फ्रेम ही एक पूर्ण, स्वयंपूर्ण प्रतिमा आहे. ती फक्त स्पेशियल रिडंडंसी वापरून कॉम्प्रेस केली जाते, अगदी जेपीईजी (JPEG) प्रमाणे. आय-फ्रेम्स व्हिडिओ स्ट्रीममध्ये अँकर पॉइंट्स म्हणून काम करतात, ज्यामुळे दर्शक प्लेबॅक सुरू करू शकतो किंवा नवीन स्थितीवर जाऊ शकतो. हे सर्वात मोठे फ्रेम प्रकार आहेत परंतु व्हिडिओ पुन्हा तयार करण्यासाठी आवश्यक आहेत.
- पी-फ्रेम (प्रेडिक्टेड फ्रेम): पी-फ्रेम मागील आय-फ्रेम किंवा पी-फ्रेमकडे पाहून एन्कोड केली जाते. संपूर्ण चित्र साठवण्याऐवजी, ती फक्त फरक साठवते. उदाहरणार्थ, ती अशा सूचना साठवते की "मागील फ्रेममधून पिक्सेलचा हा ब्लॉक घ्या, त्याला ५ पिक्सेल उजवीकडे हलवा, आणि येथे रंगांमधील किरकोळ बदल आहेत." ही प्रक्रिया मोशन एस्टिमेशन द्वारे साधली जाते.
- बी-फ्रेम (बाय-डायरेक्शनली प्रेडिक्टेड फ्रेम): बी-फ्रेम सर्वात कार्यक्षम आहे. ती प्रेडिक्शनसाठी मागील आणि पुढील दोन्ही फ्रेम्सचा संदर्भ म्हणून वापर करू शकते. हे अशा दृश्यांसाठी उपयुक्त आहे जेथे एखादी वस्तू तात्पुरती लपते आणि नंतर पुन्हा दिसते. पुढे आणि मागे पाहून, कोडेक अधिक अचूक आणि डेटा-कार्यक्षम प्रेडिक्शन तयार करू शकतो. तथापि, भविष्यातील फ्रेम्स वापरल्याने थोडा विलंब (लेटन्सी) होतो, ज्यामुळे ते व्हिडिओ कॉलसारख्या रिअल-टाइम ऍप्लिकेशन्ससाठी कमी योग्य ठरतात.
एक सामान्य GOP असा दिसू शकतो: I B B P B B P B B I .... एन्कोडर कॉम्प्रेशन कार्यक्षमता आणि सीकेबिलिटी (seekability) यांच्यात संतुलन साधण्यासाठी फ्रेम्सचा सर्वोत्तम पॅटर्न ठरवतो.
कॉम्प्रेशन पाइपलाइन: एक टप्प्याटप्प्याने विश्लेषण
आधुनिक व्हिडिओ एन्कोडिंग ही एक बहु-टप्प्याची पाइपलाइन आहे. प्रत्येक टप्पा डेटाला अधिक कॉम्प्रेस करण्यायोग्य बनवण्यासाठी रूपांतरित करतो. चला एका फ्रेमला एन्कोड करण्याच्या मुख्य पायऱ्यांमधून जाऊया.

पायरी १: कलर स्पेस कन्व्हर्जन (RGB ते YCbCr)
बहुतेक व्हिडिओ RGB (लाल, हिरवा, निळा) कलर स्पेसमध्ये सुरू होतात. तथापि, मानवी डोळा रंगातील (क्रोमा) बदलांपेक्षा तेजस्वीपणातील (ल्यूमा) बदलांसाठी अधिक संवेदनशील असतो. कोडेक्स याचा फायदा RGB ला YCbCr सारख्या ल्यूमा/क्रोमा फॉरमॅटमध्ये रूपांतरित करून घेतात.
- Y: ल्यूमा घटक (तेजस्वीपणा).
- Cb: निळा-फरक असलेला क्रोमा घटक.
- Cr: लाल-फरक असलेला क्रोमा घटक.
तेजस्वीपणाला रंगापासून वेगळे करून, आपण क्रोमा सबसॅम्पलिंग लागू करू शकतो. हे तंत्रज्ञान रंगाच्या चॅनेल्सचे (Cb आणि Cr) रिझोल्यूशन कमी करते आणि तेजस्वीपणाच्या चॅनेलचे (Y) पूर्ण रिझोल्यूशन कायम ठेवते, ज्यासाठी आपले डोळे सर्वात संवेदनशील असतात. एक सामान्य योजना 4:2:0 आहे, जी रंगाची ७५% माहिती जवळजवळ कोणत्याही दृश्यमान गुणवत्तेच्या नुकसानीशिवाय काढून टाकते, ज्यामुळे तात्काळ कॉम्प्रेशन साधले जाते.
पायरी २: फ्रेम पार्टिशनिंग (मॅक्रोब्लॉक्स)
एन्कोडर संपूर्ण फ्रेमवर एकाच वेळी प्रक्रिया करत नाही. तो फ्रेमला लहान ब्लॉक्समध्ये विभाजित करतो, सामान्यतः १६x१६ किंवा ८x८ पिक्सेल, ज्यांना मॅक्रोब्लॉक्स म्हणतात. त्यानंतरच्या सर्व प्रक्रिया (प्रेडिक्शन, ट्रान्सफॉर्म, इ.) ब्लॉक-दर-ब्लॉक आधारावर केल्या जातात.
पायरी ३: प्रेडिक्शन (इंटर आणि इंट्रा)
येथे खरी जादू घडते. प्रत्येक मॅक्रोब्लॉकसाठी, एन्कोडर इंट्रा-फ्रेम किंवा इंटर-फ्रेम प्रेडिक्शन वापरायचे की नाही हे ठरवतो.
- आय-फ्रेमसाठी (इंट्रा-प्रेडिक्शन): एन्कोडर सध्याच्या ब्लॉकचे प्रेडिक्शन त्याच फ्रेममधील आधीच एन्कोड केलेल्या शेजारील ब्लॉक्सच्या (वरच्या आणि डावीकडील ब्लॉक्स) पिक्सेलवर आधारित करतो. त्यानंतर त्याला फक्त प्रेडिक्शन आणि प्रत्यक्ष ब्लॉक यांच्यातील लहान फरक (रेसिड्यूअल) एन्कोड करावा लागतो.
- पी-फ्रेम किंवा बी-फ्रेमसाठी (इंटर-प्रेडिक्शन): हे मोशन एस्टिमेशन आहे. एन्कोडर एका संदर्भ फ्रेममध्ये जुळणारा ब्लॉक शोधतो. जेव्हा त्याला सर्वोत्तम जुळणारा ब्लॉक सापडतो, तेव्हा तो एक मोशन वेक्टर (उदा. "१० पिक्सेल उजवीकडे, २ पिक्सेल खाली हलवा") नोंदवतो आणि रेसिड्यूअलची गणना करतो. अनेकदा, रेसिड्यूअल शून्याच्या जवळ असतो, ज्यामुळे त्याला एन्कोड करण्यासाठी खूप कमी बिट्स लागतात.
पायरी ४: ट्रान्सफॉर्मेशन (उदा. डिस्क्रीट कोसाइन ट्रान्सफॉर्म - DCT)
प्रेडिक्शननंतर, आपल्याला एक रेसिड्यूअल ब्लॉक मिळतो. हा ब्लॉक डिस्क्रीट कोसाइन ट्रान्सफॉर्म (DCT) सारख्या गणितीय ट्रान्सफॉर्मेशनमधून जातो. DCT स्वतः डेटा कॉम्प्रेस करत नाही, परंतु तो डेटा कसा दर्शवला जातो हे मूलभूतपणे बदलते. तो स्पेशियल पिक्सेल व्हॅल्यूजना फ्रिक्वेन्सी कोइफिशिएंट्समध्ये रूपांतरित करतो. DCT ची जादू ही आहे की बहुतेक नैसर्गिक प्रतिमांसाठी, तो बहुतेक दृश्यात्मक ऊर्जा ब्लॉकच्या वरच्या-डाव्या कोपऱ्यातील काही कोइफिशिएंट्समध्ये (लो-फ्रिक्वेन्सी घटक) केंद्रित करतो, तर उर्वरित कोइफिशिएंट्स (हाय-फ्रिक्वेन्सी नॉईज) शून्याच्या जवळ असतात.
पायरी ५: क्वांटायझेशन (Quantization)
ही पाइपलाइनमधील प्राथमिक लॉसी (lossy) पायरी आहे आणि गुणवत्ता विरुद्ध बिटरेट यांच्यातील तडजोड नियंत्रित करण्याची गुरुकिल्ली आहे. DCT कोइफिशिएंट्सच्या रूपांतरित ब्लॉकला क्वांटायझेशन मॅट्रिक्सने भागले जाते आणि परिणामांना जवळच्या पूर्णांकात राऊंड केले जाते. क्वांटायझेशन मॅट्रिक्समध्ये हाय-फ्रिक्वेन्सी कोइफिशिएंट्ससाठी मोठी मूल्ये असतात, ज्यामुळे त्यापैकी बरेच प्रभावीपणे शून्य होतात. येथे मोठ्या प्रमाणात डेटा काढून टाकला जातो. उच्च क्वांटायझेशन पॅरामीटरमुळे अधिक शून्य, उच्च कॉम्प्रेशन आणि कमी दृश्यात्मक गुणवत्ता (जी अनेकदा ब्लॉकी आर्टिफॅक्ट्स म्हणून दिसते) मिळते.
पायरी ६: एन्ट्रॉपी कोडिंग
अंतिम टप्पा हा एक लॉसलेस कॉम्प्रेशनचा टप्पा आहे. क्वांटाइज्ड कोइफिशिएंट्स, मोशन वेक्टर्स आणि इतर मेटाडेटा स्कॅन करून बायनरी स्ट्रीममध्ये रूपांतरित केले जातात. रन-लेंथ एन्कोडिंग (RLE) आणि हफमन कोडिंग किंवा CABAC (Context-Adaptive Binary Arithmetic Coding) सारख्या अधिक प्रगत पद्धती वापरल्या जातात. हे अल्गोरिदम अधिक वारंवार येणाऱ्या प्रतीकांना (जसे की क्वांटायझेशनमुळे तयार झालेले अनेक शून्य) लहान कोड आणि कमी वारंवार येणाऱ्या प्रतीकांना लांब कोड देतात, ज्यामुळे डेटा स्ट्रीममधून अंतिम बिट्स पिळून काढले जातात.
डीकोडर फक्त या पायऱ्या उलट क्रमाने करतो: एन्ट्रॉपी डीकोडिंग -> इन्व्हर्स क्वांटायझेशन -> इन्व्हर्स ट्रान्सफॉर्म -> मोशन कॉम्पेन्सेशन -> फ्रेमची पुनर्रचना.
पायथनमध्ये एक सरलीकृत व्हिडिओ कोडेक कार्यान्वित करणे
आता, सिद्धांताला प्रत्यक्षात उतरवूया. आपण एक शैक्षणिक कोडेक तयार करू जो आय-फ्रेम्स आणि पी-फ्रेम्स वापरेल. तो मोशन एस्टिमेशन, DCT, क्वांटायझेशन आणि संबंधित डीकोडिंग पायऱ्यांची मुख्य पाइपलाइन दर्शवेल.
अस्वीकरण: हा शिकण्यासाठी तयार केलेला एक *टॉय* कोडेक आहे. तो ऑप्टिमाइझ केलेला नाही आणि H.264 शी तुलना करता येणारे परिणाम देणार नाही. आमचे ध्येय अल्गोरिदमना प्रत्यक्ष कृतीत पाहणे आहे.
पूर्व-आवश्यकता
तुम्हाला खालील पायथन लायब्ररींची आवश्यकता असेल. तुम्ही त्या pip वापरून इन्स्टॉल करू शकता:
pip install numpy opencv-python scipy
प्रोजेक्टची रचना
चला आपला कोड काही फाइल्समध्ये व्यवस्थित करूया:
main.py: एन्कोडिंग आणि डीकोडिंग प्रक्रिया चालवण्यासाठी मुख्य स्क्रिप्ट.encoder.py: एन्कोडरसाठी तर्कशास्त्र समाविष्ट आहे.decoder.py: डीकोडरसाठी तर्कशास्त्र समाविष्ट आहे.utils.py: व्हिडिओ I/O आणि ट्रान्सफॉर्मेशनसाठी मदतनीस फंक्शन्स.
भाग १: मुख्य युटिलिटीज (utils.py)
आपण DCT, क्वांटायझेशन आणि त्यांच्या इन्व्हर्ससाठी मदतनीस फंक्शन्सने सुरुवात करू. आपल्याला फ्रेमला ब्लॉक्समध्ये विभाजित करण्यासाठी एका फंक्शनची देखील आवश्यकता असेल.
# utils.py
import numpy as np
from scipy.fftpack import dct, idct
BLOCK_SIZE = 8
# A standard JPEG quantization matrix (scaled for our purposes)
QUANTIZATION_MATRIX = np.array([
[16, 11, 10, 16, 24, 40, 51, 61],
[12, 12, 14, 19, 26, 58, 60, 55],
[14, 13, 16, 24, 40, 57, 69, 56],
[14, 17, 22, 29, 51, 87, 80, 62],
[18, 22, 37, 56, 68, 109, 103, 77],
[24, 35, 55, 64, 81, 104, 113, 92],
[49, 64, 78, 87, 103, 121, 120, 101],
[72, 92, 95, 98, 112, 100, 103, 99]
])
def apply_dct(block):
"""Applies 2D DCT to a block."""
# Center the pixel values around 0
block = block - 128
return dct(dct(block.T, norm='ortho').T, norm='ortho')
def apply_idct(dct_block):
"""Applies 2D Inverse DCT to a block."""
block = idct(idct(dct_block.T, norm='ortho').T, norm='ortho')
# De-center and clip to valid pixel range
return np.round(block + 128).clip(0, 255)
def quantize(dct_block, qp=1):
"""Quantizes a DCT block. qp is a quality parameter."""
return np.round(dct_block / (QUANTIZATION_MATRIX * qp)).astype(int)
def dequantize(quantized_block, qp=1):
"""Dequantizes a block."""
return quantized_block * (QUANTIZATION_MATRIX * qp)
def frame_to_blocks(frame):
"""Splits a frame into 8x8 blocks."""
blocks = []
h, w = frame.shape
for i in range(0, h, BLOCK_SIZE):
for j in range(0, w, BLOCK_SIZE):
blocks.append(frame[i:i+BLOCK_SIZE, j:j+BLOCK_SIZE])
return blocks
def blocks_to_frame(blocks, h, w):
"""Reconstructs a frame from 8x8 blocks."""
frame = np.zeros((h, w), dtype=np.uint8)
k = 0
for i in range(0, h, BLOCK_SIZE):
for j in range(0, w, BLOCK_SIZE):
frame[i:i+BLOCK_SIZE, j:j+BLOCK_SIZE] = blocks[k]
k += 1
return frame
भाग २: एन्कोडर (encoder.py)
एन्कोडर हा सर्वात गुंतागुंतीचा भाग आहे. आपण मोशन एस्टिमेशनसाठी एक सोपा ब्लॉक-मॅचिंग अल्गोरिदम लागू करू आणि नंतर आय-फ्रेम्स आणि पी-फ्रेम्सवर प्रक्रिया करू.
# encoder.py
import numpy as np
from utils import apply_dct, quantize, frame_to_blocks, BLOCK_SIZE
def get_motion_vectors(current_frame, reference_frame, search_range=8):
"""A simple block matching algorithm for motion estimation."""
h, w = current_frame.shape
motion_vectors = []
for i in range(0, h, BLOCK_SIZE):
for j in range(0, w, BLOCK_SIZE):
current_block = current_frame[i:i+BLOCK_SIZE, j:j+BLOCK_SIZE]
best_match_sad = float('inf')
best_match_vector = (0, 0)
# Search in the reference frame
for y in range(-search_range, search_range + 1):
for x in range(-search_range, search_range + 1):
ref_i, ref_j = i + y, j + x
if 0 <= ref_i <= h - BLOCK_SIZE and 0 <= ref_j <= w - BLOCK_SIZE:
ref_block = reference_frame[ref_i:ref_i+BLOCK_SIZE, ref_j:ref_j+BLOCK_SIZE]
sad = np.sum(np.abs(current_block - ref_block))
if sad < best_match_sad:
best_match_sad = sad
best_match_vector = (y, x)
motion_vectors.append(best_match_vector)
return motion_vectors
def encode_iframe(frame, qp=1):
"""Encodes an I-frame."""
h, w = frame.shape
blocks = frame_to_blocks(frame)
quantized_blocks = []
for block in blocks:
dct_block = apply_dct(block.astype(float))
quantized_block = quantize(dct_block, qp)
quantized_blocks.append(quantized_block)
return {'type': 'I', 'h': h, 'w': w, 'data': quantized_blocks, 'qp': qp}
def encode_pframe(current_frame, reference_frame, qp=1):
"""Encodes a P-frame."""
h, w = current_frame.shape
motion_vectors = get_motion_vectors(current_frame, reference_frame)
quantized_residuals = []
k = 0
for i in range(0, h, BLOCK_SIZE):
for j in range(0, w, BLOCK_SIZE):
current_block = current_frame[i:i+BLOCK_SIZE, j:j+BLOCK_SIZE]
mv_y, mv_x = motion_vectors[k]
ref_block = reference_frame[i+mv_y : i+mv_y+BLOCK_SIZE, j+mv_x : j+mv_x+BLOCK_SIZE]
residual = current_block.astype(float) - ref_block.astype(float)
dct_residual = apply_dct(residual)
quantized_residual = quantize(dct_residual, qp)
quantized_residuals.append(quantized_residual)
k += 1
return {'type': 'P', 'motion_vectors': motion_vectors, 'data': quantized_residuals, 'qp': qp}
भाग ३: डीकोडर (decoder.py)
डीकोडर प्रक्रिया उलट करतो. पी-फ्रेम्ससाठी, तो संग्रहित मोशन वेक्टर्स वापरून मोशन कॉम्पेन्सेशन करतो.
# decoder.py
import numpy as np
from utils import apply_idct, dequantize, blocks_to_frame, BLOCK_SIZE
def decode_iframe(encoded_frame):
"""Decodes an I-frame."""
h, w = encoded_frame['h'], encoded_frame['w']
qp = encoded_frame['qp']
quantized_blocks = encoded_frame['data']
reconstructed_blocks = []
for q_block in quantized_blocks:
dct_block = dequantize(q_block, qp)
block = apply_idct(dct_block)
reconstructed_blocks.append(block.astype(np.uint8))
return blocks_to_frame(reconstructed_blocks, h, w)
def decode_pframe(encoded_frame, reference_frame):
"""Decodes a P-frame using its reference frame."""
h, w = reference_frame.shape
qp = encoded_frame['qp']
motion_vectors = encoded_frame['motion_vectors']
quantized_residuals = encoded_frame['data']
reconstructed_blocks = []
k = 0
for i in range(0, h, BLOCK_SIZE):
for j in range(0, w, BLOCK_SIZE):
# Decode the residual
dct_residual = dequantize(quantized_residuals[k], qp)
residual = apply_idct(dct_residual)
# Perform motion compensation
mv_y, mv_x = motion_vectors[k]
ref_block = reference_frame[i+mv_y : i+mv_y+BLOCK_SIZE, j+mv_x : j+mv_x+BLOCK_SIZE]
# Reconstruct the block
reconstructed_block = (ref_block.astype(float) + residual).clip(0, 255)
reconstructed_blocks.append(reconstructed_block.astype(np.uint8))
k += 1
return blocks_to_frame(reconstructed_blocks, h, w)
भाग ४: सर्व एकत्र आणणे (main.py)
ही स्क्रिप्ट संपूर्ण प्रक्रिया आयोजित करते: व्हिडिओ वाचणे, फ्रेम-दर-फ्रेम एन्कोड करणे, आणि नंतर अंतिम आउटपुट तयार करण्यासाठी डीकोड करणे.
# main.py
import cv2
import pickle # For saving/loading our compressed data structure
from encoder import encode_iframe, encode_pframe
from decoder import decode_iframe, decode_pframe
def main(input_path, output_path, compressed_file_path):
cap = cv2.VideoCapture(input_path)
frames = []
while True:
ret, frame = cap.read()
if not ret:
break
# We'll work with the grayscale (luma) channel for simplicity
frames.append(cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY))
cap.release()
# --- ENCODING --- #
print("Encoding...")
compressed_data = []
reference_frame = None
gop_size = 12 # I-frame every 12 frames
for i, frame in enumerate(frames):
if i % gop_size == 0:
# Encode as I-frame
encoded_frame = encode_iframe(frame, qp=2.5)
compressed_data.append(encoded_frame)
print(f"Encoded frame {i} as I-frame")
else:
# Encode as P-frame
encoded_frame = encode_pframe(frame, reference_frame, qp=2.5)
compressed_data.append(encoded_frame)
print(f"Encoded frame {i} as P-frame")
# The reference for the next P-frame needs to be the *reconstructed* last frame
if encoded_frame['type'] == 'I':
reference_frame = decode_iframe(encoded_frame)
else:
reference_frame = decode_pframe(encoded_frame, reference_frame)
with open(compressed_file_path, 'wb') as f:
pickle.dump(compressed_data, f)
print(f"Compressed data saved to {compressed_file_path}")
# --- DECODING --- #
print("\nDecoding...")
with open(compressed_file_path, 'rb') as f:
loaded_compressed_data = pickle.load(f)
decoded_frames = []
reference_frame = None
for i, encoded_frame in enumerate(loaded_compressed_data):
if encoded_frame['type'] == 'I':
decoded_frame = decode_iframe(encoded_frame)
print(f"Decoded frame {i} (I-frame)")
else:
decoded_frame = decode_pframe(encoded_frame, reference_frame)
print(f"Decoded frame {i} (P-frame)")
decoded_frames.append(decoded_frame)
reference_frame = decoded_frame
# --- WRITING OUTPUT VIDEO --- #
h, w = decoded_frames[0].shape
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter(output_path, fourcc, 30.0, (w, h), isColor=False)
for frame in decoded_frames:
out.write(frame)
out.release()
print(f"Decoded video saved to {output_path}")
if __name__ == '__main__':
main('input.mp4', 'output.mp4', 'compressed.bin')
निकालांचे विश्लेषण आणि पुढील संशोधन
`main.py` स्क्रिप्टला `input.mp4` फाईलसह चालवल्यानंतर, तुम्हाला दोन फाइल्स मिळतील: `compressed.bin`, ज्यात आमचा कस्टम कॉम्प्रेस्ड व्हिडिओ डेटा आहे, आणि `output.mp4`, जो पुनर्रचित व्हिडिओ आहे. कॉम्प्रेशन रेशो पाहण्यासाठी `input.mp4` च्या आकाराची `compressed.bin` शी तुलना करा. गुणवत्ता पाहण्यासाठी `output.mp4` चे दृश्यात्मक निरीक्षण करा. तुम्हाला कदाचित ब्लॉकी आर्टिफॅक्ट्स दिसतील, विशेषतः उच्च `qp` मूल्यांसह, जे क्वांटायझेशनचे एक क्लासिक चिन्ह आहे.
गुणवत्तेचे मोजमाप: पीक सिग्नल-टू-नॉईज रेशो (PSNR)
पुनर्रचनेची गुणवत्ता मोजण्यासाठी एक सामान्य वस्तुनिष्ठ मेट्रिक म्हणजे PSNR. ते मूळ फ्रेमची डीकोड केलेल्या फ्रेमशी तुलना करते. सामान्यतः उच्च PSNR चांगल्या गुणवत्तेचे द्योतक असते.
import numpy as np
import math
def calculate_psnr(original, compressed):
mse = np.mean((original - compressed) ** 2)
if mse == 0:
return float('inf')
max_pixel = 255.0
psnr = 20 * math.log10(max_pixel / math.sqrt(mse))
return psnr
मर्यादा आणि पुढील पायऱ्या
आमचा साधा कोडेक एक उत्तम सुरुवात आहे, परंतु तो परिपूर्ण नाही. येथे काही मर्यादा आणि संभाव्य सुधारणा आहेत ज्या वास्तविक-जगातील कोडेक्सच्या उत्क्रांतीचे प्रतिबिंब आहेत:
- मोशन एस्टिमेशन: आमचा एक्झॉस्टिव्ह शोध मंद आणि मूलभूत आहे. वास्तविक कोडेक्स मोशन वेक्टर्स अधिक वेगाने शोधण्यासाठी अत्याधुनिक, पदानुक्रमित शोध अल्गोरिदम वापरतात.
- बी-फ्रेम्स: आम्ही फक्त पी-फ्रेम्स लागू केल्या. बी-फ्रेम्स जोडल्याने वाढलेली गुंतागुंत आणि लेटन्सीच्या बदल्यात कॉम्प्रेशन कार्यक्षमता लक्षणीयरीत्या सुधारेल.
- एन्ट्रॉपी कोडिंग: आम्ही योग्य एन्ट्रॉपी कोडिंगचा टप्पा लागू केला नाही. आम्ही फक्त पायथन डेटा स्ट्रक्चर्सना पिकल केले. क्वांटाइज्ड शून्यांसाठी रन-लेंथ एन्कोडर, त्यानंतर हफमन किंवा अरिथमॅटिक कोडर जोडल्याने फाईलचा आकार आणखी कमी होईल.
- डीब्लॉकिंग फिल्टर: आमच्या ८x८ ब्लॉक्समधील तीक्ष्ण कडांमुळे दृश्यमान आर्टिफॅक्ट्स तयार होतात. आधुनिक कोडेक्स या कडा गुळगुळीत करण्यासाठी आणि दृश्यात्मक गुणवत्ता सुधारण्यासाठी पुनर्रचनेनंतर डीब्लॉकिंग फिल्टर लावतात.
- व्हेरिएबल ब्लॉक साइझ: आधुनिक कोडेक्स फक्त निश्चित १६x१६ मॅक्रोब्लॉक्स वापरत नाहीत. ते सामग्रीशी अधिक चांगल्या प्रकारे जुळण्यासाठी फ्रेमला विविध ब्लॉक आकार आणि आकारांमध्ये अनुकूलपणे विभाजित करू शकतात (उदा. सपाट भागांसाठी मोठे ब्लॉक्स आणि तपशीलवार भागांसाठी लहान ब्लॉक्स वापरणे).
निष्कर्ष
एक व्हिडिओ कोडेक तयार करणे, जरी तो एक सरलीकृत असला तरी, एक अत्यंत समाधानकारक व्यायाम आहे. हे आपल्या डिजिटल जीवनातील एका महत्त्वपूर्ण भागाला सामर्थ्य देणाऱ्या तंत्रज्ञानाचे रहस्य उलगडते. आम्ही स्पेशियल आणि टेम्पोरल रिडंडंसीच्या मूळ संकल्पनांमधून प्रवास केला आहे, एन्कोडिंग पाइपलाइनच्या आवश्यक टप्प्यांमधून—प्रेडिक्शन, ट्रान्सफॉर्मेशन आणि क्वांटायझेशन—पायउतार झालो आहोत, आणि या कल्पना पायथनमध्ये लागू केल्या आहेत.
येथे दिलेला कोड एक प्रारंभ बिंदू आहे. मी तुम्हाला त्याच्याबरोबर प्रयोग करण्यास प्रोत्साहित करतो. ब्लॉकचा आकार, क्वांटायझेशन पॅरामीटर (`qp`), किंवा GOP लांबी बदलण्याचा प्रयत्न करा. एक साधा रन-लेंथ एन्कोडिंग योजना लागू करण्याचा प्रयत्न करा किंवा बी-फ्रेम्स जोडण्याचे आव्हान स्वीकारा. गोष्टी तयार करून आणि मोडून, आपण अनेकदा गृहीत धरलेल्या अखंड व्हिडिओ अनुभवांमागील कल्पकतेची खोल प्रशंसा कराल. व्हिडिओ कॉम्प्रेशनचे जग विशाल आणि सतत विकसित होत आहे, जे शिकण्यासाठी आणि नवनिर्मितीसाठी अनंत संधी देते.