Как я могу написать рациональный тег, например 200/1, используя библиотеку LibTiff.Net 2.3?

Я понятия не имею, почему это так сложно сделать, но я не могу заставить LibTiff.Net 2.3 правильно установить рациональное значение... На протяжении многих лет я всегда использовал такие значения, как «200/1» в теге tiff номер 282 (XRESOLUTION ) и 283(YРАЗРЕШЕНИЕ). Но при использовании библиотеки LibTiff.Net получить такие результаты кажется невозможным. Вместо этого я всегда получаю что-то вроде «419430400/2097152». Кто-нибудь знает, как я могу решить эту проблему?

Примечания к моему вопросу:

Это библиотека libtiff (до .Net), и она выглядит как первая, если учитывает что-то вроде 200/1.

TIFFWriteDirectoryTagCheckedRationalArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, float* value)
{
    static const char module[] = "TIFFWriteDirectoryTagCheckedRationalArray";
    uint32* m;
    float* na;
    uint32* nb;
    uint32 nc;
    int o;
    assert(sizeof(uint32)==4);
    m=_TIFFmalloc(count*2*sizeof(uint32));
    if (m==NULL)
    {
        TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
        return(0);
    }
    for (na=value, nb=m, nc=0; nc<count; na++, nb+=2, nc++)
    {
        if (*na<=0.0)
        {
            nb[0]=0;
            nb[1]=1;
        }
        else if (*na==(float)(uint32)(*na))
        {
            nb[0]=(uint32)(*na);
            nb[1]=1;
        }
        else if (*na<1.0)
        {
            nb[0]=(uint32)((*na)*0xFFFFFFFF);
            nb[1]=0xFFFFFFFF;
        }
        else
        {
            nb[0]=0xFFFFFFFF;
            nb[1]=(uint32)(0xFFFFFFFF/(*na));
        }
    }
    if (tif->tif_flags&TIFF_SWAB)
        TIFFSwabArrayOfLong(m,count*2);
    o=TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_RATIONAL,count,count*8,&m[0]);
    _TIFFfree(m);
    return(o);
}

Это новая версия .Net...

private bool writeRationalArray(ref TiffDirEntry dir, float[] v)
{
    int[] t = new int [2 * dir.tdir_count];
    for (int i = 0; i < dir.tdir_count; i++)
    {
        int sign = 1;
        float fv = v[i];
        if (fv < 0)
        {
            if (dir.tdir_type == TiffType.RATIONAL)
            {
                WarningExt(this, m_clientdata, m_name,
                "\"{0}\": Information lost writing value ({1:G}) as (unsigned) RATIONAL",
                FieldWithTag(dir.tdir_tag).Name, fv);
                fv = 0;
            }
            else
            {
                fv = -fv;
                sign = -1;
            }
        }

        int den = 1;
        if (fv > 0)
        {
            while (fv < (1L << (31 - 3)) && den < (1L << (31 - 3)))
            {
                fv *= 1 << 3;
                den *= 1 << 3;
            }
        }

        t[2 * i + 0] = (int)(sign * (fv + 0.5));
        t[2 * i + 1] = den;
    }

    return writeData(ref dir, t, 2 * dir.tdir_count);
}

person Arvo Bowen    schedule 25.06.2012    source источник


Ответы (2)


Обязательно дайте мне 1UP, если это помогло вам, пожалуйста. Спасибо!

Хорошо, поэтому я решил заняться редактированием библиотеки, чтобы учесть уменьшенную дробь. Ниже приведен код, который я изменил в библиотеке .Net, чтобы он работал. И работает!

Надеюсь, Бобровский включит это в свой следующий релиз. Я уверен, что кто-то еще будет благодарен, кроме меня! ;)

Если вы немного не знаете, как редактировать библиотеку, вот шаги, которые я использовал настолько подробно, насколько это возможно...

1) Загрузите исходный код библиотеки, расположенный здесь.

2) Откройте файл проекта. Я использовал файл решения LibTiff.NET_NoSilverlight.sln, чтобы открыть проект.

3) Разверните узел проекта LibTiff.

4) Разверните узел Internals.

5) Найдите файл класса Tiff_DirWrite.cs и откройте его.

6) Внутри этого файла класса я нашел функцию с именем writeRationalArray, и вот как она выглядела...

/// <summary>
/// Setup a directory entry of an array of RATIONAL or SRATIONAL and
/// write the associated indirect values.
/// </summary>
private bool writeRationalArray(ref TiffDirEntry dir, float[] v)
{
    int[] t = new int [2 * dir.tdir_count];
    for (int i = 0; i < dir.tdir_count; i++)
    {
        int sign = 1;
        float fv = v[i];
        if (fv < 0)
        {
            if (dir.tdir_type == TiffType.RATIONAL)
            {
                WarningExt(this, m_clientdata, m_name,
                    "\"{0}\": Information lost writing value ({1:G}) as (unsigned) RATIONAL",
                    FieldWithTag(dir.tdir_tag).Name, fv);
                fv = 0;
            }
            else
            {
                fv = -fv;
                sign = -1;
            }
        }

        int den = 1;
        if (fv > 0)
        {
            while (fv < (1L << (31 - 3)) && den < (1L << (31 - 3)))
            {
                fv *= 1 << 3;
                den *= 1 << 3;
            }
        }

        t[2 * i + 0] = (int)(sign * (fv + 0.5));
        t[2 * i + 1] = den;
    }

    return writeData(ref dir, t, 2 * dir.tdir_count);
}

7) Отредактируйте функцию writeRationalArray, чтобы она выглядела следующим образом...

/// <summary>
/// Setup a directory entry of an array of RATIONAL or SRATIONAL and
/// write the associated indirect values.
/// </summary>
private bool writeRationalArray(ref TiffDirEntry dir, float[] v)
{
    int[] t = new int [2 * dir.tdir_count];
    for (int i = 0; i < dir.tdir_count; i++)
    {
        int sign = 1;
        float fv = v[i];
        if (fv < 0)
        {
            if (dir.tdir_type == TiffType.RATIONAL)
            {
                WarningExt(this, m_clientdata, m_name,
                    "\"{0}\": Information lost writing value ({1:G}) as (unsigned) RATIONAL",
                    FieldWithTag(dir.tdir_tag).Name, fv);
                fv = 0;
            }
            else
            {
                fv = -fv;
                sign = -1;
            }
        }

        int den = 1;
        if (fv > 0)
        {
            while (fv < (1L << (31 - 3)) && den < (1L << (31 - 3)))
            {
                fv *= 1 << 3;
                den *= 1 << 3;
            }
        }

        t[2 * i + 0] = (int)(sign * (fv + 0.5));
        t[2 * i + 1] = den;

        //Reduce the fraction
        int a = t[2 * i + 0];
        int b = t[2 * i + 1];
        while (b > 0) { int rem = a % b; a = b; b = rem; }
        for (int ind = 0; ind < 2; ind++) { t[2 * i + ind] /= a; }
    }

    return writeData(ref dir, t, 2 * dir.tdir_count);
}

Все, что я сделал, это добавил три строки кода, чтобы уменьшить дробь в конце.

person Arvo Bowen    schedule 26.06.2012

При использовании LibTiff.Net вы должны устанавливать рациональные значения следующим образом:

image.SetField(TiffTag.XRESOLUTION, 150.0);
image.SetField(TiffTag.YRESOLUTION, 150.0);

Библиотека запишет рациональное значение в виде пары целочисленных значений (делимого и делителя) в формате TIFF. Вам не нужно помогать ему в этом.

person Bobrovsky    schedule 25.06.2012
comment
Да и я делаю. ;) Но это не меняет того факта, что в TIFF он отображается как 419430400/2097152 вместо 200/1. По какой-то причине LibTiff не приводит дробь к наименьшему общему знаменателю. Почему это происходит? - person Arvo Bowen; 25.06.2012
comment
Я не знаю, почему авторы libtiff решили не уменьшать дробь (возможно, производительность). Если вам абсолютно необходимо уменьшить дробь, я предлагаю вам использовать модифицированный исходный код. writeRationalArray в Tiff_DirWrite.cs — это метод, который вы, возможно, захотите изменить. - person Bobrovsky; 25.06.2012
comment
Спасибо Бобровский! Вы знаете кого-нибудь, кто делал это раньше? Я никогда раньше не изменял исходный код LibTiff.Net, что я должен знать, прежде чем приступить к взлому? - person Arvo Bowen; 25.06.2012
comment
Я лично портировал libtiff на LibTiff.Net, поэтому я сильно изменил исходный код :-) Пакет исходного кода LibTiff.Net содержит несколько модульных тестов, так что у вас будет какая-то защита, когда вы начнете взламывать. Кроме этого, я думаю, это метод проб и ошибок. Цель порта - предоставить точно такую ​​же функциональность, поэтому, если libtiff записывает дроби без сокращения (стоит проверить, кстати), то LibTiff.Net будет делать то же самое. Если есть разница между этими библиотеками, то разница считается ошибкой, и я постараюсь ее исправить. - person Bobrovsky; 25.06.2012
comment
Где можно найти исходный код libtiff? - person Arvo Bowen; 25.06.2012
comment
Бобровский Я думаю, что это ошибка, так как исходная библиотека выглядит так, как будто она ее поддерживает. См. мой измененный вопрос выше (примечания). - person Arvo Bowen; 25.06.2012
comment
Спасибо. Я взгляну. Судя по фрагменту кода, похоже, что поддержка была добавлена ​​в ветке 4.x libtiff. LibTiff.Net основан на ветке 3.x. К сожалению, я не планирую переходить на более новую версию в ближайшее время, так как мне не нужна поддержка BigTIFF (это самое важное улучшение в ветке 4.x) и мне больше некому помочь с портом. - person Bobrovsky; 26.06.2012
comment
По крайней мере один человек был бы ОЧЕНЬ счастлив, если бы вы добавили этот небольшой фрагмент кода в версию .Net. ;) Или, по крайней мере, скажите мне, что я должен добавить, и позвольте мне создать свой собственный. Просто глядя на фрагменты, я не могу понять, куда бы я поместил if () {} в... или что бы я использовал, чтобы директива if работала... - person Arvo Bowen; 26.06.2012