/* * Copyright 2011-2019, European Organisation for the Exploitation of Meteorological Satellites (EUMETSAT) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "CBuffer.h" #include "CWBlock.h" #include "CACDecoder.h" #include "CVLCDecoder.h" #include "CWTDecoder.h" namespace COMP { void CWTDecoder::ZeroBlock(unsigned short i_from_line, unsigned short i_from_column, unsigned short i_to_line, unsigned short i_to_column, unsigned short i_Bs) { COMP_TRYTHIS unsigned short bX = i_from_column; unsigned short bY = i_from_line; //unsigned short nBW = (m_Image.GetW() + i_Bs - 1) / i_Bs; // nb blocks per line //unsigned short nBH = (m_Image.GetH() + i_Bs - 1) / i_Bs; // nb blocks per column //unsigned long nB = (unsigned long)nBH * nBW; // total number of blocks CWBlock wblk(i_Bs, i_Bs); wblk.Zero(); for (; bX <= i_to_column || bY < i_to_line;) { if (bX * i_Bs >= m_Image.GetW()) { bX = 0; bY++; } if (bY > i_to_line) // go out of here break; // m_Image.put_block (pixels_block, corner_column, corner_line); unsigned int nW = ((m_Image.GetW() - bX * i_Bs) < i_Bs) ? (m_Image.GetW() - bX * i_Bs) : i_Bs; unsigned int nH = ((m_Image.GetH() - bY * i_Bs) < i_Bs) ? (m_Image.GetH() - bY * i_Bs) : i_Bs; wblk.Put(m_Image, bX * i_Bs, bY * i_Bs, nW, nH); bX++; } COMP_CATCHTHIS } //----------------------------------------------------------------------- short CWTDecoder::FindNextMarker(void) { COMP_TRYTHIS // resynchronization with the next marker (or at least with the EOF) unsigned short code; m_Buf.byteAlign(); for (;;) if (m_Buf.read_marker(code)) { if (code >= c_MarkerRESTART && code <= (c_MarkerRESTART + 15)) return (code & 0x000F); if (code == c_MarkerFOOTER) return -2; // must return a negative value. -2 is nice for debugging. m_Buf.seek(8); } else { if (m_Buf.reached_end()) return -1; else m_Buf.seek(8); } COMP_CATCHTHIS } //----------------------------------------------------------------------- bool CWTDecoder::PerformResync(unsigned int i_Bs, unsigned int &t_markerNum, unsigned int &t_nbBlock, unsigned int &t_bX, unsigned int &t_bY) { COMP_TRYTHIS // Initialize things... //---Just to avoid passing these parameters... unsigned short nBW = (m_Image.GetW() + i_Bs - 1) / i_Bs; // nb blocks per line unsigned short nBH = (m_Image.GetH() + i_Bs - 1) / i_Bs; // nb blocks per column unsigned long nB = (unsigned long)nBH * nBW; // total number of blocks unsigned int begin_bY = 0; unsigned int new_bX = 0; unsigned int new_bY = 0; // Find the next restart marker short new_RSTm = FindNextMarker(); // Found new marker begin_bY = (t_markerNum * m_Param.m_RestartInterval) / nBW; if (new_RSTm < 0) // no usable marker found { new_bX = nBW; new_bY = nBH - 1; if (m_Param.m_RestartInterval > 0) { t_nbBlock = nB % m_Param.m_RestartInterval; t_markerNum = nB / m_Param.m_RestartInterval; } else { // not needed since already null //t_nbBlock = 0; //t_markerNum = 0; } } else { // a valid restart marker was found new_RSTm = new_RSTm - (t_markerNum % 16); // if new_RSTm == 0 --> assume we didn't miss 16 markers but that we found the marker we missed before t_markerNum += new_RSTm; unsigned long new_nB = (t_markerNum + 1) * m_Param.m_RestartInterval; if (new_nB > nB) new_nB = nB; t_nbBlock = m_Param.m_RestartInterval; new_bX = (new_nB - 1) % nBW; new_bY = (new_nB - 1) / nBW; } ZeroBlock(t_bY, t_bX, new_bY, new_bX, i_Bs); m_Qinfo.Negate(begin_bY * i_Bs, cmin(t_bY * i_Bs + i_Bs, m_Image.GetH()) - 1); if (t_bY < new_bY) m_Qinfo.Zero(cmin(t_bY * i_Bs + i_Bs, m_Image.GetH()), cmin(new_bY * i_Bs + i_Bs, m_Image.GetH())); // modify the returned data t_bX = new_bX; t_bY = new_bY; m_LastGoodLine = cmin(new_bY * i_Bs + i_Bs, m_Image.GetH()); if (new_RSTm < 0) return false; // do not eat the marker, it is the EOI marker else return true; COMP_CATCHTHIS } //----------------------------------------------------------------------- bool CWTDecoder::DecodeBufferBlock(const unsigned int i_Bs) { COMP_TRYTHIS unsigned int bX, bY; // Compute the number of blocks along each dimension unsigned int nbW = m_Image.GetW() / i_Bs; const unsigned int rW = m_Image.GetW() % i_Bs; if (rW) nbW++; unsigned int nbH = m_Image.GetH() / i_Bs; const unsigned int rH = m_Image.GetH() % i_Bs; if (rH) nbH++; // Start the arithmetic decoder CACDecoder ad(m_Buf); ad.Start(); // VLC decoder object creation CVLCDecoder decoder(ad); // Wavelet block object creation CWBlock wblk(i_Bs, i_Bs); // block loops unsigned int nbBlock = 0; // relative block index unsigned int markerNum = 0; // absolute block index bool goodline = true; // true if current line is ok from the beginning bool bACStopped = false; for (bY = 0; bY < nbH; bY++) { goodline = true; const unsigned int nH = bY == (nbH - 1) && rH ? rH : i_Bs; for (bX = 0; bX < nbW; bX++) { const unsigned int nW = bX == (nbW - 1) && rW ? rW : i_Bs; // Decode the stream if (!decoder.Decode(wblk, m_Param.m_nWTlevels, m_Param.m_nLossyBitPlanes) || ad.IsMarkerReached()) { // Decode failed and/or a marker was reached during decoding -> error if (goodline) { // Set quality info m_LastGoodLine = bY * i_Bs + nH; m_Qinfo.Set(bY * i_Bs, m_LastGoodLine - 1, (-1) * (int)bX * (int)i_Bs); } goodline = false; PerformResync(i_Bs, markerNum, nbBlock, bX, bY); } else { switch (m_Param.m_PredMode) { case CWTParams::e_None: wblk.IterateSt(false, m_Param.m_nWTlevels); break; case CWTParams::e_PredA: wblk.IterateSptA(false, m_Param.m_nWTlevels); break; case CWTParams::e_PredB: wblk.IterateSptB(false, m_Param.m_nWTlevels); break; case CWTParams::e_PredC: wblk.IterateSptC(false, m_Param.m_nWTlevels); break; default: Assert(0, Util::CParamException()) } // Copy CWBlock to image wblk.Put(m_Image, bX * i_Bs, bY * i_Bs, nW, nH); nbBlock++; } if (m_Param.m_RestartInterval && nbBlock == m_Param.m_RestartInterval) { unsigned short marker; // Check if the Restart marker is there nbBlock = 0; ad.Stop(); if (!m_Buf.read_marker(marker) || marker != (c_MarkerRESTART | (markerNum & 0xF))) { // Restart marker not found // Perform resynchronization if (goodline) { // Set quality info m_LastGoodLine = bY * i_Bs + nH; m_Qinfo.Set(bY * i_Bs, m_LastGoodLine - 1, (-1) * (int)(bX * i_Bs + nW)); } goodline = false; bX++; // the current block is complete and shouldn't be reset if (PerformResync(i_Bs, markerNum, nbBlock, bX, bY)) m_Buf.seek(16); nbBlock = 0; } else { // Restart marker found m_Buf.seek(16); } markerNum++; if (bX < (nbW - 1) || bY < (nbH - 1)) ad.Start(); else bACStopped = true; // Reset VLC decoder state (models probabilities) decoder.Reset(); } } if (goodline) { // Set quality info m_LastGoodLine = bY * i_Bs + nH; m_Qinfo.Set(bY * i_Bs, m_LastGoodLine - 1, m_Image.GetW()); } goodline = true; } // Stop the arithmetic decoder if (!bACStopped) ad.Stop(); if (m_LastGoodLine < m_Image.GetH()) DecodeBufferError(m_LastGoodLine, m_Image.GetH()); else { unsigned short marker = 0; // Check if Footer marker is there if (!m_Buf.read_marker(marker) || marker != c_MarkerFOOTER) { // FOOTER marker not found unsigned short nBW = (m_Image.GetW() + i_Bs - 1) / i_Bs; // nb blocks per line unsigned short begin_bY = (markerNum * m_Param.m_RestartInterval) / nBW; m_Qinfo.Negate(begin_bY * i_Bs, m_Image.GetH() - 1); } m_Buf.seek(16); } return true; COMP_CATCHTHIS } bool CWTDecoder::DecodeBufferFull(void) { COMP_TRYTHIS // Start the arithmetic decoder CACDecoder ad(m_Buf); ad.Start(); // Compute CWBlock size to allow m_Param.m_nWTlevels S+P iterations int bW = m_Image.GetW(); int bH = m_Image.GetH(); const int iteS = 1U << m_Param.m_nWTlevels; bW = (bW + iteS - 1) & (-iteS); bH = (bH + iteS - 1) & (-iteS); // Wavelet block object creation CWBlock wblk(bW, bH); // VLC decoder object creation CVLCDecoder decoder(ad); // Decode stream if (!decoder.Decode(wblk, m_Param.m_nWTlevels, m_Param.m_nLossyBitPlanes) || ad.IsMarkerReached()) // Decode failed and/or a marker was reached during decoding -> error return false; // Perform the inverse m_nWTlevels iterations of 2D S(+P) transform switch (m_Param.m_PredMode) { case CWTParams::e_None: wblk.IterateSt(false, m_Param.m_nWTlevels); break; case CWTParams::e_PredA: wblk.IterateSptA(false, m_Param.m_nWTlevels); break; case CWTParams::e_PredB: wblk.IterateSptB(false, m_Param.m_nWTlevels); break; case CWTParams::e_PredC: wblk.IterateSptC(false, m_Param.m_nWTlevels); break; default: Assert(0, Util::CParamException()) } // Copy the Wavelet block coefs to image wblk.Put(m_Image, 0, 0, m_Image.GetW(), m_Image.GetH()); // Stop the arithmetic decoder ad.Stop(); // Set quality info m_Qinfo.Set(0, m_Image.GetH() - 1, m_Image.GetW()); m_LastGoodLine = m_Image.GetH(); unsigned short marker = 0; // Check if Footer marker is there (there is no restart marker in full-image mode) if (!m_Buf.read_marker(marker) || marker != c_MarkerFOOTER) { // FOOTER marker not found m_Qinfo.Negate(0, m_Image.GetH() - 1); } m_Buf.seek(16); return true; COMP_CATCHTHIS } void CWTDecoder::DecodeBuffer(void) { COMP_TRYTHIS unsigned short marker; // reset the reading function m_Buf.real_rewind(); // Check if the Header marker is there if (!m_Buf.read_marker(marker) || marker != c_MarkerHEADER) { // HEADER marker not found DecodeBufferError(0, m_Image.GetH()); return; } m_Buf.real_seek(16); // Original image number of bits per pixel m_Param.m_BitsPerPixel = m_Buf.readN(4); m_Buf.real_seek(4); if (m_Param.m_BitsPerPixel == 0) m_Param.m_BitsPerPixel = 16; // Image width const unsigned int imgW = m_Buf.readN(16); m_Buf.real_seek(16); if (imgW != m_Image.GetW()) { // Bad image width in header DecodeBufferError(0, m_Image.GetH()); return; } // Image height const unsigned int imgH = m_Buf.readN(16); m_Buf.real_seek(16); if (imgH != m_Image.GetH()) { // Bad image height in header DecodeBufferError(0, m_Image.GetH()); return; } // Number of S+P transform iterations m_Param.m_nWTlevels = m_Buf.readN(2) + 3; m_Buf.real_seek(2); // S+P prediction type m_Param.m_PredMode = (CWTParams::EWTPredictionMode)(CWTParams::e_None + m_Buf.readN(2)); m_Buf.real_seek(2); // Block coding type const unsigned int blockType = m_Buf.readN(2); m_Buf.real_seek(2); // Restart marker interval in number of blocks m_Param.m_RestartInterval = m_Buf.readN(16); m_Buf.real_seek(16); // Lossy coding parameter m_Param.m_nLossyBitPlanes = m_Buf.readN(4); m_Buf.real_seek(4); // Skip 2 pad bits to be sure next marker dont need align m_Buf.real_seek(2); // Check if the Data marker is there if (!m_Buf.read_marker(marker) || marker != c_MarkerDATA) { // DATA marker not found DecodeBufferError(0, m_Image.GetH()); return; } m_Buf.real_seek(16); m_Buf.resync(); m_LastGoodLine = 0; switch (blockType) { case 0: m_Param.m_BlockMode = CWTParams::e_16x16Block; if (!DecodeBufferBlock(16U)) std::cerr << "Can not decode blocky (16x16) buffer" << std::endl; break; case 1: m_Param.m_BlockMode = CWTParams::e_32x32Block; if (!DecodeBufferBlock(32U)) std::cerr << "Can not decode blocky (32x32) buffer" << std::endl; break; case 2: m_Param.m_BlockMode = CWTParams::e_64x64Block; if (!DecodeBufferBlock(64U)) std::cerr << "Can not decode blocky (64x64) buffer" << std::endl; break; case 3: m_Param.m_BlockMode = CWTParams::e_FullWidth; if (!DecodeBufferFull()) std::cerr << "Can not decode full buffer" << std::endl; break; } COMP_CATCHTHIS } } // end namespace