Image

1 CFLImage

1.1 CFLImage 개요

FLImaging® 에는 기본적으로 이미지 객체(CFLImage)가 있습니다. 이 이미지 객체에 이미지 파일을 로드하거나, 알고리즘을 이 이미지에 적용하거나, 변경된 이미지를 파일로 저장하는 등 여러 기능을 사용할 수 있습니다.

CFLImage object
Fig. CFLImage object

1.2 CFLImage 제공 기능

이외에도 개발에 필요한 풍부한 기능을 제공합니다.

2 이미지 로드하기

2.1 새 이미지 로드

먼저 이미지 객체를 선언하고, 선언한 객체에 Load() 함수를 호출해서 이미지를 로드할 수 있습니다.

Load Image
Fig. CFLImage Load
// Declare an image object
CFLImage fli;
// Load an image
CResult res = fli.Load(L"D:\\ExampleImage.png");
// Declare an image object
CFLImage fli = new CFLImage();
// Load an image
CResult res = fli.Load("D:\\ExampleImage.png");
# Declare an image object
fli = CFLImage()
# Load an image
res = fli.Load("D:\\ExampleImage.png")

2.2 기존 이미지의 맨 앞장에 추가로 로드

로드한 이미지의 맨 앞장에 새 이미지를 로드할 수 있습니다.

LoadFront
Fig. CFLImage LoadFront
// Insert an image file into the front page
CResult res = fli.LoadFront(L"D:\\ImageToPushFront.png");
// Insert an image file into the front page
CResult res = fli.LoadFront("D:\\ImageToPushFront.png");
# Insert an image file into the front page
res = fli.LoadFront("D:\\ImageToPushFront.png")

2.3 기존 이미지의 맨 뒷장에 추가로 로드

로드한 이미지의 맨 뒷장에 새 이미지를 로드할 수 있습니다.

LoadBack
Fig. CFLImage LoadBack
// Insert an image file into the last page
CResult res = fli.LoadBack(L"D:\\ImageToPushBack.png");
// Insert an image file into the last page
CResult res = fli.LoadBack("D:\\ImageToPushBack.png");
# Insert an image file into the last page
res = fli.LoadBack("D:\\ImageToPushBack.png")

2.4 기존 이미지의 특정 인덱스에 로드하여 삽입

로드한 이미지의 원하는 페이지에 새 이미지를 로드해서 삽입할 수 있습니다. 아래는 1번 인덱스 위치에 이미지를 삽입하는 예제입니다.

LoadInsert
Fig. CFLImage LoadInsert
// Load an image file to the desired page index
CResult res = fli.LoadInsert(L"D:\\ImageToInsert.png", 1);
// Load an image file to the desired page index
CResult res = fli.LoadInsert("D:\\ImageToInsert.png", 1);
# Load an image file to the desired page index
res = fli.LoadInsert("D:\\ImageToInsert.png", 1)

2.5 Raw 이미지 로드

Raw 이미지를 로드하려면 이미지의 픽셀 정보, 이미지의 너비와 높이 정보 등이 추가로 필요합니다. 추가 정보를 전달하며 raw 이미지를 로드하는 예제입니다.

// Declare an image object
CFLImage fli;
// Set parameters
int32_t i32Width = 2660; // image width
int32_t i32Height = 2560; // image height
EPixelFormat ePixelFormat = EPixelFormat_C1_U8;
int32_t i32AlignByte = 4; // The alignment byte
int64_t i64OffsetFromBegin = 0; // The offset from the beginning of the data.
int64_t i64OffsetFromEnd = 0; // The offset from the end of the data, starting from the end of the file.

// Load a raw image
CResult res = fli.LoadRaw(L"D:\\ExampleRawImage.raw", i32Width, i32Height, ePixelFormat, i32AlignByte, i64OffsetFromBegin, i64OffsetFromEnd);
// Declare an image object
CFLImage fli = new CFLImage();
// Set parameters
int i32Width = 2660; // image width
int i32Height = 2560; // image height
EPixelFormat ePixelFormat = EPixelFormat.C1_U8;
int i32AlignByte = 4; // The alignment byte
long i64OffsetFromBegin = 0; // The offset from the beginning of the data.
long i64OffsetFromEnd = 0; // The offset from the end of the data, starting from the end of the file.

// Load a raw image
CResult res = fli.LoadRaw("D:\\ExampleRawImage.raw", i32Width, i32Height, ePixelFormat, i32AlignByte, i64OffsetFromBegin, i64OffsetFromEnd);
# Declare an image object
fli = CFLImage()
# Set parameters
i32Width = 2660 # image width
i32Height = 2560 # image height
ePixelFormat = EPixelFormat.C1_U8
i32AlignByte = 4 # The alignment byte
i64OffsetFromBegin = 0 # The offset from the beginning of the data.
i64OffsetFromEnd = 0 # The offset from the end of the data, starting from the end of the file.

# Load a raw image
res = fli.LoadRaw("D:\\ExampleRawImage.raw", i32Width, i32Height, ePixelFormat, i32AlignByte, i64OffsetFromBegin, i64OffsetFromEnd)

3 이미지 버퍼에 접근하기

3.1 이미지 버퍼 얻어 오기

이미지 버퍼를 얻고 싶다면 아래와 같이 버퍼의 시작 위치 포인터를 얻어 올 수 있습니다.

CFLImage fli;
uint8_t* pU8Buffer = fli.GetBuffer();
CFLImage fli = new CFLImage();

var buffer = fli.GetBuffer(); 

// 만약 byte[]로 복사해서 쓰고 싶다면:
byte[] managedBuffer = new byte[buffer.Length];
buffer.CopyTo(managedBuffer, 0);
fli = CFLImage()
# 반환값: System.Array[System.Byte]
arrayBuff = fli.GetBuffer()

`CFLImage::GetBuffer()`는 `uint8_t*`를 리턴하므로, 이미지의 픽셀 값 타입에 따라 버퍼 포인터를 다른 자료형으로 캐스팅해서 사용합니다.
// CFLImage 객체 생성
CFLImage fli;

// 이미지 로드
CResult res = fli.Load(L"D:/C4_F32_Image.flif");

// 이미지 버퍼 포인터 얻기
uint8_t* pU8Buffer = fli.GetBuffer();

// 이미지 depth 얻기
int32_t i32Depth = fli.GetDepth();

// 이미지의 value type 에 따른 분기
// 이미지의 픽셀 값 타입에 따라 버퍼 포인터를 다른 자료형으로 캐스팅해서 사용
switch(fli.GetValueType())
{
case EValueType_FloatingPoint:
	{
		if(i32Depth == 64)
		{
			double* pF64Buffer = (double*)pU8Buffer;
			// do something...
		}
		else if(i32Depth == 32)
		{
			float* pF32Buffer = (float*)pU8Buffer;
			// do something...
		}
	}
	break;
case EValueType_Signed:
	{
		if(i32Depth == 64)
		{
			int64_t* pI64Buffer = (int64_t*)pU8Buffer;
			// do something...
		}
		else if(i32Depth == 32)
		{
			int32_t* pI32Buffer = (int32_t*)pU8Buffer;
			// do something...
		}
		else if(i32Depth > 8)
		{
			int16_t* pI16Buffer = (int16_t*)pU8Buffer;
			// do something...
		}
		else if(i32Depth == 8)
		{
			int8_t* pI8Buffer = (int8_t*)pU8Buffer;
			// do something...
		}
	}
	break;
case EValueType_Unsigned:
	{
		if(i32Depth == 64)
		{
			uint64_t* pU64Buffer = (uint64_t*)pU8Buffer;
			// do something...
		}
		else if(i32Depth == 32)
		{
			uint32_t* pU32Buffer = (uint32_t*)pU8Buffer;
			// do something...
		}
		else if(i32Depth > 8)
		{
			uint16_t* pU16Buffer = (uint16_t*)pU8Buffer;
			// do something...
		}
		else if(i32Depth == 8)
		{
			// do something with uint8_t* pU8Buffer
		}
	}
	break;
}
// CFLImage 객체 생성
CFLImage fli = new CFLImage();

// 이미지 로드
res = fli.Load("D:/C4_F32_Image.flif");

// 이미지 버퍼 얻기
byte[] buffer = fli.GetBuffer();
int depth = fli.GetDepth(); // 비트 단위 (ex: 32, 64)
int byteDepth = depth / 8;
EValueType valueType = fli.GetValueType();

long i64Width = fli.GetWidth();
long i64Height = fli.GetHeight();
int i32Channels = fli.GetChannels();

long i64PixelCount = i64Width * i64Height * i32Channels;

for(long y = 0; y < i64Height; y++)
{
  for(long x = 0; x < i64Width; x++)
  {
    for(int c = 0; c < i32Channels; c++)
    {
      int i = (int)((y * i64Width) + x) * i32Channels + c;
      int offset = i * byteDepth;

      // 이미지의 value type 에 따른 분기
      // 이미지의 픽셀 값 타입에 따라 버퍼 포인터를 다른 자료형으로 캐스팅해서 사용
      switch(valueType)
      {
      case EValueType.FloatingPoint:
        if(depth == 32)
        {
          // float (4 bytes)
          float val = BitConverter.ToSingle(buffer, offset);
          Console.WriteLine($"[{x}, {y}] Ch{c} = {val}");
        }
        else if(depth == 64)
        {
          // double (8 bytes)
          double val = BitConverter.ToDouble(buffer, offset);
          Console.WriteLine($"[{x}, {y}] Ch{c} = {val}");
        }
        break;

      case EValueType.Signed:
        if(depth == 8)
        {
          sbyte val = (sbyte)Marshal.ReadByte(buffer, offset);
          Console.WriteLine($"[{x}, {y}] Ch{c} = {val}");
        }
        else if(depth == 16)
        {
          short val = Marshal.ReadInt16(buffer, offset);
          Console.WriteLine($"[{x}, {y}] Ch{c} = {val}");
        }
        else if(depth == 32)
        {
          int val = Marshal.ReadInt32(buffer, offset);
          Console.WriteLine($"[{x}, {y}] Ch{c} = {val}");
        }
        else if(depth == 64)
        {
          long val = Marshal.ReadInt64(buffer, offset);
          Console.WriteLine($"[{x}, {y}] Ch{c} = {val}");
        }
        break;

      case EValueType.Unsigned:
        if(depth == 8)
        {
          byte val = Marshal.ReadByte(buffer, offset);
          Console.WriteLine($"[{x}, {y}] Ch{c} = {val}");
        }
        else if(depth == 16)
        {
          ushort val = (ushort)Marshal.ReadInt16(buffer, offset);
          Console.WriteLine($"[{x}, {y}] Ch{c} = {val}");
        }
        else if(depth == 32)
        {
          uint val = (uint)Marshal.ReadInt32(buffer, offset);
          Console.WriteLine($"[{x}, {y}] Ch{c} = {val}");
        }
        else if(depth == 64)
        {
          ulong val = (ulong)Marshal.ReadInt64(buffer, offset);
          Console.WriteLine($"[{x}, {y}] Ch{c} = {val}");
        }
        break;
      }
    }
  }
}
import struct

# CFLImage 객체 생성
fli = CFLImage()

# 이미지 로드
res = fli.Load("D:/C4_F32_Image.flif")

# 기본 정보
buffer = fli.GetBuffer()
depth = fli.GetDepth()  # ex: 32, 64
byte_depth = depth // 8
value_type = fli.GetValueType()

width = fli.GetWidth()
height = fli.GetHeight()
channels = fli.GetChannels()

# Pixel 순회
for y in range(height):
  for x in range(width):
    for c in range(channels):
      i = ((y * width) + x) * channels + c
      offset = i * byte_depth
    
      # 바이트 슬라이스
      # 명시적으로 바이트 배열로 변환
      b = bytes([buffer[offset + i] for i in range(byte_depth)])
    
      # 값 해석
      if value_type == EValueType.FloatingPoint:
        if depth == 32:
          val = struct.unpack('<f', b)[0]
        elif depth == 64:
          val = struct.unpack('<d', b)[0]
        else:
          continue
      elif value_type == EValueType.Signed:
        if depth == 8:
          val = struct.unpack('<b', b)[0]
        elif depth == 16:
          val = struct.unpack('<h', b)[0]
        elif depth == 32:
          val = struct.unpack('<i', b)[0]
        elif depth == 64:
          val = struct.unpack('<q', b)[0]
        else:
          continue
      elif value_type == EValueType.Unsigned:
        if depth == 8:
          val = struct.unpack('<B', b)[0]
        elif depth == 16:
          val = struct.unpack('<H', b)[0]
        elif depth == 32:
          val = struct.unpack('<I', b)[0]
        elif depth == 64:
          val = struct.unpack('<Q', b)[0]
        else:
          continue
      else:
        continue
    
      print(f"[{x}, {y}] Ch{c} = {val}")

3.2 이미지 버퍼의 픽셀 값을 얻어 오거나 변경하기

아래 코드는 반복문을 통해 이미지의 모든 픽셀 값에 0.5를 곱하는 예제를 보여 줍니다. 이 예제를 통해 이미지의 버퍼에 접근해서 값을 얻어 오거나 변경하는 방법을 익혀 보세요.

CFLImage fli;
res = fli.Load(L"D:/C4_F32_Image.flif");
uint8_t* pU8Buffer = fli.GetBuffer();
int32_t i32Depth = fli.GetDepth();
int64_t i64W = fli.GetWidth(); // 이미지의 너비 (픽셀 단위)
int64_t i64H = fli.GetHeight(); // 이미지의 높이 (픽셀 단위)
int64_t i64WidthStepByte = fli.GetWidthStepByte(); // 한 줄(row)을 저장하는 데 필요한 바이트 수

switch(fli.GetValueType())
{
case EValueType_FloatingPoint:
    {
        if(i32Depth == 64)
        {
            for(int64_t y = 0; y < i64H; ++y)
            {
                double* pF64Buffer = (double*)pU8Buffer;

                for(int64_t x = 0; x < i64W; ++x)
                {
                    double f64PixelValue = *(pF64Buffer + x);
                    *(pF64Buffer + x) = f64PixelValue * 0.5;
                    // do something...
                }
                // 한 줄 내리기
                pU8Buffer += i64WidthStepByte;
            }
        }
        else if(i32Depth == 32)
        {
            for(int64_t y = 0; y < i64H; ++y)
            {
                float* pF32Buffer = (float*)pU8Buffer;

                for(int64_t x = 0; x < i64W; ++x)
                {
                    float f32PixelValue = *(pF32Buffer + x);
                    *(pF32Buffer + x) = f32PixelValue * 0.5f;
                    // do something...
                }
                // 한 줄 내리기
                pU8Buffer += i64WidthStepByte;
            }
        }
    }
    break;
    // and so on...
}
CFLImage fli = new CFLImage();
res = fli.Load("D:/C4_F32_Image.flif");

// 이미지 버퍼 얻어 오기
IntPtr pBuffer = fli.GetBufferPtr();
int depth = fli.GetDepth(); // 비트 단위 (ex: 32, 64)
int byteDepth = depth / 8;
EValueType valueType = fli.GetValueType();

long i64Width = fli.GetWidth();   // 이미지 너비
long i64Height = fli.GetHeight(); // 이미지 높이
int i32Channels = fli.GetChannels(); // 이미지 채널

long i64PixelCount = i64Width * i64Height * i32Channels;
byte[] buffer = new byte[4]; // float = 4바이트

// 버퍼에 직접 값 쓰기 (Marshal.WriteByte)
for(int y = 0; y < i64Height; y++)
{
  for(int x = 0; x < i64Width; x++)
  {
    for(int c = 0; c < i32Channels; c++)
    {
      int i = (int)((y * i64Width) + x) * i32Channels + c;
      int offset = i * byteDepth;

      // float (4 bytes)
      Marshal.Copy(pBuffer + offset, buffer, 0, 4);

      // x 번째 픽셀값에 0.5를 곱하여 어둡게 만들기
      byte[] newBytes = BitConverter.GetBytes(BitConverter.ToSingle(buffer, 0) * 0.5f);
      Marshal.Copy(newBytes, 0, pBuffer + offset, 4);
    }
  }
}
# 이미지 로드
fli = CFLImage()
res = fli.Load("D:/C4_F32_Image.flif")

# 기본 정보
pBuffer = fli.GetBufferPtr()  # IntPtr
depth = fli.GetDepth()        # 32 (float)
byte_depth = depth // 8       # 4
value_type = fli.GetValueType()

width = fli.GetWidth()
height = fli.GetHeight()
channels = fli.GetChannels()

# 전체 픽셀 순회
for y in range(height):
  for x in range(width):
    for c in range(channels):
      i = ((y * width) + x) * channels + c
      offset = i * byte_depth

      # 버퍼에서 float32 4바이트 읽기
      raw_ptr = int(pBuffer.ToInt64()) + offset
      float_bytes = ctypes.string_at(raw_ptr, 4)
      value = struct.unpack('<f', float_bytes)[0]

      # 0.5 곱하기
      new_value = value * 0.5
      new_bytes = struct.pack('<f', new_value)

      # 다시 버퍼에 쓰기
      ctypes.memmove(raw_ptr, new_bytes, 4)

Width Step Byte 란?

이미지 처리에서 width step (또는 stride)과 byte에 대한 개념은 주로 컴퓨터 비전이나 그래픽스에서 다루는 내용입니다. 이는 이미지 데이터를 메모리에서 어떻게 저장하고 처리하는지와 관련이 있습니다.

Byte와 Width Step

이미지의 메모리 구조는 다음과 같은 방식으로 정의됩니다:

3.3 Y-Offset Table, X-Offset Table 이용하기

위에서 확인한 예제에서는 width step byte와 버퍼 casting을 이용하여 각 줄의 시작 위치와 각 픽셀의 시작 위치를 알 수 있었습니다. 그러나 y-offset table, x-offset table을 이용하면 각 줄의 시작 주소와 각 픽셀 값의 시작 위치에 바로 접근할 수 있습니다.

CFLImage fli;
// 4채널 32비트 floating point 이미지 로드
fli.Load(L"D:\\C4_F32_Image.png");

// 각 줄의 시작 주소 테이블
uint8_t** pU8YOffsetTable = fli.GetYOffsetTable();
// 각 픽셀의 시작 위치 오프셋 테이블
int64_t* pU64XOffsetTable = fli.GetXOffsetTable();
// 이미지 버퍼
uint8_t* pU8Buffer = fli.GetBuffer();

for(int64_t y = 0; y < fli.GetHeight(); ++y)
{
	// YOffsetTable을 통해 y 번째 줄의 시작 주소를 얻기
	uint8_t* pU8CurrY = pU8YOffsetTable[y];

	for(int64_t x = 0; x < fli.GetWidth(); ++x)
	{
		// XOffsetTable을 통해 x 번째 픽셀의 시작 주소를 얻기
    // 로드한 이미지는 f32 타입의 이미지이므로 float으로 캐스팅
		float* pF32CurrPixelValue = (float*)(pU8CurrY + pU64XOffsetTable[x]);

    // 로드한 이미지는 4채널 이미지이므로
		// x 번째 픽셀의 0, 1, 2 채널 값에 0.5를 곱하여 어둡게 만들기
		pF32CurrPixelValue[0] = pF32CurrPixelValue[0] * 0.5f;
		pF32CurrPixelValue[1] = pF32CurrPixelValue[1] * 0.5f;
		pF32CurrPixelValue[2] = pF32CurrPixelValue[2] * 0.5f;
	}
}
// 현재 C#에서 지원하지 않는 기능입니다.
# 현재 Python에서 지원하지 않는 기능입니다.

위 예제 코드를 실행하면, 아래와 같이 원본 이미지보다 어두워진 이미지를 확인할 수 있습니다.

Original Image
Fig. Original Image
Modified Image
Fig. Modified Image

4 이미지 정리, 페이지 삭제하기

이미지를 클리어하려면 CFLImage::Clear()함수를 사용하면 간단합니다. 이 외에도, CFLImage 클래스는 사용자의 필요에 따라 세분화된 클리어 기능들을 제공합니다. 튜토리얼을 따라 여러 가지 이미지 정리 기능을 익혀보세요.

4.1 페이지 버퍼 클리어하기(ClearPage)

우선 아래와 같이 총 6장의 페이지가 있는 이미지를 CFLImage 객체에 로드했다고 가정합니다.

Original Image
Fig. Original Image

여기서 2번 페이지의 버퍼 및 정보를 클리어하려면, ClearPage() 함수를 이용해 페이지 버퍼 및 정보를 클리어할 수 있습니다.

// CFLImage 객체 생성
CFLImage fli;

// 멀티페이지 이미지 로드
if (fli.Load(L"multi_page_image.flif").IsFail())
    return;
  
fli.ClearPage(2); // 2번 페이지 버퍼 및 정보를 클리어
// CFLImage 객체 생성
CFLImage fli = new CFLImage();

// 멀티페이지 이미지 로드
if (fli.Load("multi_page_image.flif").IsFail())
    return;
  
fli.ClearPage(2); // 2번 페이지 버퍼 및 정보를 클리어
# CFLImage 객체 생성
fli = CFLImage()

# 멀티페이지 이미지 로드
if fli.Load("multi_page_image.flif").IsFail():
  return
  
fli.ClearPage(2) # 2번 페이지 버퍼 및 정보를 클리어

위 예제 코드를 실행하면, 아래와 같이 2번 페이지가 빈 버퍼가 되는 결과가 나옵니다.
Original Image
Fig. Original Image
Clear Page
Fig. Clear Page

4.2 페이지 삭제하기(RemovePage)

2번 페이지를 삭제하려면, RemovePage 함수를 사용하면 됩니다.

// CFLImage 객체 생성
CFLImage fli;

// 멀티페이지 이미지 로드
if (fli.Load(L"multi_page_image.flif").IsFail())
    return;
  
fli.RemovePage(2); // 2번 페이지를 제거
// CFLImage 객체 생성
CFLImage fli = new CFLImage();

// 멀티페이지 이미지 로드
if (fli.Load("multi_page_image.flif").IsFail())
    return;
  
fli.RemovePage(2); // 2번 페이지를 제거
# CFLImage 객체 생성
fli = CFLImage()

# 멀티페이지 이미지 로드
if fli.Load("multi_page_image.flif").IsFail():
  return
  
# 2번 페이지를 제거
fli.RemovePage(2) 

위 예제 코드를 실행하면, 아래와 같이 2번 페이지가 이미지 객체에서 완전히 삭제되어 총 5페이지가 됩니다.
Original Image
Fig. Original Image
Remove Page
Fig. Remove Page

4.3 여러 페이지 삭제하기(RemovePages)

여러 페이지를 삭제하려면, 삭제할 인덱스를 Base::CFLArray<int32_t> 객체에 담아서 RemovePages() 함수에 전달하면 됩니다.

// CFLImage 객체 생성
CFLImage fli;

// 멀티페이지 이미지 로드
if (fli.Load(L"multi_page_image.flif").IsFail())
    return;

// 제거할 페이지 인덱스 지정 (0-based 인덱스)
CFLArray<int32_t> flaPageIndex;
flaPageIndex.PushBack(2);
flaPageIndex.PushBack(3);
flaPageIndex.PushBack(4);

// RemovePages 호출
fli.RemovePages(flaPageIndex); // 2, 3, 4번 페이지를 제거
// CFLImage 객체 생성
CFLImage fli = new CFLImage();

// 멀티페이지 이미지 로드
if(fli.Load("multi_page_image.flif").IsFail())
  return;

// 제거할 페이지 인덱스 지정 (0-based 인덱스)
List<int> pageIndicesToRemove = new List<int> { 2, 3, 4 }; // 2, 3, 4번 페이지를 제거

// RemovePages 호출
fli.RemovePages(pageIndicesToRemove);
# CFLImage 객체 생성
fli = CFLImage()

# 멀티페이지 이미지 로드
if fli.Load("multi_page_image.flif").IsFail():
  return

# 제거할 페이지 인덱스 지정 (0-based 인덱스)
indices_to_remove = List[int]()
# 2, 3, 4번 페이지를 제거
indices_to_remove.Add(2)
indices_to_remove.Add(3)
indices_to_remove.Add(4)  

# RemovePages 호출
fli.RemovePages(indices_to_remove)

위 예제 코드를 실행하면, 아래와 같이 2, 3, 4번 페이지가 이미지 객체에서 완전히 삭제되어 총 3페이지가 됩니다.
Original Image
Fig. Original Image
Remove Pages
Fig. Remove Pages

5 이미지 생성하기

이미지 버퍼를 생성하는 방법을 익혀 봅시다.

5.1 이미지 생성하기 - 단일 값

CFLImage 객체를 선언하고, 이 객체에 원하는 크기와 포맷으로 이미지를 생성하는 방법을 익혀 봅시다. 모든 픽셀 값이 222인 이미지를 생성하는 단순한 예제입니다.

// CFLImage 객체 생성
CFLImage fli;
int32_t i32W = 128; // 이미지 너비
int32_t i32H = 128; // 이미지 높이

// 1채널 uint8 포맷으로 이미지를 생성 및 모든 픽셀 값을 222로 채우기
fli.Create(i32W, i32H, CMultiVarUL(222), EPixelFormat_C1_U8);
// CFLImage 객체 생성
CFLImage fli = new CFLImage();
int i32W = 128; // 이미지 너비
int i32H = 128; // 이미지 높이

// 1채널 uint8 포맷으로 이미지를 생성 및 모든 픽셀 값을 222로 채우기
fli.Create(i32W, i32H, new CMultiVar<double>(222), EPixelFormat.C1_U8);
# CFLImage 객체 생성
fli = CFLImage()

i32W = 128 # 이미지 너비
i32H = 128 # 이미지 높이

# 1채널 uint8 포맷으로 이미지를 생성 및 모든 픽셀 값을 222로 채우기
fli.Create(i32W, i32H, CMultiVar[Double](222), EPixelFormat.C1_U8)

위 예제 코드를 실행하면 아래와 같이 모든 픽셀 값이 222인 128*128 크기의 1채널 8비트 이미지가 생성됩니다.

Create Image
Fig. Create image by MultiVar

5.2 이미지 생성하기 - 버퍼

CFLImage 객체를 선언하고, 이 객체에 특정 버퍼를 전달하여 3채널 8비트 이미지를 생성하는 방법을 익혀 봅시다.

// CFLImage 객체 생성
CFLImage fli;
int32_t i32W = 128; // 이미지 너비
int32_t i32H = 128; // 이미지 높이
int32_t i32Channel = 3; // 이미지 채널
uint8_t* pU8Buffer = new uint8_t[i32W * i32H * i32Channel];

for(int32_t y = 0; y < i32H; ++y)
{
    uint8_t* pU8CurrBuffer = pU8Buffer + y * i32W * i32Channel;

    for(int32_t x = 0; x < i32W; ++x)
    {
        float f32Ratio = (float)x / (float)i32W;
        *pU8CurrBuffer = (uint8_t)(f32Ratio * 255.f); // Blue
        *(pU8CurrBuffer + 1) = 200; // Green
        *(pU8CurrBuffer + 2) = 255 - (uint8_t)(f32Ratio * 255.f); // Red

        pU8CurrBuffer += 3;
    }
}

// 이미지 생성
fli.Create(i32W, i32H, pU8Buffer, EPixelFormat_C3_U8);
// CFLImage 객체 생성
CFLImage fli = new CFLImage();
int i32W = 128; // 이미지 너비
int i32H = 128; // 이미지 높이
int i32Channel = 3; // 이미지 채널

// unmanaged 메모리 할당 (IntPtr로 받음)
IntPtr ptr = Marshal.AllocHGlobal(i32W * i32H * i32Channel);

// 버퍼에 직접 값 쓰기 (Marshal.WriteByte)
for(int y = 0; y < i32H; y++)
{
  for(int x = 0; x < i32W; x++)
  {
    float ratio = (float)x / i32W;
    int offset = (y * i32W + x) * i32Channel;

    Marshal.WriteByte(ptr, offset, (byte)(ratio * 255));              // Blue
    Marshal.WriteByte(ptr, offset + 1, 200);                          // Green
    Marshal.WriteByte(ptr, offset + 2, (byte)(255 - ratio * 255));    // Red
  }
}

// 이미지 생성
fli.Create(i32W, i32H, ref ptr, EPixelFormat.C3_U8);

// 해제
Marshal.FreeHGlobal(ptr);
import ctypes

# CFLImage 객체 생성
fli = CFLImage()

# 이미지 설정
i32W = 128
i32H = 128
i32Channels = 3

# ctypes 버퍼 생성 (연속 메모리 할당)
buffer = (ctypes.c_ubyte * (i32W * i32H * i32Channels))()

# 버퍼 데이터 채우기 (BGR 순서)
for y in range(i32H):
  for x in range(i32W):
    ratio = x / i32W
    offset = (y * i32W + x) * i32Channels
    buffer[offset] = int(ratio * 255)        # Blue
    buffer[offset + 1] = 200                 # Green
    buffer[offset + 2] = 255 - int(ratio * 255)  # Red

# ctypes 포인터 → System.IntPtr
ptr_value = ctypes.cast(buffer, ctypes.c_void_p).value
intptr = IntPtr(ptr_value)

# 이미지 생성
fli.Create(Int64(i32W), Int64(i32H), intptr, EPixelFormat.C3_U8)

위 예제 코드를 실행하면 아래와 같이 미리 정의된 버퍼를 이용해서 이미지가 생성된 것을 확인할 수 있습니다.

Create Image
Fig. Create image by buffer

5.3 페이지 생성하기

CFLImage 객체에 빈 페이지를 생성하는 방법을 익혀 봅시다.

// CFLImage 객체 생성
CFLImage fli;

// 멀티페이지 이미지 로드
if (fli.Load(L"multi_page_image.flif").IsFail())
    return;

// 페이지 생성
fli.CreatePage(1); // insert index = 1
// CFLImage 객체 생성
CFLImage fli = new CFLImage();

// 멀티페이지 이미지 로드
if(fli.Load("multi_page_image.flif").IsFail())
  return;

// 페이지 생성
fli.CreatePage(1); // insert index = 1
# CFLImage 객체 생성
fli = CFLImage()

# 멀티페이지 이미지 로드
if fli.Load("multi_page_image.flif").IsFail():
  return

# 페이지 생성
fli.CreatePage(1) # insert index = 1

위 예제 코드를 실행하면 아래와 같이 1번째 인덱스에 빈 페이지가 생성된 것을 확인할 수 있습니다.

Original Image
Fig. Original Image
Create Page
Fig. Create Page

5.4 페이지에 이미지 버퍼 생성

CFLImage 객체의 특정 페이지에 버퍼를 생성하는 방법을 익혀 봅시다.

// CFLImage 객체 생성
CFLImage fli;

// 멀티페이지 이미지 로드
if (fli.Load(L"multi_page_image.flif").IsFail())
    return;

// 페이지 생성
fli.CreatePage(1); // insert index = 1

// 1번 페이지 선택
int32_t i32PageIndex = 1;
fli.SelectPage(i32PageIndex);

int32_t i32W = 128; // 이미지 너비
int32_t i32H = 128; // 이미지 높이

// 1채널 uint8 포맷으로 이미지를 생성 및 모든 픽셀 값을 222로 채우기
fli.Create(i32W, i32H, CMultiVarUL(222), EPixelFormat_C1_U8);
// CFLImage 객체 생성
CFLImage fli = new CFLImage();

// 멀티페이지 이미지 로드
if(fli.Load("multi_page_image.flif").IsFail())
  return;

// 페이지 생성
fli.CreatePage(1); // insert index = 1

// 1번 페이지 선택
int i32PageIndex = 1;
fli.SelectPage(i32PageIndex);

int i32W = 128; // 이미지 너비
int i32H = 128; // 이미지 높이

// 1채널 uint8 포맷으로 이미지를 생성 및 모든 픽셀 값을 222로 채우기
fli.Create(i32W, i32H, new CMultiVar<double>(222), EPixelFormat.C1_U8);
# CFLImage 객체 생성
fli = CFLImage()

# 멀티페이지 이미지 로드
if fli.Load("multi_page_image.flif").IsFail():
  return

# 페이지 생성
fli.CreatePage(1) # insert index = 1

# 1번 페이지 선택
i32PageIndex = 1
fli.SelectPage(i32PageIndex)

i32W = 128 # 이미지 너비
i32H = 128 # 이미지 높이

# 1채널 uint8 포맷으로 이미지를 생성 및 모든 픽셀 값을 222로 채우기
fli.Create(i32W, i32H, CMultiVar[Double](222), EPixelFormat.C1_U8)

위 예제 코드를 실행하면 아래와 같이 1번째 인덱스 페이지에 이미지 버퍼가 생성된 것을 확인할 수 있습니다. 우선 페이지를 선택하고, 5.1 이미지 생성하기 섹션에서 사용한 `CFLImage::Create()` 함수를 호출하면 선택한 페이지에 이미지 버퍼가 생성됩니다.
Original Image
Fig. Original Image
Create Page
Fig. Create buffer on page

6 이미지에 도형과 레이블 삽입, 삭제, 변경

6.1 이미지에 도형과 레이블 추가

이미지 안에 도형과 레이블을 추가할 수 있습니다.

// CFLImage 객체 생성
CFLImage fli;

// 이미지 로드
if (fli.Load(L"D://Image.png").IsFail())
    return;

// 도형 정의(사각형)
CFLRect<int32_t> flrB(169, 113, 240, 196);
flrB.SetName(L"B"); // label 설정
CFLRect<int32_t> flrC(50, 283, 121, 365);
flrC.SetName(L"C"); // label 설정

// 이미지에 도형과 label 추가
fli.PushBackFigure(CFigureUtilities::ConvertFigureObjectToString(flrB));
fli.PushBackFigure(CFigureUtilities::ConvertFigureObjectToString(flrC));
// CFLImage 객체 생성
CFLImage fli = new CFLImage();

// 이미지 로드
if(fli.Load("D://Image.png").IsFail())
  return;

// 도형 정의(사각형)
CFLRect<int> flrB = new CFLRect<int>(169, 113, 240, 196);
flrB.SetName("B"); // label 설정
CFLRect<int> flrC = new CFLRect<int>(50, 283, 121, 365);
flrC.SetName("C"); // label 설정

// 이미지에 도형과 label 추가
fli.PushBackFigure(CFigureUtilities.ConvertFigureObjectToString(flrB));
fli.PushBackFigure(CFigureUtilities.ConvertFigureObjectToString(flrC));
# CFLImage 객체 생성
fli = CFLImage()

# 멀티페이지 이미지 로드
if fli.Load("D://Image.png").IsFail():
  return

# 도형 정의(사각형)
flrB = CFLRect[Int32](169, 113, 240, 196)
flrB.SetName("B") # label 설정
flrC = CFLRect[Int32](50, 283, 121, 365)
flrC.SetName("C") # label 설정

# 이미지에 도형과 label 추가
fli.PushBackFigure(CFigureUtilities.ConvertFigureObjectToString(flrB))
fli.PushBackFigure(CFigureUtilities.ConvertFigureObjectToString(flrC))

위 예제 코드와 같이 도형 객체를 선언, 정의하고 이름을 설정한 뒤(optional) CFigureUtilities::ConvertFigureObjectToString() 을 통해 문자열로 바꾸어서 이미지에 도형과 레이블을 추가할 수 있습니다.

Original Image
Fig. Original Image
Push Back Figure
Fig. Push Back Figure(B,C)

특정 인덱스에 도형을 삽입할 수 있습니다. 아래 예제 코드는 0번 인덱스에 도형과 레이블을 추가하는 방법을 보여 줍니다.

// 도형 정의(사각형)
CFLRect<int32_t> flrA(72, 46, 143, 128);
flrA.SetName(L"A");// label 설정

// 0번 인덱스에 도형과 label 추가
fli.InsertFigureAt(0, CFigureUtilities::ConvertFigureObjectToString(flrA));
// 도형 정의(사각형)
CFLRect<int> flrA = new CFLRect<int>(72, 46, 143, 128);
flrA.SetName("A"); // label 설정

// 0번 인덱스에 도형과 label 추가
fli.InsertFigureAt(0, CFigureUtilities.ConvertFigureObjectToString(flrA));
# 도형 정의(사각형)
flrA = CFLRect[Int32](72, 46, 143, 128)
flrA.SetName("A") # label 설정

# 0번 인덱스에 도형과 label 추가
fli.InsertFigureAt(0, CFigureUtilities.ConvertFigureObjectToString(flrA))

Original Image
Fig. Original Image
Insert Figure
Fig. Insert Figure(A)

6.2 이미지에 도형과 레이블 삭제

이미지에 추가한 도형과 레이블을 아래와 같이 인덱스로 접근하여 삭제할 수 있습니다.

// 1번 인덱스 도형을 삭제
fli.DeleteFigureAt(1);
// 1번 인덱스 도형을 삭제
fli.DeleteFigureAt(1);
# 1번 인덱스 도형을 삭제
fli.DeleteFigureAt(1)

위 코드를 수행하면 아래와 같이 1번 인덱스의 도형(B 위의 사각형)이 삭제된 것을 확인할 수 있습니다.
Original Image
Fig. Original Image
Insert Figure
Fig. Delete Figure(B)

전체 도형을 삭제하려면 아래와 같이 ClearFigures 계열 함수를 이용하면 모든 도형을 삭제할 수 있습니다.

// 현재 선택된 페이지 안의 모든 도형을 삭제
fli.ClearFigures();
// 3번 페이지 안의 모든 도형을 삭제
fli.ClearFigures(3);
// 모든 페이지에서 도형을 전부 삭제
fli.ClearFiguresAllPages();
// 현재 선택된 페이지 안의 모든 도형을 삭제
fli.ClearFigures();
// 3번 페이지 안의 모든 도형을 삭제
fli.ClearFigures(3);
// 모든 페이지에서 도형을 전부 삭제
fli.ClearFiguresAllPages();
# 현재 선택된 페이지 안의 모든 도형을 삭제
fli.ClearFigures()
# 3번 페이지 안의 모든 도형을 삭제
fli.ClearFigures(3)
# 모든 페이지에서 도형을 전부 삭제
fli.ClearFiguresAllPages()

6.3 인덱스의 도형 설정

특정 인덱스의 도형을 변경하고자 하는 경우, SetFigureAt() 함수를 이용하면 해당 인덱스의 도형을 설정할 수 있습니다.

CFLRegion flrg;
flrg.PushBack(CFLPoint<double>(75.4, 123.0));
flrg.PushBack(CFLPoint<double>(106.9, 38.0));
flrg.PushBack(CFLPoint<double>(140.9, 121.7));
flrg.SetName(L"A");
// 0번 인덱스 도형을 변경
fli.SetFigureAt(0, CFigureUtilities::ConvertFigureObjectToString(flrg));
// 도형 정의(다각형)
CFLRegion flrg = new CFLRegion();
flrg.PushBack(new CFLPoint<double>(75.4, 123.0));
flrg.PushBack(new CFLPoint<double>(106.9, 38.0));
flrg.PushBack(new CFLPoint<double>(140.9, 121.7));
flrg.SetName("A");

// 0번 인덱스 도형을 변경
fli.SetFigureAt(0, CFigureUtilities.ConvertFigureObjectToString(flrg));
# 도형 정의(다각형)
flrg = CFLRegion()
flrg.PushBack(CFLPoint[Double](75.4, 123.0))
flrg.PushBack(CFLPoint[Double](106.9, 38.0))
flrg.PushBack(CFLPoint[Double](140.9, 121.7))
flrg.SetName("A")

# 0번 인덱스 도형을 변경
fli.SetFigureAt(0, CFigureUtilities.ConvertFigureObjectToString(flrg))

위 코드를 수행하면 0번째 인덱스의 도형(A 위의 사각형)이 삼각형으로 변경됩니다.

Original Image
Fig. Original Image
Insert Figure
Fig. Set Figure(A)

6.4 이미지에서 도형 얻어 오기

이미지에 저장된 도형 개수와 이미지에 저장된 도형을 인덱스를 통해 얻는 방법입니다. 아래 예제 코드와 같이 GetFigureCount() 를 통해 이미지에 저장된 도형 개수를 얻을 수 있고, GetFigure()를 통해 특정 인덱스의 도형을 문자열로 얻어 올 수 있습니다. 이 문자열을 CFigureUtilities::ConvertFigureStringToObject()를 통해 CFLFigure 객체로 변환할 수 있습니다. 단, CFigureUtilities::ConvertFigureStringToObject()를 사용해 CFLFigure 객체를 얻었다면, 메모리를 반드시 해제해야 합니다.

// 이미지에 저장된 도형 개수만큼 loop 반복
for(int32_t i = 0; i < fli.GetFigureCount(); i++)
{
    // i 번째 도형 얻어 오기
    CFLFigure* pFlf = CFigureUtilities::ConvertFigureStringToObject(fli.GetFigure(i));

    if(!pFlf)
        continue;

    // 도형의 이름(label) 얻기
    CFLString<wchar_t> strName = pFlf->GetName();    
    // do something...

    // 메모리 해제
    delete pFlf;
    pFlf = nullptr;
}
// 이미지에 저장된 도형 개수만큼 loop 반복
for(int i = 0; i < fli.GetFigureCount(); i++)
{
  // i 번째 도형 얻어 오기
  CFLFigure pFlf = CFigureUtilities.ConvertFigureStringToObject(fli.GetFigure(i));

  if(pFlf == null)
    continue;

  // 도형의 이름(label) 얻기
  String strName = pFlf.GetName();
  // do something...
}
# 이미지에 저장된 도형 개수만큼 loop 반복
for i in range (0, fli.GetFigureCount()):
  # i 번째 도형 얻어 오기
  pFlf = CFigureUtilities.ConvertFigureStringToObject(fli.GetFigure(i))

  if pFlf == None:
    continue

  # 도형의 이름(label) 얻기
  strName = pFlf.GetName()
  # do something...

7 추가 정보(컬러 시퀀스, FT 이미지 정보 등) 이용: Extra Data

이미지에 따라 특수 목적의 정보가 필요한 경우가 있습니다. 예를 들어 컬러 이미지의 경우, RGB 순서인지 BGR 순서인지 등의 순서 정보가 필요하고 Fourier Transform 이미지의 경우 원본 이미지 크기나 픽셀 최댓값 등의 정보가 필요합니다. 이 경우 CFLImage 클래스는 Extra data 객체를 통해 추가 정보를 관리합니다.

7.1 이미지에서 추가 정보 얻어 오기

Extra data는 이미지에서 인덱스로 접근 가능합니다. 아래와 같이 GetExtraData()를 통해 이미지에 저장된 CFLImageExtraDataBase 객체의 포인터를 얻어 올 수 있습니다. 이 객체에 대해 GetExtraDataType()을 호출하면 Extra data의 타입을 알 수 있고, 이 타입을 통해 적절한 클래스로 캐스팅하여 추가 정보에 올바르게 접근할 수 있습니다.

// CFLImage 객체 생성
CFLImage fli;

// 이미지 로드
fli.Load(L"D://Image.png");

// 이미지의 Extra Data 개수만큼 loop 반복
for(int32_t i = 0; i < fli.GetExtraDataCount(); i++)
{
    // i번째 Extra Data 얻기
    CFLImageExtraDataBase* pEd = fli.GetExtraData(i);

    if(!pEd)
        continue;

    // Extra Data의 타입 얻기
    EImageExtraDataType eExtraDataType = pEd->GetExtraDataType();

    switch(eExtraDataType)
    {      
    // Fourier transform 이미지에 대한 추가 정보
    case FLImaging::Base::EImageExtraDataType_FourierTransform:
        {
            CFLImageExtraDataFT* pExtraDataFT = dynamic_cast<CFLImageExtraDataFT*>(pEd);

            if(!pExtraDataFT)
                break;

            int64_t i64OrigW = pExtraDataFT->GetOriginalWidth();
            int64_t i64OrigH = pExtraDataFT->GetOriginalHeight();
            EPixelFormat ePFOrig = pExtraDataFT->GetOriginalPixelFormat();
            bool bShiftState = pExtraDataFT->GetShiftState();
            double f64MaxVal = pExtraDataFT->GetMaxValue();
        }
        break;
    // Real-valued Fourier transform 이미지에 대한 추가 정보
    case FLImaging::Base::EImageExtraDataType_RealValuedFourierTransform:
        {
            CFLImageExtraDataRFT* pExtraDataRFT = dynamic_cast<CFLImageExtraDataRFT*>(pEd);

            if(!pExtraDataRFT)
                break;

            int64_t i64OrigW = pExtraDataRFT->GetOriginalWidth();
            int64_t i64OrigH = pExtraDataRFT->GetOriginalHeight();
            EPixelFormat ePFOrig = pExtraDataRFT->GetOriginalPixelFormat();
            bool bShiftState = pExtraDataRFT->GetShiftState();
            double f64MaxVal = pExtraDataRFT->GetMaxValue();
        }
        break;
    // 이미지의 컬러에 대한 추가 정보
    case FLImaging::Base::EImageExtraDataType_Color:
        {
            CFLImageExtraDataColor* pExtraDataColor = dynamic_cast<CFLImageExtraDataColor*>(pEd);

            if(!pExtraDataColor)
                break;

            EColorSequence eCS = pExtraDataColor->GetColorSequence();
        }
        break;
    // Discrete Wavelet Transform 에 대한 추가 정보
    case FLImaging::Base::EImageExtraDataType_DiscreteWaveletTransform:
        {
            CFLImageExtraDataWT* pExtraDataWT = dynamic_cast<CFLImageExtraDataWT*>(pEd);

            if(!pExtraDataWT)
                break;

            int64_t i64OrigW = pExtraDataWT->GetOriginalWidth();
            int64_t i64OrigH = pExtraDataWT->GetOriginalHeight();
            EPixelFormat ePFOrig = pExtraDataWT->GetOriginalPixelFormat();
            int32_t i32AlignByte = pExtraDataWT->GetOriginalAlignByte();
            int64_t i64WSB = pExtraDataWT->GetOriginalWidthStepByte();
            int64_t i64DecompositionLevel = pExtraDataWT->GetDecompositionLevel();
            int32_t i32BasisFunction = pExtraDataWT->GetBasisFunction();
        }
        break;
    // Discrete Cosine Transform 에 대한 추가 정보
    case FLImaging::Base::EImageExtraDataType_DiscreteCosineTransform:
        {
            CFLImageExtraDataCT* pExtraDataCT = dynamic_cast<CFLImageExtraDataCT*>(pEd);

            if(!pExtraDataCT)
                break;

            int64_t i64OrigW = pExtraDataCT->GetOriginalWidth();
            int64_t i64OrigH = pExtraDataCT->GetOriginalHeight();
            EPixelFormat ePFOrig = pExtraDataCT->GetOriginalPixelFormat();
            int32_t i32AlignByte = pExtraDataCT->GetOriginalAlignByte();
            int64_t i64WSB = pExtraDataCT->GetOriginalWidthStepByte();
            double f64MaxVal = pExtraDataCT->GetMaxValue();
        }
        break;
    }
}
// CFLImage 객체 생성
CFLImage fli = new CFLImage();

// 이미지 로드
if(fli.Load("D://Image.png").IsFail())
  return;

// 이미지의 Extra Data 개수만큼 loop 반복
for(int i = 0; i < fli.GetExtraDataCount(); i++)
{
  // i번째 Extra Data 얻기
  CFLImageExtraDataBase pEd = fli.GetExtraData(i);

  if(pEd == null)
    continue;

  // Extra Data의 타입 얻기
  EImageExtraDataType eExtraDataType = pEd.GetExtraDataType();

  switch(eExtraDataType)
  {
  // Fourier transform 이미지에 대한 추가 정보
  case EImageExtraDataType.FourierTransform:
    {
      CFLImageExtraDataFT pExtraDataFT = pEd as CFLImageExtraDataFT;

      if(pExtraDataFT == null)
        break;

      long i64OrigW = pExtraDataFT.GetOriginalWidth();
      long i64OrigH = pExtraDataFT.GetOriginalHeight();
      EPixelFormat ePFOrig = pExtraDataFT.GetOriginalPixelFormat();
      bool bShiftState = pExtraDataFT.GetShiftState();
      double f64MaxVal = pExtraDataFT.GetMaxValue();
    }
    break;
  // Real-valued Fourier transform 이미지에 대한 추가 정보
  case EImageExtraDataType.RealValuedFourierTransform:
    {
      CFLImageExtraDataRFT pExtraDataRFT = pEd as CFLImageExtraDataRFT;

      if(pExtraDataRFT == null)
        break;

      long i64OrigW = pExtraDataRFT.GetOriginalWidth();
      long i64OrigH = pExtraDataRFT.GetOriginalHeight();
      EPixelFormat ePFOrig = pExtraDataRFT.GetOriginalPixelFormat();
      bool bShiftState = pExtraDataRFT.GetShiftState();
      double f64MaxVal = pExtraDataRFT.GetMaxValue();
    }
    break;
  // 이미지의 컬러에 대한 추가 정보
  case EImageExtraDataType.Color:
    {
      CFLImageExtraDataColor pExtraDataColor = pEd as CFLImageExtraDataColor;

      if(pExtraDataColor == null)
        break;

      EColorSequence eCS = pExtraDataColor.GetColorSequence();
    }
    break;
  // Discrete Wavelet Transform 에 대한 추가 정보
  case EImageExtraDataType.DiscreteWaveletTransform:
    {
      CFLImageExtraDataWT pExtraDataWT = pEd as CFLImageExtraDataWT;

      if(pExtraDataWT == null)
        break;

      long i64OrigW = pExtraDataWT.GetOriginalWidth();
      long i64OrigH = pExtraDataWT.GetOriginalHeight();
      EPixelFormat ePFOrig = pExtraDataWT.GetOriginalPixelFormat();
      int i32AlignByte = pExtraDataWT.GetOriginalAlignByte();
      long i64WSB = pExtraDataWT.GetOriginalWidthStepByte();
      long i64DecompositionLevel = pExtraDataWT.GetDecompositionLevel();
      int i32BasisFunction = pExtraDataWT.GetBasisFunction();
    }
    break;
  // Discrete Cosine Transform 에 대한 추가 정보
  case EImageExtraDataType.DiscreteCosineTransform:
    {
      CFLImageExtraDataCT pExtraDataCT = pEd as CFLImageExtraDataCT;

      if(pExtraDataCT == null)
        break;

      long i64OrigW = pExtraDataCT.GetOriginalWidth();
      long i64OrigH = pExtraDataCT.GetOriginalHeight();
      EPixelFormat ePFOrig = pExtraDataCT.GetOriginalPixelFormat();
      int i32AlignByte = pExtraDataCT.GetOriginalAlignByte();
      long i64WSB = pExtraDataCT.GetOriginalWidthStepByte();
      double f64MaxVal = pExtraDataCT.GetMaxValue();
    }
    break;
  }
}
# CFLImage 객체 생성
fli = CFLImage()

# 이미지 로드
if fli.Load("D://Image.png").IsFail():
  return

# 이미지의 Extra Data 개수만큼 loop 반복
for i in range(0, fli.GetExtraDataCount()):
  # i번째 Extra Data 얻기
  pEd = fli.GetExtraData(i)

  if pEd == None:
    continue

  # Extra Data의 타입 얻기
  eExtraDataType = pEd.GetExtraDataType()
  
  # Fourier transform 이미지에 대한 추가 정보
  if eExtraDataType == EImageExtraDataType.FourierTransform:
    pExtraDataFT = CFLImageExtraDataFT(pEd)

    if pExtraDataFT == None:
      break

    i64OrigW = pExtraDataFT.GetOriginalWidth()
    i64OrigH = pExtraDataFT.GetOriginalHeight()
    ePFOrig = pExtraDataFT.GetOriginalPixelFormat()
    bShiftState = pExtraDataFT.GetShiftState()
    f64MaxVal = pExtraDataFT.GetMaxValue()

  # Real-valued Fourier transform 이미지에 대한 추가 정보
  elif eExtraDataType == EImageExtraDataType.RealValuedFourierTransform:
    pExtraDataRFT = CFLImageExtraDataRFT(pEd)

    if pExtraDataRFT == None:
      break

    i64OrigW = pExtraDataRFT.GetOriginalWidth()
    i64OrigH = pExtraDataRFT.GetOriginalHeight()
    ePFOrig = pExtraDataRFT.GetOriginalPixelFormat()
    bShiftState = pExtraDataRFT.GetShiftState()
    f64MaxVal = pExtraDataRFT.GetMaxValue()
    
  # 이미지의 컬러에 대한 추가 정보
  elif eExtraDataType == EImageExtraDataType.Color:
    pExtraDataColor = CFLImageExtraDataColor(pEd)

    if pExtraDataColor == None:
      break

    eCS = pExtraDataColor.GetColorSequence()
    
  # Discrete Wavelet Transform 에 대한 추가 정보
  elif eExtraDataType == EImageExtraDataType.DiscreteWaveletTransform:
    pExtraDataWT = CFLImageExtraDataWT(pEd)

    if pExtraDataWT == None:
      break

    i64OrigW = pExtraDataWT.GetOriginalWidth()
    i64OrigH = pExtraDataWT.GetOriginalHeight()
    ePFOrig = pExtraDataWT.GetOriginalPixelFormat()
    i32AlignByte = pExtraDataWT.GetOriginalAlignByte()
    i64WSB = pExtraDataWT.GetOriginalWidthStepByte()
    i64DecompositionLevel = pExtraDataWT.GetDecompositionLevel()
    i32BasisFunction = pExtraDataWT.GetBasisFunction()
    
  # Discrete Cosine Transform 에 대한 추가 정보
  elif eExtraDataType == EImageExtraDataType.DiscreteCosineTransform:
    pExtraDataCT = CFLImageExtraDataCT(pEd)

    if pExtraDataCT == None:
      break

    i64OrigW = pExtraDataCT.GetOriginalWidth()
    i64OrigH = pExtraDataCT.GetOriginalHeight()
    ePFOrig = pExtraDataCT.GetOriginalPixelFormat()
    i32AlignByte = pExtraDataCT.GetOriginalAlignByte()
    i64WSB = pExtraDataCT.GetOriginalWidthStepByte()
    f64MaxVal = pExtraDataCT.GetMaxValue()

7.2 이미지에 Extra data 추가하기

이미지에 Extra data를 추가할 수도 있습니다. 아래는 컬러 이미지에 color sequence 정보를 추가하는 예시입니다.

// CFLImage 객체 생성
CFLImage fli;

// 이미지 로드
fli.Load(L"D://Image.png");

// 이미지의 채널 얻어 오기
int32_t i32Channel = fli.GetChannels();

// Extra data 선언
CFLImageExtraDataColor extDataColor;

// Extra data 설정
if(i32Channel == 4)
    extDataColor.SetColorSequence(EColorSequence_RGBA);
else if(i32Channel == 3)
    extDataColor.SetColorSequence(EColorSequence_RGB);

// Extra data를 이미지에 추가
fli.AddExtraData(extDataColor);
// CFLImage 객체 생성
CFLImage fli = new CFLImage();

// 이미지 로드
if(fli.Load("D://Image.png").IsFail())
  return;

// 이미지의 채널 얻어 오기
int i32Channel = fli.GetChannels();

// Extra data 선언
CFLImageExtraDataColor extDataColor = new CFLImageExtraDataColor();

// Extra data 설정
if(i32Channel == 4)
  extDataColor.SetColorSequence(EColorSequence.RGBA);
else if(i32Channel == 3)
  extDataColor.SetColorSequence(EColorSequence.RGB);

// Extra data를 이미지에 추가
fli.AddExtraData(extDataColor);
# CFLImage 객체 생성
fli = CFLImage()

# 이미지 로드
if fli.Load("D://Image.png").IsFail():
  return
      
# 이미지의 채널 얻어 오기
i32Channel = fli.GetChannels()

# Extra data 선언
extDataColor = CFLImageExtraDataColor()

# Extra data 설정
if i32Channel == 4:
  extDataColor.SetColorSequence(EColorSequence.RGBA)
elif i32Channel == 3:
  extDataColor.SetColorSequence(EColorSequence.RGB)

# Extra data를 이미지에 추가
fli.AddExtraData(extDataColor)

7.3 이미지에서 추가 정보 제거

이미지에 추가된 Extra data를 하나씩 제거하거나, 모두 클리어할 수 있습니다. 아래 함수를 통해 Extra data를 제거하면, 현재 선택된 페이지에서 제거됩니다.

// 현재 선택된 페이지에서 0번째 Extra data를 제거
fli.DeleteExtraData(0);
// 현재 선택된 페이지에서 Extra data를 모두 제거
fli.ClearExtraData();
// 현재 선택된 페이지에서 0번째 Extra data를 제거
fli.DeleteExtraData(0);
// 현재 선택된 페이지에서 Extra data를 모두 제거
fli.ClearExtraData();
# 현재 선택된 페이지에서 0번째 Extra data를 제거
fli.DeleteExtraData(0)
# 현재 선택된 페이지에서 Extra data를 모두 제거
fli.ClearExtraData()