तरंग का उपयोग कर छवि संपीड़न

वेवलेट संपीड़न एक एन्कोडिंग छवि के दो-आयामी तरंगिका अपघटन का उपयोग करके छवि एन्कोडिंग विधियों के एक वर्ग का सामान्य नाम है। आमतौर पर गुणवत्ता के नुकसान के साथ संपीड़न का मतलब है। लेख में जटिल गणितीय सूत्र नहीं होंगे, लेख के नीचे दिए गए लिंक से पूरे सिद्धांत को पढ़ा जा सकता है। केवल यहाँ अभ्यास!

जेपीईजी अंतर


जेपीईजी एल्गोरिथ्म, वेवलेट एक के विपरीत, आकार 8 की मूल छवि के प्रत्येक ब्लॉक को 8 पिक्सल से अलग-अलग संपीड़ित करता है। नतीजतन, उच्च संपीड़न अनुपात में, एक ब्लॉक संरचना पुनर्निर्मित छवि में ध्यान देने योग्य हो सकती है। तरंगिका संपीड़न के साथ, ऐसी समस्या उत्पन्न नहीं होती है, लेकिन एक अन्य प्रकार की विकृतियां दिखाई दे सकती हैं, जिसमें तेज सीमाओं के पास "भूतिया" लहर का रूप होता है।
यह माना जाता है कि ऐसी कलाकृतियों, औसतन, जेपीईजी द्वारा निर्मित "वर्गों" की तुलना में पर्यवेक्षक के लिए कम ध्यान देने योग्य हैं।

उदाहरण


उदाहरण के लिए, समान छवि को लगभग समान आकार में दृढ़ता से संपीड़ित करें:

JPEG का उपयोग करने की शुरुआत में:
7959 बाइट्स
(7959 बाइट्स)

तब JPEG 2000 तरंगिका संपीड़न एल्गोरिथ्म:
7813 बाइट्स
(7813 बाइट्स)

इन उदाहरणों में, आप तुरंत दोनों एल्गोरिदम द्वारा शुरू की गई विकृति की प्रकृति देख सकते हैं। एक तरंगिका का उपयोग करके एक संकुचित छवि स्पष्ट रूप से बेहतर दिखती है।

एक और छवि के संपीड़न का एक और उदाहरण, फ़ाइल का आकार 3kb प्रत्येक तक कम करना:

जेपीईजी
(JPEG)

जेपी २०००
(जेपीईजी 2000)

यह क्या है


मैंने जिस विधि को लागू किया है वह अनिवार्य रूप से प्रसिद्ध जेपीईजी 2000 तरंगिका संपीड़न एल्गोरिदम का एक एनालॉग है, जिसने कभी भी लोकप्रिय जेपीईजी को प्रतिस्थापित नहीं किया है। कुछ साल पहले मैंने इसे VisualBasic6.0 में धार्मिक बनाया, लेकिन विशेष रूप से स्रोत कोड को समझने के लिए, मैंने इसे दर्शकों के अधिकांश भाग के लिए C # में फिर से लिखा।
प्रस्तुत कार्यान्वयन में, मैंने जेपीईजी 2000 छवि संपीड़न एल्गोरिथ्म में उपयोग किए गए असतत बायोरिथोगोनल सीडीएफ 9/7 तरंगिका का तेजी से उठाने का उपयोग किया। सी कार्यान्वयन यहां डाउनलोड किया जा सकता है

यहाँ प्रस्तुत कोड की सीमाएँ:


कोड को न बढ़ाने के लिए, मैंने किसी भी आकार और किसी भी पहलू अनुपात की छवियों को संपीड़ित करने की क्षमता को स्थानांतरित नहीं किया। इसलिए, यह केवल 256x256, 512x512, 1024x1024, 2048x2548 के साइड साइज वाली स्क्वायर इमेज को कंप्रेस कर सकता है। मैंने संपीड़न में उपयोग किए गए गुणांकों के साथ हेडर फ़ाइल को मेमोरी उपयोग और सहेजने / पढ़ने के अनुकूलन को भी हटा दिया। अन्यथा, कोड फिर से अनुचित रूप से फूला हुआ होगा।

मुख्य संपीड़न परिदृश्य:

  1. हम फ़ाइल से छवि को लोड करते हैं;
  2. डाउनलोड की गई छवि को RGB मानों के बाइट सरणी में परिवर्तित करें;
  3. परिणामी रंग घटकों के परिमाणीकरण के साथ आरजीबी को YCrCb में फिर से दर्ज करें;
  4. एक वेवलेट लागू करें;
  5. हम एक बहुआयामी सरणी का एक आयामी (सपाट) एक में अनुवाद करते हैं;
  6. किसी भी उपलब्ध साधन (उदाहरण के लिए, GZip) द्वारा परिणामी धारा को संपीड़ित करें।
प्रस्तुत कोड में संपीड़न के लिए गुणांक का चयन आकार में न्यूनतम आउटपुट फ़ाइल प्राप्त करने के लिए किया जाता है, लेकिन कुछ देखने की क्षमता के साथ।

कंप्रेसर कोड (C #)


मैं C # में एक ऐस नहीं हूं, इसलिए हो सकता है कि कोड में ऐसे स्थान हों जहां आप मानक .Net विधियों का उपयोग कर सकें।

using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices; using System.IO.Compression; using System.IO; namespace WaveleteCompression { class wvCompress { //  public const int WV_LEFT_TO_RIGHT = 0; public const int WV_TOP_TO_BOTTOM = 1; public byte[] run(string path) { //     Bitmap bmp = new Bitmap(path, true); //       byte[, ,] b = this.BmpToBytes_Unsafe(bmp); //   byte[] o = this.Compress(b, bmp.Width, bmp.Height); //   RAW  - FileStream f = new System.IO.FileStream(path + ".raw", FileMode.Create, FileAccess.Write); f.Write(o, 0, o.Length); f.Close(); //     Gzip-     //         GZIP,        2   string outGZ = path + ".gz"; FileStream outfile = new FileStream(outGZ, FileMode.Create); GZipStream compressedzipStream = new GZipStream(outfile, CompressionMode.Compress, true); compressedzipStream.Write(o, 0, o.Length); compressedzipStream.Close(); //   GZip-  return o; } private byte[] Compress(byte[, ,] rgb, int cW, int cH) { // ,     int[] dwDiv = { 48, 32, 16, 16, 24, 24, 1, 1 }; int[] dwTop = { 24, 32, 24, 24, 24, 24, 32, 32 }; int SamplerDiv = 2, SamplerTop = 2; //   Y, cr, cb   int YPerec = 100, crPerec = 85, cbPerec = 85; int WVCount = 6; //     //  RGB  YCrCb double[, ,] YCrCb = YCrCbEncode(rgb, cW, cH, YPerec, crPerec, cbPerec, cW, cH); //         for (int z = 0; z < 3; z++) { //       for (int dWave = 0; dWave < WVCount; dWave++) { int waveW = Convert.ToInt32(cW / Math.Pow(2, dWave)); int waveH = Convert.ToInt32(cH / Math.Pow(2, dWave)); if (z == 2) { //    Y    , // ..      ( ),        YCrCb = WaveletePack(YCrCb, z, waveW, waveH, dwDiv[dWave], dwTop[dWave], dWave); } else { YCrCb = WaveletePack(YCrCb, z, waveW, waveH, dwDiv[dWave] * SamplerDiv, dwTop[dWave] * SamplerTop, dWave); } } } //     byte[] flattened = doPack(YCrCb, cW, cH, WVCount); return flattened; } /*     Double    Byte            .    Double    Short.  ,         ,         255 */ private byte[] doPack(double[, ,] ImgData, int cW, int cH, int wDepth) { short Value; int lPos = 0; int size = cW * cH * 3; //   short  int intCount = 0; short[] shorts = new short[size]; byte[] Ret = new byte[size]; //     - for(int d = wDepth-1; d >= 0; d--) { int wSize = (int)Math.Pow(2f, Convert.ToDouble(d)); int W = cW / wSize; int H = cH / wSize; int w2 = W / 2; int h2 = H / 2; //    if (d == wDepth - 1) { for (int z = 0; z < 3; z++) { for (int j = 0; j < h2; j++) { for (int i = 0; i < w2; i++) { Value = (short)Math.Round(ImgData[z, i, j]); if ((Value >= -127) && (Value <= 127)) { Ret[lPos++] = Convert.ToByte(Value + 127); } else { Ret[lPos++] = 255; shorts[intCount++] = Value; } } } } } //   +   for (int z = 0; z < 3; z++) { for (int j = 0; j < H; j++) { for (int i = w2; i < W; i++) { Value = (short)Math.Round(ImgData[z, i, j]); if ((Value >= -127) && (Value <= 127)) { Ret[lPos++] = Convert.ToByte(Value + 127); } else { Ret[lPos++] = 255; shorts[intCount++] = Value; } } } } //   for (int z = 0; z < 3; z++) { for (int j = h2; j < H; j++) { for (int i = 0; i < w2; i++) { Value = (short)Math.Round(ImgData[z, i, j]); if ((Value >= -127) && (Value <= 127)) { Ret[lPos++] = Convert.ToByte(Value + 127); } else { Ret[lPos++] = 255; shorts[intCount++] = Value; } } } } } //    (byte[]  short[])   int shortArraySize = intCount * 2; Array.Resize(ref Ret, Ret.Length + shortArraySize); Buffer.BlockCopy(shorts, 0, Ret, Ret.Length - shortArraySize, shortArraySize); //     return Ret; } private double[, ,] WaveletePack(double[, ,] ImgArray, int Component, int cW, int cH, int dwDevider, int dwTop, int dwStep) { short Value; int cw2 = cW / 2; int cH2 = cH / 2; //    double dbDiv = 1f / dwDevider; ImgArray = Wv(ImgArray, cW, cH, Component, WV_TOP_TO_BOTTOM); ImgArray = Wv(ImgArray, cH, cW, Component, WV_LEFT_TO_RIGHT); //  for (int j = 0; j < cH; j++) { for (int i = 0; i < cW; i++) { if ((i >= cw2) || (j >= cH2)) { Value = (short)Math.Round(ImgArray[Component, i, j]); if (Value != 0) { int value2 = Value; if (value2 < 0) { value2 = -value2; } if (value2 < dwTop) { ImgArray[Component, i, j] = 0; } else { ImgArray[Component, i, j] = Value * dbDiv; } } } } } return ImgArray; } //     CDF 9/7  private double[, ,] Wv(double[, ,] ImgArray, int n, int dwCh, int Component, int Side) { double a; int i, j, n2 = n / 2; double[] xWavelet = new double[n]; double[] tempbank = new double[n]; for (int dwPos = 0; dwPos < dwCh; dwPos++) { if (Side == WV_LEFT_TO_RIGHT) { for (j = 0; j < n; j++) { xWavelet[j] = ImgArray[Component, dwPos, j]; } } else if (Side == WV_TOP_TO_BOTTOM) { for (i = 0; i < n; i++) { xWavelet[i] = ImgArray[Component, i, dwPos]; } } // Predict 1 a = -1.586134342f; for (i = 1; i < n - 1; i += 2) { xWavelet[i] += a * (xWavelet[i - 1] + xWavelet[i + 1]); } xWavelet[n - 1] += 2 * a * xWavelet[n - 2]; // Update 1 a = -0.05298011854f; for (i = 2; i < n; i += 2) { xWavelet[i] += a * (xWavelet[i - 1] + xWavelet[i + 1]); } xWavelet[0] += 2 * a * xWavelet[1]; // Predict 2 a = 0.8829110762f; for (i = 1; i < n - 1; i += 2) { xWavelet[i] += a * (xWavelet[i - 1] + xWavelet[i + 1]); } xWavelet[n - 1] += 2 * a * xWavelet[n - 2]; // Update 2 a = 0.4435068522f; for (i = 2; i < n; i += 2) { xWavelet[i] += a * (xWavelet[i - 1] + xWavelet[i + 1]); } xWavelet[0] += 2 * a * xWavelet[1]; // Scale a = 1f / 1.149604398f; j = 0; //     "" //     "" if (Side == WV_LEFT_TO_RIGHT) { for (i = 0; i < n2; i++) { ImgArray[Component, dwPos, i] = xWavelet[j++] / a; ImgArray[Component, dwPos, n2 + i] = xWavelet[j++] * a; } } else if (Side == WV_TOP_TO_BOTTOM) { for (i = 0; i < n2; i++) { ImgArray[Component, i, dwPos] = xWavelet[j++] / a; ImgArray[Component, n2 + i, dwPos] = xWavelet[j++] * a; } } } return ImgArray; } //   RGB  YCrCb private double[, ,] YCrCbEncode(byte[, ,] BytesRGB, int cW, int cH, double Ydiv, double Udiv, double Vdiv, int oW, int oH) { double vr, vg, vb; double kr = 0.299, kg = 0.587, kb = 0.114, kr1 = -0.1687, kg1 = 0.3313, kb1 = 0.5, kr2 = 0.5, kg2 = 0.4187, kb2 = 0.0813; Ydiv = Ydiv / 100f; Udiv = Udiv / 100f; Vdiv = Vdiv / 100f; double[, ,] YCrCb = new double[3, cW, cH]; for (int j = 0; j < oH; j++) { for (int i = 0; i < oW; i++) { vb = (double)BytesRGB[0, i, j]; vg = (double)BytesRGB[1, i, j]; vr = (double)BytesRGB[2, i, j]; YCrCb[2, i, j] = (kr * vr + kg * vg + kb * vb) * Ydiv; YCrCb[1, i, j] = (kr1 * vr - kg1 * vg + kb1 * vb + 128) * Udiv; YCrCb[0, i, j] = (kr2 * vr - kg2 * vg - kb2 * vb + 128) * Udiv; } } return YCrCb; } private unsafe byte[, ,] BmpToBytes_Unsafe(Bitmap bmp) { BitmapData bData = bmp.LockBits(new Rectangle(new Point(), bmp.Size), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); // number of bytes in the bitmap int byteCount = bData.Stride * bmp.Height; byte[] bmpBytes = new byte[byteCount]; Marshal.Copy(bData.Scan0, bmpBytes, 0, byteCount); // Copy the locked bytes from memory // don't forget to unlock the bitmap!! bmp.UnlockBits(bData); byte[, ,] ret = new byte[3, bmp.Width, bmp.Height]; for (int z = 0; z < 3; z++) { for (int i = 0; i < bmp.Width; i++) { for (int j = 0; j < bmp.Height; j++) { ret[z, i, j] = bmpBytes[j * bmp.Width * 3 + i * 3 + z]; } } } return ret; } } } 

डिकम्प्रेसर कोड


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

 using System; using System.Collections.Generic; using System.Text; namespace WaveleteCompression { class wvDecompress { //  public const int WV_LEFT_TO_RIGHT = 0; public const int WV_TOP_TO_BOTTOM = 1; public byte[] run(byte[] compressed) { int z; int dwDepth = 6; //     (  ,   ) //     int w = 512; int h = 512; //            (header)   int[] dwDiv = { 48, 32, 16, 16, 24, 24, 1, 1 }, dwTop = { 24, 32, 24, 24, 24, 24, 32, 32 }; int SamplerDiv = 2, YPerec = 100, crPerec = 85, cbPerec = 85; double[,,] yuv = doUnPack(compressed, w, h, dwDepth); //   for(z = 0; z < 2; z++) { for(int dWave = dwDepth - 1; dWave >= 0; dWave--) { int w2 = Convert.ToInt32(w / Math.Pow(2, dWave)); int h2 = Convert.ToInt32(h / Math.Pow(2, dWave)); WaveleteUnPack(yuv, z, w2, h2, dwDiv[dWave] * SamplerDiv); } } z = 2; for(int dWave = dwDepth - 1; dWave >= 0; dWave--) { int w2 = Convert.ToInt32(w / Math.Pow(2, dWave)); int h2 = Convert.ToInt32(h / Math.Pow(2, dWave)); WaveleteUnPack(yuv, z, w2, h2, dwDiv[dWave]); } // YCrCb  +      byte[] rgb_flatened = this.YCrCbDecode(yuv, w, h, YPerec, crPerec, cbPerec); return rgb_flatened; } //      DoPack   wvCompress. //      (short)double-   byte[] private static double[, ,] doUnPack(byte[] Bytes, int cW, int cH, int dwDepth) { int lPos = 0; byte Value; int intIndex = 0; //      int size = cW * cH * 3; //        double[,,] ImgData = new double[3, cW, cH]; int shortsLength = Bytes.Length - size; short[] shorts = new short[shortsLength / 2]; Buffer.BlockCopy(Bytes, size, shorts, 0, shortsLength); for (int d = dwDepth - 1; d >= 0; d--) { int wSize = (int)Math.Pow(2, d); int W = cW / wSize; int H = cH / wSize; int w2 = W / 2; int h2 = H / 2; //   if (d == dwDepth - 1) { for (int z = 0; z < 3; z++) { for (int j = 0; j < h2; j++) { for (int i = 0; i < w2; i++) { Value = Bytes[lPos++]; if(Value == 255) { ImgData[z, i, j] = shorts[intIndex++]; } else { ImgData[z, i, j] = Value - 127; } } } } } //   +   for (int z = 0; z < 3; z++) { for (int j = 0; j < H; j++) { for (int i = w2; i < W; i++) { Value = Bytes[lPos++]; if(Value == 255) { ImgData[z, i, j] = shorts[intIndex++]; } else { ImgData[z, i, j] = Value - 127; } } } } //   for (int z = 0; z < 3; z++) { for (int j = h2; j < H; j++) { for (int i = 0; i < w2; i++) { Value = Bytes[lPos++]; if (Value == 255) { ImgData[z, i, j] = shorts[intIndex++]; } else { ImgData[z, i, j] = Value - 127; } } } } } //   return ImgData; } //    private void WaveleteUnPack(double[,,] ImgArray, int Component, int cW, int cH, int dwDevider) { int cw2 = cW / 2, ch2 = cH / 2; double dbDiv = 1f / dwDevider; //   for(int i = 0; i < cW; i++) { for(int j = 0; j < cH; j++) { if ((i >= cw2) || (j >= ch2)) { if (ImgArray[Component, i, j] != 0) { ImgArray[Component, i, j] /= dbDiv; } } } } //   for(int i = 0; i < cW; i++) { reWv(ref ImgArray, cH, Component, i, WV_LEFT_TO_RIGHT); } for(int j = 0; j < cH; j++) { reWv(ref ImgArray, cW, Component, j, WV_TOP_TO_BOTTOM); } } //       CDF 9/7  private void reWv(ref double[, ,] shorts, int n, int z, int dwPos, int Side) { double a; double[] xWavelet = new double[n]; double[] tempbank = new double[n]; if(Side == WV_LEFT_TO_RIGHT) { for(int j = 0; j < n; j++) { xWavelet[j] = shorts[z, dwPos, j]; } } else if (Side == WV_TOP_TO_BOTTOM) { for(int i = 0; i < n; i++) { xWavelet[i] = shorts[z, i, dwPos]; } } for(int i = 0; i < n / 2; i++) { tempbank[i * 2] = xWavelet[i]; tempbank[i * 2 + 1] = xWavelet[i + n / 2]; } for(int i = 0; i < n; i++) { xWavelet[i] = tempbank[i]; } // Undo scale a = 1.149604398f; for(int i = 0; i < n; i++) { if(i % 2 != 0) { xWavelet[i] = xWavelet[i] * a; } else { xWavelet[i] = xWavelet[i] / a; } } // Undo update 2 a = -0.4435068522f; for (int i = 2; i < n; i += 2) { xWavelet[i] = xWavelet[i] + a * (xWavelet[i - 1] + xWavelet[i + 1]); } xWavelet[0] = xWavelet[0] + 2 * a * xWavelet[1]; // Undo predict 2 a = -0.8829110762f; for (int i = 1; i < n - 1; i += 2) { xWavelet[i] = xWavelet[i] + a * (xWavelet[i - 1] + xWavelet[i + 1]); } xWavelet[n - 1] = xWavelet[n - 1] + 2 * a * xWavelet[n - 2]; // Undo update 1 a = 0.05298011854f; for (int i = 2; i < n; i += 2) { xWavelet[i] = xWavelet[i] + a * (xWavelet[i - 1] + xWavelet[i + 1]); } xWavelet[0] = xWavelet[0] + 2 * a * xWavelet[1]; // Undo predict 1 a = 1.586134342f; for (int i = 1; i < n - 1; i += 2) { xWavelet[i] = xWavelet[i] + a * (xWavelet[i - 1] + xWavelet[i + 1]); } xWavelet[n - 1] = xWavelet[n - 1] + 2 * a * xWavelet[n - 2]; if(Side == WV_LEFT_TO_RIGHT) { for (int j = 0; j < n; j++) { shorts[z, dwPos, j] = xWavelet[j]; } } else if(Side == WV_TOP_TO_BOTTOM) { for(int i = 0; i < n; i++) { shorts[z, i, dwPos] = xWavelet[i]; } } } //   YCrCb  RGB private byte[] YCrCbDecode(double[, ,] yuv, int w, int h, double Ydiv, double Udiv, double Vdiv) { byte[] bytes_flat = new byte[3 * w * h]; double vr, vg, vb; double vY, vCb, vCr; Ydiv = Ydiv / 100f; Udiv = Udiv / 100f; Vdiv = Vdiv / 100f; for(int j = 0; j < h; j++) { for (int i = 0; i < w ; i++) { vCr = yuv[0, i, j] / Vdiv; vCb = yuv[1, i, j] / Udiv; vY = yuv[2, i, j] / Ydiv; vr = vY + 1.402f * (vCr - 128f); vg = vY - 0.34414f * (vCb - 128f) - 0.71414f * (vCr - 128f); vb = vY + 1.722f * (vCb - 128f); if (vr > 255) {vr = 255;} if (vg > 255) {vg = 255;} if (vb > 255) {vb = 255;} if (vr < 0) {vr = 0;} if (vg < 0) {vg = 0;} if (vb < 0) {vb = 0;} bytes_flat[j * w * 3 + i * 3 + 0] = (byte)vb; bytes_flat[j * w * 3 + i * 3 + 1] = (byte)vg; bytes_flat[j * w * 3 + i * 3 + 2] = (byte)vr; } } return bytes_flat; } } } 


C # प्रोजेक्ट स्रोत कोड



GitHub

संदर्भ

  1. छोटा लहर
  2. वेवलेट कम्प्रेशन
  3. जेपीईजी 2000
  4. जेपीईजी
  5. असतत वेवलेट ट्रांसफ़ॉर्म
  6. उठाने की योजना
युपीडी:
mikhanoid : 1.149604398, -0.4435068522, -0.8829110762, आदि। - तरंग बिंदुओं के मूल्यों के गुणांक कुछ बिंदुओं पर कार्य करते हैं। सामान्य रूप से पढ़ें: भारोत्तोलन योजना

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


All Articles