Я использовал библиотеку FJCore в проекте Silverlight, чтобы помочь с некоторой обработкой изображений в реальном времени. , и я пытаюсь понять, как добиться от библиотеки немного большего сжатия и производительности. Теперь, насколько я понимаю, стандарт JPEG позволяет вам указать коэффициент подвыборки цветности (см. http://en.wikipedia.org/wiki/Chroma_subsampling и http://en.wikipedia.org/wiki/Jpeg); и кажется, что это должно быть реализовано в библиотеке FJCore с использованием массивов HsampFactor и VsampFactor:
public static readonly byte[] HsampFactor = { 1, 1, 1 };
public static readonly byte[] VsampFactor = { 1, 1, 1 };
Однако мне сложно понять, как их использовать. Мне кажется, что текущие значения должны представлять подвыборку 4: 4: 4 (например, без подвыборки вообще), и что если бы я хотел получить подвыборку 4: 1: 1, правильные значения были бы примерно такими:
public static readonly byte[] HsampFactor = { 2, 1, 1 };
public static readonly byte[] VsampFactor = { 2, 1, 1 };
По крайней мере, так эти значения используются в других подобных библиотеках (например, см. Пример кода здесь для libjpeg).
Однако ни приведенные выше значения {2, 1, 1}, ни любой другой набор значений, который я пробовал, кроме {1, 1, 1}, не дает четкого изображения. Глядя на код, не кажется, что он так написан. Но хоть убей, я не могу понять, что на самом деле пытается делать код FJCore. Похоже, что он просто использует коэффициенты выборки для повторения операций, которые уже были выполнены, то есть, если бы я не знал лучше, я бы сказал, что это ошибка. Но это довольно устоявшаяся библиотека, основанная на довольно хорошо известном Java-коде, поэтому я был бы удивлен, если бы это было так.
Есть ли у кого-нибудь предложения по использованию этих значений для получения субдискретизации цветности 4: 2: 2 или 4: 1: 1?
Вот соответствующий код из JpegEncoder класс:
for (comp = 0; comp < _input.Image.ComponentCount; comp++)
{
Width = _input.BlockWidth[comp];
Height = _input.BlockHeight[comp];
inputArray = _input.Image.Raster[comp];
for (i = 0; i < _input.VsampFactor[comp]; i++)
{
for (j = 0; j < _input.HsampFactor[comp]; j++)
{
xblockoffset = j * 8;
yblockoffset = i * 8;
for (a = 0; a < 8; a++)
{
// set Y value. check bounds
int y = ypos + yblockoffset + a; if (y >= _height) break;
for (b = 0; b < 8; b++)
{
int x = xpos + xblockoffset + b; if (x >= _width) break;
dctArray1[a, b] = inputArray[x, y];
}
}
dctArray2 = _dct.FastFDCT(dctArray1);
dctArray3 = _dct.QuantizeBlock(dctArray2, FrameDefaults.QtableNumber[comp]);
_huf.HuffmanBlockEncoder(buffer, dctArray3, lastDCvalue[comp], FrameDefaults.DCtableNumber[comp], FrameDefaults.ACtableNumber[comp]);
lastDCvalue[comp] = dctArray3[0];
}
}
}
И обратите внимание, что в циклах i и j они не управляют никаким пропуском пикселей: если для HsampFactor [0] установлено значение два, он просто захватывает два блока вместо одного.