क्यूटी और मोबाइल कैमरा। भाग 2, मीगो

नमस्कार, खभ्रवर्णों!

पिछले लेख में, मैंने सिम्बियन उपकरणों पर कैमरे के लिए एक सुविधाजनक क्यूटी व्यूफ़ाइंडर बनाने के बारे में बात की थी। अब यह हमारे कोड को अधिक सार्वभौमिक बनाने और Meego 1.2 के साथ संगतता जोड़ने का समय है, जो कि, जैसा कि यह निकला, सबसे तुच्छ कार्य नहीं है।

मुझे संक्षेप में याद होगा कि हमने पिछले भाग में प्राप्त किया था:
अब हम केवल नोकिया N9 (N900, N950) के लिए इस विजेट को आवेदन में क्यों नहीं जोड़ सकते हैं। यह कैमरा द्वारा लौटाए गए छवि प्रारूप के बारे में है - UYVY। जब आप हमारा व्यूफ़ाइंडर शुरू करते हैं, तो हम सभी को डिबग लॉग में गलत प्रारूप के बारे में एक संदेश मिलता है। UYVY क्या है, मुझे नहीं पता, लेकिन, सौभाग्य से, विदेशी मैमो / मीगो उपयोगकर्ताओं की मदद के बिना, मैं इस समस्या का हल खोजने में कामयाब रहा। आगे मैं नए संशोधनों के साथ पहले लेख से केवल कोड स्निपेट दूंगा।

समाधान UYVY को अधिक परिचित और, सबसे महत्वपूर्ण, क्यूटी-अनुकूल आरजीबी प्रारूप में परिवर्तित करना है। अजीब तरह से पर्याप्त है, जो कोड हम भविष्य में उपयोग करेंगे वह QtMobility में शामिल है और सूत्रों में पाया जा सकता है (लेख के पाद लेख में लिंक)। UYVY-> RGB16 के कार्यान्वयन के लिए कार्य योजना निम्नानुसार है:
  1. छवि तक पहुंच प्राप्त करने के लिए हमारे दृश्यदर्शी में काल्पनिक यूवाईवाई समर्थन जोड़ें, और रीडिंग लॉग को बर्बाद करने के लिए नहीं
  2. हम फ़्रेम हैंडलर वर्ग में रूपांतरण फ़ंक्शन को लागू करते हैं
  3. हम अपने नए फ़ंक्शन के जोड़ के साथ QAbstractVideoSurface :: present () विधि को संशोधित करते हैं
सबसे पहले, हमें सिस्टम को यह बताने की आवश्यकता है कि UYVY एक वैध प्रारूप है। ऐसा करने के लिए, मूल्य जोड़ें QVideoFrame :: Format_UYVY, QAbstractVideoSurface :: supportPixelFormats () विधि द्वारा लौटाए गए प्रारूपों की सूची में:

>> myvideosurface.cpp
QList<QVideoFrame::PixelFormat> myVideoSurface::supportedPixelFormats( QAbstractVideoBuffer::HandleType handleType) const { if (handleType == QAbstractVideoBuffer::NoHandle) { return QList<QVideoFrame::PixelFormat>() << QVideoFrame::Format_RGB32 << QVideoFrame::Format_ARGB32 << QVideoFrame::Format_ARGB32_Premultiplied << QVideoFrame::Format_RGB565 << QVideoFrame::Format_RGB555 << QVideoFrame::Format_UYVY; //  } else { return QList<QVideoFrame::PixelFormat>(); } } 

इसके अलावा, सबसे महत्वपूर्ण हिस्सा, जिसके कारण यह पूरा लेख दिखाई दिया, UYVY-> RGB16 रूपांतरण है। ऐसा करने के लिए, ARM \ Neon ग्राफिक्स प्रोसेसर के लिए अनुकूलित लाइन रूपांतरण फ़ंक्शन का उपयोग करें। जैसा कि मैंने ऊपर लिखा था, आप इसे QtMobility (\ src \ मल्टीमीडिया \ qgraphicsvideoitem_maemo5.cpp) के स्रोत कोड में पा सकते हैं। हालाँकि, लोगों को इस फ़ंक्शन को उनके आरामदायक कोड में स्थानांतरित करने में समस्या है (स्वीकार करने के लिए, मैं केवल दूसरी बार सफल हुआ), इसलिए मैं पूरी तरह से कोड दे दूंगा। यह इस एएसएम स्निपेट के अर्थ में तल्लीन करने के लिए पूरी तरह से वैकल्पिक है (कार्य के भाग के रूप में हल हो रहा है)। मुझे खुद नहीं पता है कि इसमें क्या हो रहा है (टिप्पणी लाइनों के अलावा), इसलिए बस इस टुकड़े को अपने आप को कॉपी-पेस्ट करें। आप myVideoSurface वर्ग के तरीकों को लागू करने से पहले इसे myvideosurface.cpp में जोड़ सकते हैं।
  #include <stdint.h> #ifdef __ARM_NEON__ /* * ARM NEON optimized implementation of UYVY -> RGB16 convertor */ static void uyvy422_to_rgb16_line_neon (uint8_t * dst, const uint8_t * src, int n) { /* and this is the NEON code itself */ static __attribute__ ((aligned (16))) uint16_t acc_r[8] = { 22840, 22840, 22840, 22840, 22840, 22840, 22840, 22840, }; static __attribute__ ((aligned (16))) uint16_t acc_g[8] = { 17312, 17312, 17312, 17312, 17312, 17312, 17312, 17312, }; static __attribute__ ((aligned (16))) uint16_t acc_b[8] = { 28832, 28832, 28832, 28832, 28832, 28832, 28832, 28832, }; /* * Registers: * q0, q1 : d0, d1, d2, d3 - are used for initial loading of YUV data * q2 : d4, d5 - are used for storing converted RGB data * q3 : d6, d7 - are used for temporary storage * * q6 : d12, d13 - are used for converting to RGB16 * q7 : d14, d15 - are used for storing RGB16 data * q4-q5 - reserved * * q8, q9 : d16, d17, d18, d19 - are used for expanded Y data * q10 : d20, d21 * q11 : d22, d23 * q12 : d24, d25 * q13 : d26, d27 * q13, q14, q15 - various constants (#16, #149, #204, #50, #104, #154) */ asm volatile (".macro convert_macroblock size\n" /* load up to 16 source pixels in UYVY format */ ".if \\size == 16\n" "pld [%[src], #128]\n" "vld1.32 {d0, d1, d2, d3}, [%[src]]!\n" ".elseif \\size == 8\n" "vld1.32 {d0, d1}, [%[src]]!\n" ".elseif \\size == 4\n" "vld1.32 {d0}, [%[src]]!\n" ".elseif \\size == 2\n" "vld1.32 {d0[0]}, [%[src]]!\n" ".else\n" ".error \"unsupported macroblock size\"\n" ".endif\n" /* convert from 'packed' to 'planar' representation */ "vuzp.8 d0, d1\n" /* d1 - separated Y data (first 8 bytes) */ "vuzp.8 d2, d3\n" /* d3 - separated Y data (next 8 bytes) */ "vuzp.8 d0, d2\n" /* d0 - separated U data, d2 - separated V data */ /* split even and odd Y color components */ "vuzp.8 d1, d3\n" /* d1 - evenY, d3 - oddY */ /* clip upper and lower boundaries */ "vqadd.u8 q0, q0, q4\n" "vqadd.u8 q1, q1, q4\n" "vqsub.u8 q0, q0, q5\n" "vqsub.u8 q1, q1, q5\n" "vshr.u8 d4, d2, #1\n" /* d4 = V >> 1 */ "vmull.u8 q8, d1, d27\n" /* q8 = evenY * 149 */ "vmull.u8 q9, d3, d27\n" /* q9 = oddY * 149 */ "vld1.16 {d20, d21}, [%[acc_r], :128]\n" /* q10 - initialize accumulator for red */ "vsubw.u8 q10, q10, d4\n" /* red acc -= (V >> 1) */ "vmlsl.u8 q10, d2, d28\n" /* red acc -= V * 204 */ "vld1.16 {d22, d23}, [%[acc_g], :128]\n" /* q11 - initialize accumulator for green */ "vmlsl.u8 q11, d2, d30\n" /* green acc -= V * 104 */ "vmlsl.u8 q11, d0, d29\n" /* green acc -= U * 50 */ "vld1.16 {d24, d25}, [%[acc_b], :128]\n" /* q12 - initialize accumulator for blue */ "vmlsl.u8 q12, d0, d30\n" /* blue acc -= U * 104 */ "vmlsl.u8 q12, d0, d31\n" /* blue acc -= U * 154 */ "vhsub.s16 q3, q8, q10\n" /* calculate even red components */ "vhsub.s16 q10, q9, q10\n" /* calculate odd red components */ "vqshrun.s16 d0, q3, #6\n" /* right shift, narrow and saturate even red components */ "vqshrun.s16 d3, q10, #6\n" /* right shift, narrow and saturate odd red components */ "vhadd.s16 q3, q8, q11\n" /* calculate even green components */ "vhadd.s16 q11, q9, q11\n" /* calculate odd green components */ "vqshrun.s16 d1, q3, #6\n" /* right shift, narrow and saturate even green components */ "vqshrun.s16 d4, q11, #6\n" /* right shift, narrow and saturate odd green components */ "vhsub.s16 q3, q8, q12\n" /* calculate even blue components */ "vhsub.s16 q12, q9, q12\n" /* calculate odd blue components */ "vqshrun.s16 d2, q3, #6\n" /* right shift, narrow and saturate even blue components */ "vqshrun.s16 d5, q12, #6\n" /* right shift, narrow and saturate odd blue components */ "vzip.8 d0, d3\n" /* join even and odd red components */ "vzip.8 d1, d4\n" /* join even and odd green components */ "vzip.8 d2, d5\n" /* join even and odd blue components */ "vshll.u8 q7, d0, #8\n" //red "vshll.u8 q6, d1, #8\n" //greed "vsri.u16 q7, q6, #5\n" "vshll.u8 q6, d2, #8\n" //blue "vsri.u16 q7, q6, #11\n" //now there is rgb16 in q7 ".if \\size == 16\n" "vst1.16 {d14, d15}, [%[dst]]!\n" //"vst3.8 {d0, d1, d2}, [%[dst]]!\n" "vshll.u8 q7, d3, #8\n" //red "vshll.u8 q6, d4, #8\n" //greed "vsri.u16 q7, q6, #5\n" "vshll.u8 q6, d5, #8\n" //blue "vsri.u16 q7, q6, #11\n" //now there is rgb16 in q7 //"vst3.8 {d3, d4, d5}, [%[dst]]!\n" "vst1.16 {d14, d15}, [%[dst]]!\n" ".elseif \\size == 8\n" "vst1.16 {d14, d15}, [%[dst]]!\n" //"vst3.8 {d0, d1, d2}, [%[dst]]!\n" ".elseif \\size == 4\n" "vst1.8 {d14}, [%[dst]]!\n" ".elseif \\size == 2\n" "vst1.8 {d14[0]}, [%[dst]]!\n" "vst1.8 {d14[1]}, [%[dst]]!\n" ".else\n" ".error \"unsupported macroblock size\"\n" ".endif\n" ".endm\n" "vmov.u8 d8, #15\n" /* add this to U/V to saturate upper boundary */ "vmov.u8 d9, #20\n" /* add this to Y to saturate upper boundary */ "vmov.u8 d10, #31\n" /* sub this from U/V to saturate lower boundary */ "vmov.u8 d11, #36\n" /* sub this from Y to saturate lower boundary */ "vmov.u8 d26, #16\n" "vmov.u8 d27, #149\n" "vmov.u8 d28, #204\n" "vmov.u8 d29, #50\n" "vmov.u8 d30, #104\n" "vmov.u8 d31, #154\n" "subs %[n], %[n], #16\n" "blt 2f\n" "1:\n" "convert_macroblock 16\n" "subs %[n], %[n], #16\n" "bge 1b\n" "2:\n" "tst %[n], #8\n" "beq 3f\n" "convert_macroblock 8\n" "3:\n" "tst %[n], #4\n" "beq 4f\n" "convert_macroblock 4\n" "4:\n" "tst %[n], #2\n" "beq 5f\n" "convert_macroblock 2\n" "5:\n" ".purgem convert_macroblock\n":[src] "+&r" (src),[dst] "+&r" (dst), [n] "+&r" (n) :[acc_r] "r" (&acc_r[0]),[acc_g] "r" (&acc_g[0]),[acc_b] "r" (&acc_b[0]) :"cc", "memory", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31"); } #endif 

ऐसे सरल तरीके से, आप UYVY सोलह-बिट आरजीबी की एक पंक्ति बना सकते हैं। अब यह इस रूपांतरण को हमारे फ्रेम हैंडलर में लागू करना बाकी है। मैंने एक अलग विधि myVideoSurface :: ConvertFrame () में सभी रूपांतरण निकाले। यह इस तरह दिखता है:

>> myvideosurface.cpp
  QPixmap myVideoSurface::convertFrame(QVideoFrame lastVideoFrame) { QPixmap lastFrame = QPixmap(); if (!lastVideoFrame.isValid()){ return QPixmap(); } if (lastVideoFrame.map(QAbstractVideoBuffer::ReadOnly)) { #ifdef __ARM_NEON__ if (lastVideoFrame.pixelFormat() == QVideoFrame::Format_UYVY) { QImage lastImage(lastVideoFrame.size(), QImage::Format_RGB16); const uchar *src = lastVideoFrame.bits(); uchar *dst = lastImage.bits(); const int srcLineStep = lastVideoFrame.bytesPerLine(); const int dstLineStep = lastImage.bytesPerLine(); const int h = lastVideoFrame.height(); const int w = lastVideoFrame.width(); /** *   itself */ for (int y=0; y<h; y++) { uyvy422_to_rgb16_line_neon(dst, src, w); src += srcLineStep; dst += dstLineStep; } lastFrame = QPixmap::fromImage( lastImage.scaled(lastVideoFrame.size(), Qt::IgnoreAspectRatio, Qt::FastTransformation)); } else #endif { QImage::Format imgFormat = QVideoFrame::imageFormatFromPixelFormat(lastVideoFrame.pixelFormat()); if (imgFormat != QImage::Format_Invalid) { QImage lastImage(lastVideoFrame.bits(), lastVideoFrame.width(), lastVideoFrame.height(), lastVideoFrame.bytesPerLine(), imgFormat); lastFrame = QPixmap::fromImage( lastImage.scaled(lastVideoFrame.size(), Qt::IgnoreAspectRatio, Qt::FastTransformation)); } } lastVideoFrame.unmap(); return lastFrame; } } 

अंतिम स्पर्श - हम विधि को संशोधित करते हैं myVideoSurface :: present (), नवाचारों को ध्यान में रखते हुए:

>> myvideosurface.cpp
 bool myVideoSurface::present(const QVideoFrame &frame){ m_frame = frame; if(surfaceFormat().pixelFormat() != m_frame.pixelFormat() || surfaceFormat().frameSize() != m_frame.size()) { stop(); return false; } else { observer->newImage(convertFrame(frame)); return true; } } 

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

सामान्य तौर पर, इमेज प्रोसेसिंग का यह सिद्धांत समान QtMobility में उपयोग किया जाता है, जिसके लिए हम इसे स्वयं लागू करते हैं, एकमात्र प्रश्न प्लेटफ़ॉर्म में है (जैसा कि आप स्रोतों से देख सकते हैं, गतिशीलता के अंदर इस विधि का उपयोग Maemo 5 में किया जाता है)। इसलिए यह आशा बनी हुई है कि भविष्य में मीगो के लिए गतिशीलता कोड में इस तरह के रूपांतरण को लागू किया जाएगा।

PS वैसे, इस कोड को लिखने के समय, Qt (4.7.2) में RGB16 को JPEG को बचाने के लिए अभी भी कोई समर्थन नहीं था (हालांकि यह बगट्रैक पर इंगित किया गया था कि बग को ठीक किया गया था)। इसलिए, इस तरह की बचत से पहले कॉलबैक में, मुझे एक और रूपांतरण चरण लागू करना पड़ा - RGB16-> RGB888। यह एक पंक्ति में किया जाता है:
 image = image.convertToFormat(QImage::Format_RGB888); 
यहाँ, इमेज QImage ऑब्जेक्ट है जो newImage () विधि के लिए आया था। यह प्रक्रिया भी गति को प्रभावित नहीं करती है, इसलिए वास्तविक समय में स्क्रीन पर छवियों को प्रदर्शित करते समय यह काफी लागू होता है।

एक अच्छा रूपांतरण है!

संदर्भ:

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


All Articles