Unity 3D : TIFF 8bit / 16bit / II / MM 解碼器 範例

版权声明:本文为博主 ( 黃彥霖 ) 原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_38884324/article/details/81904939

前言 :

此程式碼可以解 TIFF,且 II 或 MM 排列都能解,16bit 也能解。程式碼有點亂哈,湊合看吧,以後有時間我再整理一下。

另外編碼器可以看我之前寫的這篇 : https://blog.csdn.net/weixin_38884324/article/details/82042992

執行結果 :

这里写图片描述

这里写图片描述

DecodeTIFF.cs :

using System.Collections.Generic;
using System.IO;
using UnityEngine;

public class DecodeTIFF
{
    IFH InitIFH(byte[] b)
    {
        IFH ifh = new IFH();
        ifh.ByteOrder = (char)b[0] + "" + (char)b[1];

        if (ifh.ByteOrder != "II" && ifh.ByteOrder != "MM")
        {
            ifh.IsTIFF = false;
            return ifh;
        };

        ifh.Version = ByteArrayToInt(ifh.ByteOrder, new byte[] { b[2], b[3] });

        ifh.IsTIFF = ifh.Version == 42 ? true : false;
        if (ifh.IsTIFF == false) return ifh;

        ifh.OffsetToFirstIFD = ByteArrayToInt(ifh.ByteOrder, new byte[] { b[4], b[5], b[6], b[7] });
        return ifh;
    }

    int ByteArrayToInt(string order, byte[] b)
    {
        int value = 0;

        if (order == "II")
            for (int i = 0; i < b.Length; i++) value |= b[i] << i * 8;
        else if (order == "MM")
            for (int i = 0; i < b.Length; i++) value |= b[b.Length - 1 - i] << i * 8;
        else
            throw new UnityException("The order value is not II or MM.");

        return value;
    }

    // [ x, y, rgb ]
    public uint[,,] pixel;

    public Texture2D Open(string path)
    {
        byte[] b = File.ReadAllBytes(path);

        //---------------------------------------------------
        // IFH Init

        IFH ifh = InitIFH(b);

        if (ifh.IsTIFF == false)
        {
            Debug.LogError("Not a TIFF File.");
            return null;
        }

        Debug.Log("order : " + ifh.ByteOrder);
        Debug.Log("version : " + ifh.Version);
        Debug.Log("firstIFDOffset : " + ifh.OffsetToFirstIFD);

        //---------------------------------------------------


        IFD ifd = new IFD();
        DE de = new DE();

        int TagCount = ByteArrayToInt(ifh.ByteOrder,
            new byte[] { b[ifh.OffsetToFirstIFD + 0], b[ifh.OffsetToFirstIFD + 1] });

        Debug.Log("TagCount : " + TagCount);



        int FirstDEIndex = ifh.OffsetToFirstIFD + 2;

        Debug.Log("# FirstDEIndex : " + FirstDEIndex);

        TIFF_TAG_VALUE tiff_tag_value = new TIFF_TAG_VALUE();

        for (int i = 0; i < TagCount; i++)
        {
            int index = FirstDEIndex + i * 12;

            int tag = ByteArrayToInt(ifh.ByteOrder, new byte[] { b[index + 0], b[index + 1] });
            int type = ByteArrayToInt(ifh.ByteOrder, new byte[] { b[index + 2], b[index + 3] });
            int length = ByteArrayToInt(ifh.ByteOrder, new byte[] { b[index + 4], b[index + 5], b[index + 6], b[index + 7] });
            int valueOffset = 0;



            //---------------------------------
            // 只完成 1~5 部分,後續須查資料

            string type_str = "";

            if (type == 1)
            {
                type_str = "byte"; // 8 bit ( 無符號 )
                valueOffset = b[index + 8];
            }
            else if (type == 2)
            {
                type_str = "ascii"; // 7 bit ASCII + 1 bit 2 進制 0
                valueOffset = ByteArrayToInt(ifh.ByteOrder, new byte[] { b[index + 8], b[index + 9], b[index + 10], b[index + 11] });
            }
            else if (type == 3)
            {
                type_str = "short"; // 16 bit ( 無符號 )

                int valueBitCount = 16 * length;

                if (valueBitCount <= 32)
                {
                    valueOffset = ByteArrayToInt(ifh.ByteOrder, new byte[] { b[index + 8], b[index + 9] });
                }
                else
                {
                    valueOffset = ByteArrayToInt(ifh.ByteOrder, new byte[] { b[index + 8], b[index + 9], b[index + 10], b[index + 11] });
                }
            }
            else if (type == 4)
            {
                type_str = "long"; // 32 bit ( 無符號 )
                valueOffset = ByteArrayToInt(ifh.ByteOrder, new byte[] { b[index + 8], b[index + 9], b[index + 10], b[index + 11] });
            }
            else if (type == 5)
            {
                type_str = "rational"; // 64 bit ( 2 個 LONG,第一個是分子,第二個是分母 )
                valueOffset = ByteArrayToInt(ifh.ByteOrder, new byte[] { b[index + 8], b[index + 9], b[index + 10], b[index + 11] });
            }
            else if (type == 6) type_str = "sbyte";
            else if (type == 7) type_str = "undefined";
            else if (type == 8) type_str = "sshort";
            else if (type == 9) type_str = "slong";
            else if (type == 10) type_str = "srational";
            else if (type == 11) type_str = "float";
            else if (type == 12) type_str = "double";
            else type_str = "????";

            //---------------------------------

            string tag_str = "";

            // TIFF
            if (tag == 254) tag_str = "NewSubfileType";
            else if (tag == 255) tag_str = "SubfileType";
            else if (tag == 256) tag_str = "ImageWidth";
            else if (tag == 257) tag_str = "ImageLength";
            else if (tag == 258) tag_str = "BitsPerSample";
            else if (tag == 259) tag_str = "Compression";
            else if (tag == 262) tag_str = "PhotometricInterpretation";
            else if (tag == 273) tag_str = "StripOffsets";
            else if (tag == 274) tag_str = "Orientation";
            else if (tag == 277) tag_str = "SamplesPerPixel";
            else if (tag == 278) tag_str = "RowsPerStrip";
            else if (tag == 279) tag_str = "StripByteCounts";
            else if (tag == 282) tag_str = "XResolution";
            else if (tag == 283) tag_str = "YResolution";
            else if (tag == 284) tag_str = "PlanarConfig";
            else if (tag == 296) tag_str = "ResolutionUnit";
            else if (tag == 305) tag_str = "Software";
            else if (tag == 306) tag_str = "DateTime";
            // PS
            else if (tag == 700) tag_str = "XMP";
            else if (tag == 33723) tag_str = "RichTiffIPTC";
            else if (tag == 34377) tag_str = "PhotoshopImageResrs";
            else if (tag == 34665) tag_str = "ExifIFDPhotoshop";
            else if (tag == 34675) tag_str = "ICCProfile";
            else if (tag == 37724) tag_str = "ImageSourceDataPShp";
            // DNG
            else if (tag == 50706) tag_str = "DNGVersion";
            else if (tag == 50707) tag_str = "DNGBackwardVersion";
            else if (tag == 50708) tag_str = "UniqueCameraModel";
            else if (tag == 50709) tag_str = "LocalizedCameraModel";
            else if (tag == 50710) tag_str = "CFAPlaneColor"; // Color Filter Array Plane Color
            else if (tag == 50711) tag_str = "CFALayout"; // Color Filter Array Layout
            else if (tag == 50712) tag_str = "LinearizationTable";
            else if (tag == 50713) tag_str = "BlackLevelRepeatDim";
            else if (tag == 50714) tag_str = "BlackLevel";
            else if (tag == 50715) tag_str = "BlackLevelDeltaH";
            else if (tag == 50716) tag_str = "BlackLevelDeltaV";
            else if (tag == 50717) tag_str = "WhiteLevel";
            else if (tag == 50718) tag_str = "DefaultScale";
            else if (tag == 50780) tag_str = "BestQualityScale";
            else if (tag == 50719) tag_str = "DefaultCropOrigin";
            else if (tag == 50720) tag_str = "DefaultCropSize";
            else if (tag == 50778) tag_str = "CalibrationIlluminant1";
            else if (tag == 50779) tag_str = "CalibrationIlluminant2";
            else if (tag == 50721) tag_str = "ColorMatrix1";
            else if (tag == 50722) tag_str = "ColorMatrix2";
            else if (tag == 50723) tag_str = "CameraCalibration1";
            else if (tag == 50724) tag_str = "CameraCalibration1";
            else if (tag == 50725) tag_str = "ReductionMatrix1";
            else if (tag == 50726) tag_str = "ReductionMatrix2";
            else if (tag == 50727) tag_str = "AnalogBalance";
            else if (tag == 50728) tag_str = "AsShotNeutral";
            else if (tag == 50729) tag_str = "AsShotWhiteXY";
            else if (tag == 50730) tag_str = "BaselineExposure";
            //else if (tag == 507) tag_str = "";
            //else if (tag == 507) tag_str = ""; // 太多了,以後再看文檔加
            //else if (tag == 507) tag_str = "";
            else if (tag == 50964) tag_str = "ForwardMatrix1";
            else if (tag == 50965) tag_str = "ForwardMatrix2";
            //else if (tag == 50) tag_str = "";
            //else if (tag == 50) tag_str = ""; // 太多了,以後再看文檔加
            //else if (tag == 50) tag_str = "";
            else if (tag == 51125) tag_str = "DefaultUserCrop";


            else tag_str = "????";



            //---------------------------------

            string value = "N/A";

            if (type_str == "ascii")
            {
                value = "";
                for (int k = valueOffset; k < valueOffset + length; k++)
                {
                    value += (char)b[k];
                }
            }

            //---------------------------------

            if (type_str == "rational")
            {
                int A = ByteArrayToInt(ifh.ByteOrder, new byte[] { b[valueOffset + 0], b[valueOffset + 1], b[valueOffset + 2], b[valueOffset + 3] });
                int B = ByteArrayToInt(ifh.ByteOrder, new byte[] { b[valueOffset + 4], b[valueOffset + 5], b[valueOffset + 6], b[valueOffset + 7] });
                value = (A / B) + "";
            }

            //---------------------------------
            // 預設放三個值,之後要可彈性調整

            if (tag_str == "BitsPerSample")
            {
                int A = ByteArrayToInt(ifh.ByteOrder, new byte[] { b[valueOffset + 0], b[valueOffset + 1] });
                int B = ByteArrayToInt(ifh.ByteOrder, new byte[] { b[valueOffset + 2], b[valueOffset + 3] });
                int C = ByteArrayToInt(ifh.ByteOrder, new byte[] { b[valueOffset + 4], b[valueOffset + 5] });
                value = A + ", " + B + ", " + C;

                List<int> v = new List<int>();
                v.Add(A);
                v.Add(B);
                v.Add(C);

                tiff_tag_value.BitsPerSample = v;
            }

            //---------------------------------

            if (tag_str == "PhotometricInterpretation")
            {
                // 參考 :
                // https://www.awaresystems.be/imaging/tiff/tifftags/photometricinterpretation.html

                if (valueOffset == 0) value = "MINISWHITE";
                else if (valueOffset == 1) value = "MINISBLACK";
                else if (valueOffset == 2) value = "RGB";
                else if (valueOffset == 3) value = "PALETTE";
                else if (valueOffset == 4) value = "MASK";
                else if (valueOffset == 5) value = "SEPARATED";
                else if (valueOffset == 6) value = "YCBCR";
                else if (valueOffset == 8) value = "CIELAB";
                else if (valueOffset == 9) value = "ICCLAB";
                else if (valueOffset == 10) value = "ITULAB";
                else if (valueOffset == 32844) value = "LOGL";
                else if (valueOffset == 32845) value = "LOGLUV";
                else value = "????";
            }

            //---------------------------------

            if (tag_str == "Compression")
            {
                // 參考 :
                // https://www.awaresystems.be/imaging/tiff/tifftags/compression.html


                if (valueOffset == 1) value = "NONE";
                else if (valueOffset == 2) value = "CCITTRLE";
                else if (valueOffset == 3) value = "CCITTFAX3";
                else if (valueOffset == 4) value = "CCITTFAX4";
                else if (valueOffset == 5) value = "LZW";
                else if (valueOffset == 6) value = "OJPEG";
                else if (valueOffset == 7) value = "JPEG";
                else if (valueOffset == 32766) value = "NEXT";
                else if (valueOffset == 32771) value = "CCITTRLEW";
                else if (valueOffset == 32773) value = "PACKBITS";
                else if (valueOffset == 32809) value = "THUNDERSCAN";
                else if (valueOffset == 32895) value = "IT8CTPAD";
                else value = "????";
                // 太多了...未完待續... 請參考該網址加入...
            }

            //---------------------------------

            if (tag_str == "Orientation")
            {
                // 參考 :
                // https://www.awaresystems.be/imaging/tiff/tifftags/orientation.html


                if (valueOffset == 1) value = "TOPLEFT";
                else if (valueOffset == 2) value = "TOPRIGHT";
                else if (valueOffset == 3) value = "BOTRIGHT";
                else if (valueOffset == 4) value = "BOTLEFT";
                else if (valueOffset == 5) value = "LEFTTOP";
                else if (valueOffset == 6) value = "RIGHTTOP";
                else if (valueOffset == 7) value = "RIGHTBOT";
                else if (valueOffset == 8) value = "LEFTBOT";
                else value = "????";
            }

            //---------------------------------

            if (tag_str == "StripOffsets")
            {
                tiff_tag_value.StripOffsets = valueOffset;
            }

            if (tag_str == "ImageWidth")
            {
                tiff_tag_value.ImageWidth = valueOffset;
            }

            if (tag_str == "ImageLength")
            {
                tiff_tag_value.ImageLength = valueOffset;
            }

            if (tag_str == "PhotometricInterpretation")
            {
                tiff_tag_value.PhotometricInterpretation = value;
            }

            //---------------------------------

            Debug.Log((i + 1) + ". Tag:" + tag + "[" + tag_str + "], Type:" + type + "[" + type_str + "], length:" + length + ", valueOffset:" + valueOffset + ", value:" + value);
        }

        Texture2D t = new Texture2D(tiff_tag_value.ImageWidth, tiff_tag_value.ImageLength, TextureFormat.RGB24, false);

        pixel = new uint[t.width, t.height, 3];

        if (tiff_tag_value.PhotometricInterpretation == "RGB" && tiff_tag_value.BitsPerSample[0] == 8)
        {
            // RGB 8bit
            for (int y = 0; y < t.height; y++)
            {
                for (int x = 0; x < t.width; x++)
                {
                    int i = y * t.width + x;
                    int mY = t.height - 1 - y;

                    byte R = b[tiff_tag_value.StripOffsets + i * 3 + 0];
                    byte G = b[tiff_tag_value.StripOffsets + i * 3 + 1];
                    byte B = b[tiff_tag_value.StripOffsets + i * 3 + 2];

                    t.SetPixel(x, mY, new Color(R / 255f, G / 255f, B / 255f));

                    pixel[x, mY, 0] = R;
                    pixel[x, mY, 1] = G;
                    pixel[x, mY, 2] = B;
                }
            }
        }
        else if (tiff_tag_value.PhotometricInterpretation == "RGB" && tiff_tag_value.BitsPerSample[0] == 16 && ifh.ByteOrder == "II")
        {
            // RGB 16bit II
            for (int y = 0; y < t.height; y++)
            {
                for (int x = 0; x < t.width; x++)
                {
                    int i = y * t.width + x;
                    int mY = t.height - 1 - y;

                    int RL = b[tiff_tag_value.StripOffsets + i * 6 + 0];
                    int RH = b[tiff_tag_value.StripOffsets + i * 6 + 1];

                    int GL = b[tiff_tag_value.StripOffsets + i * 6 + 2];
                    int GH = b[tiff_tag_value.StripOffsets + i * 6 + 3];

                    int BL = b[tiff_tag_value.StripOffsets + i * 6 + 4];
                    int BH = b[tiff_tag_value.StripOffsets + i * 6 + 5];

                    uint R = (uint)(RH << 8 | RL);
                    uint G = (uint)(GH << 8 | GL);
                    uint B = (uint)(BH << 8 | BL);

                    t.SetPixel(x, mY, new Color(R / 65535f, G / 65535f, B / 65535f));

                    pixel[x, mY, 0] = R;
                    pixel[x, mY, 1] = G;
                    pixel[x, mY, 2] = B;
                }
            }
        }
        else if (tiff_tag_value.PhotometricInterpretation == "RGB" && tiff_tag_value.BitsPerSample[0] == 16 && ifh.ByteOrder == "MM")
        {
            // RGB 16bit MM
            for (int y = 0; y < t.height; y++)
            {
                for (int x = 0; x < t.width; x++)
                {
                    int i = y * t.width + x;
                    int mY = t.height - 1 - y;

                    int RH = b[tiff_tag_value.StripOffsets + i * 6 + 0];
                    int RL = b[tiff_tag_value.StripOffsets + i * 6 + 1];

                    int GH = b[tiff_tag_value.StripOffsets + i * 6 + 2];
                    int GL = b[tiff_tag_value.StripOffsets + i * 6 + 3];

                    int BH = b[tiff_tag_value.StripOffsets + i * 6 + 4];
                    int BL = b[tiff_tag_value.StripOffsets + i * 6 + 5];

                    uint R = (uint)(RH << 8 | RL);
                    uint G = (uint)(GH << 8 | GL);
                    uint B = (uint)(BH << 8 | BL);

                    t.SetPixel(x, mY, new Color(R / 65535f, G / 65535f, B / 65535f));

                    pixel[x, mY, 0] = R;
                    pixel[x, mY, 1] = G;
                    pixel[x, mY, 2] = B;
                }
            }
        }



        t.Apply();

        return t;
    }

    // Image File Header (IFH)
    struct IFH
    {
        public string ByteOrder;
        public int Version;
        public bool IsTIFF;
        public int OffsetToFirstIFD;
    }

    // Image File Directory (IFD)
    struct IFD
    {
        public int DirectoryEntryCount;
        public DE[] DirectoryEntry;
        public int OffsetToNextIFD;
    }

    // Directory Entry (DE)
    struct DE
    {
        public int Tag;
        public int Type;
        public int Length;
        public int ValueOffset;
        public object value;
    }

    struct TIFF_TAG_VALUE
    {
        public int NewSubfileType;
        public int SubfileType;
        public int ImageWidth;
        public int ImageLength;
        public List<int> BitsPerSample;
        public int Compression;
        public string PhotometricInterpretation;
        public int StripOffsets;
        public int Orientation;
        public int SamplesPerPixel;
        public int RowsPerStrip;
        public int StripByteCounts;
        public int XResolution;
        public int YResolution;
        public int PlanarConfig;
        public int ResolutionUnit;
        public string Software;
        public string DateTime;
    }
}

參考 :

DNG格式解析 : https://www.cnblogs.com/adong7639/p/4446828.html
DCRAW ( 主流 RAW 解析,只用一萬行程式碼 ) : http://www.cybercom.net/~dcoffin/dcraw/
TIFF Revision 6.0基本TIFF部分翻译 : https://wenku.baidu.com/view/37404d9602d276a200292e8f.html
Adobe 官方 DNG 格式 : https://www.adobe.com/content/dam/acom/en/products/photoshop/pdfs/dng_spec_1.4.0.0.pdf
【文件格式-TIFF】TIFF图像格式结构 : https://blog.csdn.net/chenlu5201314/article/details/56276903

猜你喜欢

转载自blog.csdn.net/weixin_38884324/article/details/81904939
今日推荐