Ejemplo n.º 1
0
        //Разжать изображение из формата .compressed (подробнее в Readme.md)
        static public float[] decompressImageBytes(
            byte[] compressedBytes,
            out int width,
            out int height,
            out string colorScheme,
            out string method,
            out int blockSize,
            out string travelMode,
            out bool crossMerge
            )
        {
            //считваем заголовок, что бы узнать информацию об изображении
            readHeader_v1(
                compressedBytes,
                out width,
                out height,
                out colorScheme,
                out method,
                out blockSize,
                out travelMode,
                out crossMerge
                );

            int blocksAmount             = getTotalBlocksAmount(blockSize, width, height);
            int elementsAmountInOneBlock = blockSize * blockSize * 3;

            //разжимаем основную часть (уложенне блоки и остаток)
            float[] values = byteArrayAsFloatArray(decompressBytes(compressedBytes, Constants.HeaderSize));

            //восстанавливаем блоки
            float[][] blocks = new float[blocksAmount][];

            if (crossMerge) //с кросс-мерджем
            {
                restoreCrossMergedBlocks(values, blocksAmount, elementsAmountInOneBlock, blocks);
            }
            else //без кросс-мерджа
            {
                Parallel.For(0, blocks.Length, i =>
                {
                    float[] temp = new float[elementsAmountInOneBlock];
                    for (int j = 0; j < elementsAmountInOneBlock; ++j)
                    {
                        temp[j] = values[i * elementsAmountInOneBlock + j];
                    }
                    blocks[i] = temp;
                });
            }

            //если надо - восстанавливаем линейность из зигзага
            if (travelMode == "Зиг-заг")
            {
                ZigZag.precompute(blockSize);
                Parallel.For(0, blocks.Length, i =>
                {
                    blocks[i] = fromZigzag(blocks[i]);
                });
            }

            //к каждому блоку применяем обратное преобразование
            switch (method)
            {
            case "FHT":
                MathUtils.mapFunctionToEachBlock(MathUtils.inv_fht_2d, blocks);
                break;

            case "DCT":
                MathUtils.mapFunctionToEachBlock(MathUtils.inv_dct_2d, blocks);
                break;
            }

            float[] resultValues = mergeBlocksToImageValues(blocks, width, height, blockSize);

            //записываем остаток, если он был
            long remainderSize = getRemainderSize(width, height, blockSize);

            if (remainderSize != 0)
            {
                float[] remainder = new float[remainderSize];
                for (int i = 0; i < remainderSize; ++i)
                {
                    remainder[i] = values[blocks.Length * elementsAmountInOneBlock + i];
                }

                writeRemainderIntoImageValues(remainder, resultValues, width, height, blockSize);
            }

            return(resultValues);
        }
Ejemplo n.º 2
0
        //сжать изображение в формат .compressed (подробнее в Readme.md)
        public static byte[] compressImageValues(
            float[] imageValues,
            int width,
            int height,
            string colorScheme,
            string method,
            Tuple <float, float, float> quality,
            int blockSize,
            string travelMode,
            bool crossMerge
            )
        {
            if (imageValues == null)
            {
                return(null);
            }

            //узнаем размер остатка
            long remainderSize = getRemainderSize(width, height, blockSize);

            //бьем изображение на блоки
            var blocks                    = splitImageValuesIntoBlocks(imageValues, width, height, blockSize);
            int pixelsInBlock             = blockSize * blockSize;
            var elementsAmountInOneBlock  = pixelsInBlock * 3;
            var elementsAmountInAllBlocks = elementsAmountInOneBlock * blocks.Length;

            //применяем прямое преобразоваение к каждому блоку
            switch (method)
            {
            case "FHT":
                MathUtils.mapFunctionToEachBlock(MathUtils.fht_2d, blocks);
                break;

            case "DCT":
                MathUtils.mapFunctionToEachBlock(MathUtils.dct_2d, blocks);
                break;
            }

            // Обнуляем малозначимые коэффициенты

            // узнаем, сколько элементов должно быть обнулено
            int[] q = new int[3];
            q[0] = (int)MathUtils.MapInterval(100 - quality.Item1, 0, 100, 0, pixelsInBlock);
            q[1] = (int)MathUtils.MapInterval(100 - quality.Item2, 0, 100, 0, pixelsInBlock);
            q[2] = (int)MathUtils.MapInterval(100 - quality.Item3, 0, 100, 0, pixelsInBlock);

            switch (method)
            {
            case "FHT":
            {
                Parallel.For(0, blocks.Length, i =>
                    {
                        //В преобразовни Хаара малозначащие коэффициенты - это те,
                        //которые близки к нулю.

                        //Поэтому для каждого канала создадим список пар вида (индекс, значение)
                        //отсортируем по модулю значения
                        //и оставим только индексы
                        //и обнулим необходимое количество элементов по первым q[j] из этих индексов

                        float[][] channels = new float[3][];
                        for (int j = 0; j < 3; ++j)
                        {
                            channels[j] = new float[pixelsInBlock];
                            for (int k = 0; k < pixelsInBlock; ++k)
                            {
                                channels[j][k] = blocks[i][k * 3 + j];
                            }
                        }

                        int[][] indexes = new int[3][];
                        for (int j = 0; j < 3; ++j)
                        {
                            indexes[j] = channels[j].Select((elem, ind) => new { Index = ind, Value = elem })
                                         .OrderBy(x => Math.Abs(x.Value))
                                         .Take(q[j])
                                         .Select(x => x.Index)
                                         .ToArray();
                        }

                        for (int j = 0; j < 3; ++j)
                        {
                            for (int k = 0; k < q[j]; ++k)
                            {
                                blocks[i][indexes[j][k] * 3 + j] = 0;
                            }
                        }
                    });
                break;
            }

            case "DCT":
            {
                //малозначимые коэффициенты скапливаются в правом нижнем углу,
                //так что обнуляем начиная оттуда и пока не обнулим столько, сколько надо
                int[] indexes = ZigZag.getIndexes(blockSize);
                Parallel.For(0, blocks.Length, i =>
                    {
                        for (int channel = 0; channel < 3; ++channel)
                        {
                            for (int j = 0; j < q[channel]; ++j)
                            {
                                blocks[i][indexes[pixelsInBlock - 1 - j] * 3 + channel] = 0;
                            }
                        }
                    });
                break;
            }
            }


            //если нужно - обходим блоки зиг-загом
            if (travelMode == "Зиг-заг")
            {
                ZigZag.precompute(blockSize);
                Parallel.For(0, blocks.Length, i =>
                {
                    blocks[i] = toZigzag(blocks[i]);
                });
            }

            float[] values = new float[elementsAmountInAllBlocks];

            //укладываем блоки
            if (crossMerge) //с кросс-мерджем
            {
                crossMergeBlocks(blocks, values);
            }
            else //без кросс-мерджа
            {
                Parallel.For(0, blocks.Length, block_i =>
                {
                    for (int i = 0; i < elementsAmountInOneBlock; ++i)
                    {
                        values[block_i * elementsAmountInOneBlock + i] = blocks[block_i][i];
                    }
                });
            }

            // если есть остаток
            if (remainderSize != 0)
            {
                //то получаем его
                float[] remainder = getRemainder(imageValues, width, height, blockSize);

                //увеличиваем values, что бы он влез
                Array.Resize(ref values, values.Length + remainder.Length);

                //дописываем его в конец
                for (int i = 0; i < remainder.Length; ++i)
                {
                    values[elementsAmountInAllBlocks + i] = remainder[i];
                }
            }

            //сжимаем
            byte[] compressedBytes = compressBytes(floatArrayAsByteArray(values));

            //сюда запишем итоговый .compresed (то есть заголовок и сжатые байты(уложенные блоки + остаток))
            byte[] resultBytes = new byte[Constants.HeaderSize + compressedBytes.Length + remainderSize * 4];

            //записываем заголовок
            writeHeader_v1(resultBytes, width, height, colorScheme, method, blockSize, travelMode, crossMerge);

            //дописываем сжатые данные
            for (int i = 0; i < compressedBytes.Length; ++i)
            {
                resultBytes[Constants.HeaderSize + i] = compressedBytes[i];
            }

            return(resultBytes);
        }