DIY: होम मल्टीमीडिया सिस्टम के लिए यूनिवर्सल एबिलिट - एटमोसवेट

शुभ दोपहर

अपने पहले लेख के लिए, मैंने अपने सबसे सफल हस्तशिल्पों में से एक को चुना: फिलिप्स से एंबीलाइट के एचडीएमआई-पार्थस्ट्रॉग एनालॉग, अब से मैं इस रचना को "एटमोसवेट" कहूंगा।

परिचय

इंटरनेट पर अगर आप पीसी से चित्र प्रदर्शित करते हैं तो मॉनिटर / टीवी के लिए Ambilight कैसे बनाएं, इस पर तैयार / खुला समाधान और लेख खोजना बहुत मुश्किल नहीं है। लेकिन मेरे मल्टीमीडिया सिस्टम में, एक पीसी से टीवी के लिए एक तस्वीर आउटपुट करने में मेरे द्वारा उपयोग किए जाने वाले समय का केवल 5% लगता है, अधिक समय मैं गेम कंसोल से खेलता हूं, जिसका मतलब है कि मुझे अपने खुद के कुछ के साथ आना था।


स्रोत डेटा:


अधिकांश डिवाइस एचडीसीपी का उपयोग खेलते समय भी सामग्री खेलने के लिए करते हैं।
आवश्यकता:

टीवी से जुड़े सभी उपकरणों के लिए एटमोसविट के लिए केंद्रीकृत समर्थन प्रदान करना आवश्यक है।

कार्यान्वयन

मैं आपको यह नहीं बताऊंगा कि मैंने टीवी के लिए 4.5 मीटर की एलईडी पट्टी कैसे संलग्न की और Arduino के साथ क्या करने की आवश्यकता है, आप इस लेख को आधार के रूप में उपयोग कर सकते हैं।

केवल चेतावनी:
मैंने देखा कि स्क्रीन के नीचे अजीब झिलमिलाहट हैं, सबसे पहले मैंने एक गलती की, डिफ्लेयर को फिर से चुना, तस्वीर के आकार को बदल दिया और सब कुछ का एक गुच्छा खोदा, यह बेहतर हो गया, लेकिन इससे टिमटिमाने में मदद नहीं मिली। मैं अवलोकन करने लगा। यह पता चला कि झिलमिलाहट टेप के अंत में और फिर ज्वलंत दृश्यों के साथ थी। मल्टीमीटर लेते हुए, मैंने टेप की शुरुआत, मध्य और अंत में वोल्टेज को मापा और टिमटिमा के कारण का अनुमान लगाया: टेप की शुरुआत में 4.9V था (हाँ चीनी PSU एक विचलन के साथ एक वोल्टेज देता है, यह महत्वपूर्ण नहीं है। 4.5 के मध्य में अंत 4.22 पर - वोल्टेज ड्रॉप बहुत महत्वपूर्ण है , मुझे बस समस्या का समाधान करना था - टेप के बीच में मैंने पीएसयू से बिजली का सारांश दिया, तार को टीवी के पीछे जाने दिया। यह तुरंत मदद की, किसी भी झिलमिलाहट पूरी तरह से बंद कर दिया।

एक वेब कैमरा के साथ एक तस्वीर कैप्चर करना

विचार में चलने के लिए पहला परीक्षण संस्करण और इसके दृश्य को एक वेब कैमरा के माध्यम से छवि को कैप्चर करने के माध्यम से चुना गया था) यह कुछ इस तरह दिखता था:
छवि

कम रंग प्रतिपादन और उच्च विलंबता ने दिखाया कि इस कार्यान्वयन का किसी भी तरह से उपयोग नहीं किया जा सकता है।

एचडीएमआई के माध्यम से चित्रों को कैप्चर करना


संभावित विकल्पों की खोज की प्रक्रिया में, निम्नलिखित योजना को सबसे विश्वसनीय और बजट के रूप में चुना गया:


प्रारंभ में, यह जंगली और बैसाखी की तरह दिखता है, लेकिन:


एचडीएमआई कैप्चर के लिए मैंने बोर्ड का उपयोग क्यों नहीं किया? यह सरल है: सबसे सस्ता विकल्प और सस्ती है BlackmagicIntensity शटल, लेकिन यह 1080p / 60fps सिग्नल के साथ काम नहीं कर सकता, केवल 1080p / 30fps के साथ - जो स्वीकार्य नहीं है, क्योंकि मैं फ्रेम दर को कम नहीं करना चाहता था ताकि मैं तस्वीर पर कब्जा कर सकूं। + इस मामले में लगभग 10 हजार का खर्च आया। रूबल। - जो एक अज्ञात परिणाम के साथ सस्ता नहीं है।

46x26 एलईडी बैकलाइट के संकल्प में रंग कैप्चर करने के लिए एचडीएमआई के एस-वीडियो में रूपांतरण महत्वपूर्ण नहीं हैं।

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

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

मुलायम

मेरे लिए यह सबसे दिलचस्प हिस्सा था, क्योंकि लोहे के टुकड़ों के साथ मैं वास्तव में चारों ओर प्रहार करना पसंद नहीं करता।

छवि पर कब्जा करने के लिए, मैंने ओपनसीवी और विशेष रूप से इसके .NET रैपर इमगू सीवी का उपयोग किया

मैंने छवि को पोस्ट-प्रोसेसिंग और नियंत्रक को रंगों की सूची देने से पहले इसे तैयार करने के लिए कई अलग-अलग तकनीकों को लागू करने का निर्णय लिया।

फ्रेम प्रसंस्करण

1. कब्जा कर लिया फ्रेम

2. काली पट्टियों को बाहर करने के लिए फसल फ्रेम

यहाँ सब कुछ सरल है:
frame.ROI = new Rectangle(8, 8, frame.Width - 8, frame.Height - 18 - 8); 

ऊपर से 8 पिक्सेल, दाईं ओर 8 और नीचे से 18 पिक्सेल। (बाईं ओर कोई पट्टी नहीं है)

3. बैकलाइट के रिज़ॉल्यूशन में फ्रेम का आकार बदलें, हमारे साथ एक स्वस्थ तस्वीर ले जाने की कोई आवश्यकता नहीं है

कुछ भी जटिल नहीं है, हम इसे OpenCV टूल के साथ करते हैं:
फ्रेम। आकार (लेडीडथ - 2 * लेडसाइडओवरएड,
लेडहाइट - लेडबॉटोमओवरएड - लेडोटोपावरएडगे,
INTER.CV_INTER_LINEAR);
एक चौकस पाठक चर की बहुतायत को नोटिस करेगा। तथ्य यह है कि मेरा टीवी फ्रेम काफी बड़ा है, पक्षों पर 1 एलईडी, शीर्ष पर 1 और नीचे 3 पर कब्जा कर रहा है, इसलिए आकार का प्रदर्शन सीधे विपरीत एलईडी पर किया जाता है, और हम बाद में कोनों को पूरक करते हैं। आकार देते समय, हमें बस औसत रंग मिलते हैं जो कि एलइडी के पिक्सल में होने चाहिए।

4. हम कट फ्रेम से एलईडी की मैपिंग करते हैं

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

5. रंग सुधार (सफेद संतुलन / रंग संतुलन) करें

टीवी के पीछे की दीवारें बीम से हैं, बीम पीले रंग की है, इसलिए आपको पीलेपन की भरपाई करने की आवश्यकता है।
var blue = 255.0f / (255.0f + blueLevelFloat) * pixelBuffer [k];
var हरा = 255.0f / (255.0f + greenLevelFloat) * पिक्सेलबफ़र [k + 1];
var लाल = 255.0f / (255.0f + redLevelFloat) * पिक्सेलबफ़र [k + 2];
वास्तव में, मैंने शुरू में कुछ ओपन सोर्स एडिटर के स्रोत से रंग संतुलन लिया था, लेकिन यह सफेद नहीं हुआ (सफेद बने रहे), मैंने सूत्रों को थोड़ा बदल दिया, सील कर दिया, और मुझे वह मिला जिसकी मुझे सीधे जरूरत है: यदि रंग घटक का स्तर नकारात्मक है (मैं यह पता लगाऊंगा कि - यह रंग पर्याप्त नहीं है), फिर हम इसकी तीव्रता और इसके विपरीत जोड़ते हैं। मेरी दीवारों के लिए, इसने काम किया: RGB (-30.5.85)।

रंग सुधार में, मैं ब्लैक लेवलिंग भी करता हूं (आरजीबी में 13.13.13 के आसपास कहीं काला आता है) बस प्रत्येक घटक के साथ 13 घटाकर।

6. प्रदर्शन में कमी (छवि संतृप्ति में कमी)

अंतिम सेटअप में, मैं desaturation का उपयोग नहीं करता हूं, लेकिन कुछ बिंदु पर इसकी आवश्यकता हो सकती है, वास्तव में यह फिलिप्स अंबिलिट की तरह रंगों को अधिक "पेस्टल" बनाता है। मैं कोड नहीं दूंगा, हम बस RGB -> HSL से कन्वर्ट करते हैं, संतृप्ति घटक (संतृप्ति) को कम करते हैं और वापस RGB में लौटते हैं।

7. डिफ्लिकर

तो यह पता चला है कि इनपुट छवि "हिल रही है" - यह एनालॉग सिग्नल में रूपांतरण का एक परिणाम है, जैसा कि मेरा मानना ​​है। सबसे पहले मैंने अपने तरीके से खुद को तय करने की कोशिश की, फिर मैंने वर्चुअलडब में इस्तेमाल किए गए डिफ्लिकर फिल्टर के स्रोत को देखा, इसे C # में लिखा (यह C ++ में था), मैंने महसूस किया कि यह काम नहीं करता है, क्योंकि यह इतना प्रभावित होता है कि यह फ्रेम के बीच झिलमिलाहट से लड़ता है। अंत में, मैंने अपने निर्णय और इस डिफ्लेयर को मिला दिया, कुछ अजीब हो रहा था, लेकिन उम्मीद से बेहतर काम कर रहा था। प्रारंभिक डिफ्लेयर ने पूरे फ्रेम की तीव्रता के साथ ही काम किया, मुझे अलग से प्रत्येक एलईडी की आवश्यकता है। प्रारंभिक डिफ्लेक्टर ने एक राशि के रूप में तीव्रता में परिवर्तन की तुलना की, मुझे कलर वेक्टर की लंबाई की तुलना अधिक पसंद है। प्रारंभिक डिफ्लेक्टर ने पिछले फ्रेम की तुलना में तीव्रता में परिवर्तन के डेल्टा की तुलना की, यह फिट नहीं होता है, और मैंने इसे पिछले फ्रेम की खिड़की के भीतर तीव्रता के औसत मूल्य में बदल दिया। और कई अन्य छोटी चीजें, जिसके परिणामस्वरूप प्रारंभिक डिफ्लेयर के बहुत कम बचा है।
मुख्य विचार: पिछले फ्रेम की औसत तीव्रता से आगे बढ़ना, वर्तमान फ्रेम को संशोधित करना अगर इसकी तीव्रता एक निश्चित सीमा से अधिक नहीं है (मेरे पास 25 की अंतिम सेटअप में यह सीमा है), यदि सीमा पार हो जाती है, तो खिड़की बिना संशोधन के रीसेट हो जाती है।
मेरे विक्षेपक का थोड़ा संशोधित (संदर्भ से बाहर पठनीयता के लिए) कोड:
 Array.Copy(_leds, _ledsOld, _leds.Length); for (var i = 0; i < _leds.Length; i++) { double lumSum = 0; // Calculate the luminance of the current led. lumSum += _leds[i].R*_leds[i].R; lumSum += _leds[i].G*_leds[i].G; lumSum += _leds[i].B*_leds[i].B; lumSum = Math.Sqrt(lumSum); // Do led processing var avgLum = 0.0; for (var j = 0; j < LedLumWindow; j++) { avgLum += _lumData[j, i]; } var avg = avgLum/LedLumWindow; var ledChange = false; if (_strengthcutoff < 256 && _lumData[0, i] != 256 && Math.Abs((int) lumSum - avg) >= _strengthcutoff) { _lumData[0, i] = 256; ledChange = true; } // Calculate the adjustment factor for the current led. var scale = 1.0; int r, g, b; if (ledChange) { for (var j = 0; j < LedLumWindow; j++) { _lumData[j, i] = (int) lumSum; } } else { for (var j = 0; j < LedLumWindow - 1; j++) { _lumData[j, i] = _lumData[j + 1, i]; } _lumData[LedLumWindow - 1, i] = (int) lumSum; if (lumSum > 0) { scale = 1.0f/((avg+lumSum)/2); var filt = 0.0f; for (var j = 0; j < LedLumWindow; j++) { filt += (float) _lumData[j, i]/LedLumWindow; } scale *= filt; } // Adjust the current Led. r = _leds[i].R; g = _leds[i].G; b = _leds[i].B; // save source values var sr = r; var sg = g; var sb = b; var max = r; if (g > max) max = g; if (b > max) max = b; double s; if (scale*max > 255) s = 255.0/max; else s = scale; r = (int) (s*r); g = (int) (s*g); b = (int) (s*b); // keep highlight double k; if (sr > _lv) { k = (sr - _lv)/(double) (255 - _lv); r = (int) ((k*sr) + ((1.0 - k)*r)); } if (sg > _lv) { k = (sg - _lv)/(double) (255 - _lv); g = (int) ((k*sg) + ((1.0 - k)*g)); } if (sb > _lv) { k = (sb - _lv)/(double) (255 - _lv); b = (int) ((k*sb) + ((1.0 - k)*b)); } _leds[i] = Color.FromArgb(r, g, b); } /* Temporal softening phase. */ if (ledChange || _softening == 0) continue; var diffR = Math.Abs(_leds[i].R - _ledsOld[i].R); var diffG = Math.Abs(_leds[i].G - _ledsOld[i].G); var diffB = Math.Abs(_leds[i].B - _ledsOld[i].B); r = _leds[i].R; g = _leds[i].G; b = _leds[i].B; int sum; if (diffR < _softening) { if (diffR > (_softening >> 1)) { sum = _leds[i].R + _leds[i].R + _ledsOld[i].R; r = sum/3; } } if (diffG < _softening) { if (diffG > (_softening >> 1)) { sum = _leds[i].G + _leds[i].G + _ledsOld[i].G; g = sum/3; } } if (diffB < _softening) { if (diffB > (_softening >> 1)) { sum = _leds[i].B + _leds[i].B + _ledsOld[i].B; b = sum/3; } } _leds[i] = Color.FromArgb(r, g, b); } 

चलो _leds रंग वर्ग के एल ई डी की एक सरणी हो, _ledsOld रूपांतरण से पहले फ्रेम मान हो, LedLumWindow पिछले फ्रेम की खिड़की की चौड़ाई हो, तीव्रता में औसत परिवर्तन का अनुमान लगाने के लिए, अंतिम सेटअप में मेरे पास 100 की विंडो थी, जो लगभग 30fps पर 3 सेकंड के बराबर होती है। _lumData - पिछले फ़्रेमों की तीव्रता मान की एक सरणी।

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

8. पड़ोसियों में एलईडी को चिकना करना।

सामान्य तौर पर, अंतिम सेटअप में, मुझे वास्तव में चौरसाई पसंद नहीं थी, और मैंने इसे बंद कर दिया, लेकिन कुछ मामलों में यह काम में आ सकता है। यहाँ हम अपने पड़ोसी के ऊपर प्रत्येक एलईडी के रंग को औसत करते हैं।
 var smothDiameter = 2*_smoothRadius + 1; Array.Copy(_leds, _ledsOld, _leds.Length); for (var i = 0; i < _ledsOld.Length; i++) { var r = 0; var g = 0; var b = 0; for (var rad = -_smoothRadius; rad <= _smoothRadius; rad++) { var pos = i + rad; if (pos < 0) { pos = _ledsOld.Length + pos; } else if (pos > _ledsOld.Length - 1) { pos = pos - _ledsOld.Length; } r += _ledsOld[pos].R; g += _ledsOld[pos].G; b += _ledsOld[pos].B; } _leds[i] = Color.FromArgb(r/smothDiameter, g/smothDiameter, b/smothDiameter); } 


9. वर्तमान स्थिति को बचाएं ताकि पैकेट थ्रेड कब्रों को भेजने और बैकलाइट नियंत्रक को भेजता है।

मैंने जानबूझकर प्रोसेसिंग फ्रेम और पैकेट भेजने की प्रक्रिया को कंट्रोलर में विभाजित किया है: पैकेट एक निश्चित अंतराल (एक बार 40 मी) में एक बार भेजे जाते हैं, ताकि Arduino पिछले एक को प्रोसेस कर सके, क्योंकि 30ms से अधिक यह चोक हो जाता है, इसलिए यह पता चलता है कि हम सीधे फ्रेम रेट पर निर्भर नहीं हैं। कब्जा करें और उस प्रक्रिया में हस्तक्षेप न करें (लेकिन एक पैकेट भेजने में भी समय लगता है)।

Arduino के बारे में थोड़ा सा

आप श्रृंखला के लिए Arduino के लिए एक उचित पैकेज नहीं ले सकते और भेज सकते हैं, क्योंकि यह डिफ़ॉल्ट HardwareSerial बफर से आगे जाएगा और आप इसका अंत खो देंगे।
यह काफी सरल रूप से हल किया गया है: हमने हार्डवेयरसरीयर बफर के आकार को काफी बड़ा किया है ताकि रंगों के एक सरणी के साथ पूरे भेजे गए पैकेट को फिट किया जा सके, मेरे लिए यह 410 है।

यूआई

सॉफ़्टवेयर को एक जीत सेवा के रूप में लागू किया गया था, सभी मापदंडों को कॉन्फ़िगर करने के लिए + सक्षम / अक्षम करने के लिए, मैंने एक वेब यूआई बनाया जिसने सेवा पर वेबसर्विस के माध्यम से सेवा से संपर्क किया। मोबाइल स्क्रीन पर अंतिम इंटरफ़ेस इस तरह दिखता है:
छवि

अब मैं HTCP से जुड़े विंडोज के लिए Kinect के माध्यम से आवाज नियंत्रण पेंच करने की योजना बना रहा हूं।

परिणाम

नतीजतन, परिणाम सभी अपेक्षाओं को पूरा करता था, और अब कंसोल पर गेम खेलने से मुझे खेल के वातावरण में और भी अधिक विसर्जन मिलता है।

कार्य के सामान्य परिणाम के रूप में, मैंने अपनी योजना के अनुसार वातावरण के काम के साथ एक वीडियो रिकॉर्ड किया:

टेस्ट सैंपल 1: प्रशांत रिम, शंघाई में लड़ाई का दृश्य, यह फिल्म परीक्षण और प्रदर्शन, कई ज्वलंत दृश्यों और चमक, बिजली के हमलों, आदि के लिए अच्छी तरह से अनुकूल है।



टेस्ट सैंपल 2: YouTube से मर्ज किए गए MLP के कुछ प्रकार के वीडियो चमकीले रंगों के साथ दृश्यों के परीक्षण के लिए बहुत उपयुक्त हैं (मुझे पट्टियाँ पसंद हैं), साथ ही साथ जल्दी से बदलते दृश्य (वीडियो के अंत में, आप केवल वीडियो पर दिखाई देने वाली देरी के परिणाम देख सकते हैं, जब यह देखने का वास्तविक ध्यान देने योग्य नहीं है, मैंने वीडियो द्वारा देरी को मापने की कोशिश की - यह 10-20 सेमी निकला):



और अंत में, यह HTPC से संसाधन खपत के बारे में ध्यान देने योग्य है:
HTPC I में 3 पर ASRock Vision 3D है, वायुमंडल सेवा 5-10% CPU और 32MB RAM खाती है।

आपका ध्यान देने के लिए धन्यवाद, मुझे वास्तव में उम्मीद है कि मेरा लेख किसी की मदद करेगा।

Source: https://habr.com/ru/post/In222925/


All Articles