版权声明:本文为博主 ( 黃彥霖 ) 原创文章,未经博主允许不得转载。 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