해당 파일은 lcms2 에서 JPEG 내부의 MARKER 정보중 Color Profile 을 사용 하여서 만든 이미지면, 해당 마커에 컬러 프로파일러와 LIBJPEG 에서 추출한 색상 데이터를 기반으로 변환 해주는 역할을 한다.
LCMS2 사용하여 JPEG 내부에 있는 컬러 프로파일러 정보를 확인하고 Color Transform 하는 파일은 jpgicc.c 파일이다.
Main 함수는 아래와 같이 단순하다. 앱을 초기화하고 명령 인수에서 전달받은 파일명으로 컬러 프로파일을 사용하는 이미지를 RGB 형식으로 변환하여 저장한다.
int main(int argc, char* argv[])
{
InitUtils("jpgicc"); // App 초기화 ErrorHanlder 등록
HandleSwitches(argc, argv);
// 명령인수 관련 에러처리
if ((argc - xoptind) != 2) {
Help(0);
}
// import, export 될 대상 파일의 경로이다 아래 함수에서는 파일로 로딩하는 역할을 한다.
OpenInput(argv[xoptind]);
OpenOutput(argv[xoptind+1]);
// 실제 Color Profile 을 적용하는 코드
TransformImage(cInpProf, cOutProf);
// -v 옵션으로 실행시 진행 상황이나 추가 정보를 출력하는 역할을 한다.
if (Verbose) { fprintf(stdout, "\n"); fflush(stdout); }
Done();
return 0;
}
LIBJPEG 내에서 사용하는 ErrorHandler를 등록 및 초기화 함수이다. JPEG 이미지의 HEADER의 정보를 읽고 나면 마커에 따라서 jpeg_color_space(실제 색상), out_color_space(출력 색상)을 알 수 있다.
static cmsBool OpenInput(const char* FileName)
{
int m;
lIsITUFax = FALSE;
InFile = fopen(FileName, "rb");
if (InFile == NULL) {
FatalError("Cannot open '%s'", FileName);
}
// Now we can initialize the JPEG decompression object.
Decompressor.err = jpeg_std_error(&ErrorHandler.pub);
ErrorHandler.pub.error_exit = my_error_exit;
ErrorHandler.pub.output_message = my_error_exit;
jpeg_create_decompress(&Decompressor);
jpeg_stdio_src(&Decompressor, InFile);
for (m = 0; m < 16; m++)
jpeg_save_markers(&Decompressor, JPEG_APP0 + m, 0xFFFF);
// setup_read_icc_profile(&Decompressor);
fseek(InFile, 0, SEEK_SET);
jpeg_read_header(&Decompressor, TRUE);
return TRUE;
}
종류는 아래와 같다. Marker에 따라서 ICC Profile를 사용 유무를 결정하기 때문에 아래 정보가 중요하다.
APP0 - JFIF | R/W/C | JPEG File Interchange Format |
APP0 - JFXX | R | Extended JFIF |
APP0 - CIFF | R/W | Camera Image File Format (used by some Canon models) |
APP0 - AVI1 | R | JPEG AVI information |
APP0 - Ocad | R | Photobucket Ocad segment |
APP1 - EXIF | R/W/C | Exchangeable Image File Format (multi-segment) |
APP1 - XMP | R/W/C | Extensible Metadata Platform (multi-segment) |
APP1 - QVCI | R | Casio QV-7000SX QVCI information |
APP1 - FLIR | R | FLIR thermal imaging data (multi-segment) |
APP1 - RawThermalImage | R | Thermal image from Parrot Bebop-Pro Thermal drone |
APP2 - ICC | R/W/C | International Color Consortium (multi-segment) |
APP2 - FPXR | R | FlashPix Ready (multi-segment) |
APP2 - MPF | R | Multi-Picture Format |
APP2 - PreviewImage | R | Samsung/GE APP2 preview image (multi-segment) |
APP3 - Kodak Meta | R/W | Kodak Meta information (EXIF-like) |
APP3 - Stim | R | Stereo Still Image format |
APP3 - PreviewImage | R | Samsung/HP preview image (multi-segment) |
APP4 - Scalado | R | (presumably written by Scalado mobile software) |
APP4 - FPXR | R | FlashPix Ready in non-standard location (multi-segment) |
APP4 - PreviewImage | R | (continued from APP3) |
APP5 - Ricoh RMETA | R | Ricoh custom fields |
APP5 - Samsung UniqueID | R | Samsung Unique ID |
APP5 - PreviewImage | R | (continued from APP4) |
APP6 - EPPIM | R | Toshiba PrintIM |
APP6 - NITF | R | National Imagery Transmission Format |
APP6 - HP TDHD | R | Hewlett-Packard Photosmart R837 TDHD information |
APP6 - GoPro | R | GoPro Metadata Format (GPMF) information |
APP7 - Pentax | R | Pentax APP7 maker notes |
APP7 - Qualcomm | R | Qualcomm Camera Attributes |
APP7 - Huawei | R | Huawei APP7 maker notes (extract with Unknown option) |
APP8 - SPIFF | R | Still Picture Interchange File Format |
APP9 - Media Jukebox | R | Media Jukebox XML information |
APP10 - Comment | R | PhotoStudio Unicode Comment |
APP11 - JPEG-HDR | R | JPEG-HDR compressed ratio image |
APP12 - Picture Info | R | ASCII-based Picture Information |
APP12 - Ducky | R/W/C | Photoshop "Save for Web" |
APP13 - Photoshop IRB | R/W/C | Image Resource Block (multi-segment, includes IPTC) |
APP13 - Adobe CM | R | Adobe Color Management |
APP14 - Adobe | R/W/C | Adobe DCT filter |
APP15 - GraphicConverter | R | GraphicConverter quality |
COM | R/W/C | JPEG Comment (multi-segment) |
DQT | R | (used to calculate the Extra:JPEGDigest tag value) |
SOF | R | JPEG Start Of Frame |
출력용 jpeg 이미지를 설정하는 메서드이다. 컬러 색상 정보를 설정하고 compress 파라미터를 초기화하는 역할을 하며 해당 함수에서는 아직 초기화 상태이기 때문에 UNKNOWN 데이터를 채워져 있다.
static cmsBool OpenOutput(const char* FileName)
{
OutFile = fopen(FileName, "wb");
if (OutFile == NULL) {
FatalError("Cannot create '%s'", FileName);
}
Compressor.err = jpeg_std_error(&ErrorHandler.pub);
ErrorHandler.pub.error_exit = my_error_exit;
ErrorHandler.pub.output_message = my_error_exit;
Compressor.input_components = Compressor.num_components = 4;
jpeg_create_compress(&Compressor);
jpeg_stdio_dest(&Compressor, OutFile);
return TRUE;
}
실제 색상 프로파일을 이용하여 로딩하는 함수이다. 코드의 양이 길어서 LCMS2에서 제공하는 함수들을 하나하나 확인하면서 다음 글에 포스팅하도록 하겠다.
static int TransformImage(char *cDefInpProf, char *cOutputProf)
{
cmsHPROFILE hIn, hOut, hProof;
cmsHTRANSFORM xform;
cmsUInt32Number wInput, wOutput;
int OutputColorSpace;
cmsUInt32Number dwFlags = 0;
cmsUInt32Number EmbedLen;
cmsUInt8Number* EmbedBuffer;
cmsSetAdaptationState(ObserverAdaptationState);
if (BlackPointCompensation) {
dwFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION;
}
switch (PrecalcMode) {
case 0: dwFlags |= cmsFLAGS_NOOPTIMIZE; break;
case 2: dwFlags |= cmsFLAGS_HIGHRESPRECALC; break;
case 3: dwFlags |= cmsFLAGS_LOWRESPRECALC; break;
default:;
}
if (GamutCheck) {
dwFlags |= cmsFLAGS_GAMUTCHECK;
cmsSetAlarmCodes(Alarm);
}
// Take input color space
wInput = GetInputPixelType();
if (lIsDeviceLink) {
hIn = cmsOpenProfileFromFile(cDefInpProf, "r");
hOut = NULL;
hProof = NULL;
}
else {
if (!IgnoreEmbedded && read_icc_profile(&Decompressor, &EmbedBuffer, &EmbedLen))
{
hIn = cmsOpenProfileFromMem(EmbedBuffer, EmbedLen);
if (Verbose) {
fprintf(stdout, " (Embedded profile found)\n");
PrintProfileInformation(hIn);
fflush(stdout);
}
if (hIn != NULL && SaveEmbedded != NULL)
SaveMemoryBlock(EmbedBuffer, EmbedLen, SaveEmbedded);
free(EmbedBuffer);
}
else
{
// Default for ITU/Fax
if (cDefInpProf == NULL && T_COLORSPACE(wInput) == PT_Lab)
cDefInpProf = "*Lab";
if (cDefInpProf != NULL && cmsstrcasecmp(cDefInpProf, "*lab") == 0)
hIn = CreateITU2PCS_ICC();
else
hIn = OpenStockProfile(0, cDefInpProf);
}
if (cOutputProf != NULL && cmsstrcasecmp(cOutputProf, "*lab") == 0)
hOut = CreatePCS2ITU_ICC();
else
hOut = OpenStockProfile(0, cOutputProf);
hProof = NULL;
if (cProofing != NULL) {
hProof = OpenStockProfile(0, cProofing);
if (hProof == NULL) {
FatalError("Proofing profile couldn't be read.");
}
dwFlags |= cmsFLAGS_SOFTPROOFING;
}
}
if (!hIn)
FatalError("Input profile couldn't be read.");
if (!lIsDeviceLink && !hOut)
FatalError("Output profile couldn't be read.");
// Assure both, input profile and input JPEG are on same colorspace
if (cmsGetColorSpace(hIn) != _cmsICCcolorSpace(T_COLORSPACE(wInput)))
FatalError("Input profile is not operating in proper color space");
// Output colorspace is given by output profile
if (lIsDeviceLink) {
OutputColorSpace = GetDevicelinkColorSpace(hIn);
}
else {
OutputColorSpace = GetProfileColorSpace(hOut);
}
jpeg_copy_critical_parameters(&Decompressor, &Compressor);
WriteOutputFields(OutputColorSpace);
wOutput = ComputeOutputFormatDescriptor(wInput, OutputColorSpace);
xform = cmsCreateProofingTransform(hIn, wInput,
hOut, wOutput,
hProof, Intent,
ProofingIntent, dwFlags);
if (xform == NULL)
FatalError("Cannot transform by using the profiles");
DoTransform(xform, OutputColorSpace);
jcopy_markers_execute(&Decompressor, &Compressor);
cmsDeleteTransform(xform);
cmsCloseProfile(hIn);
cmsCloseProfile(hOut);
if (hProof) cmsCloseProfile(hProof);
return 1;
}
TransformImage 메소드에서 최종적으로 호출하는 메서드로 색상프로파일러를 읽어 OUTPUT 파일을 생성 할때 COLOR PROFILE 의 생상 정보로 RGB 형식으로 변환해 주는 소스이다. cmsDoTransform 메소드를 통하여 COLOR_PROFILE 의 적용된 픽셀의 SCANELINE 단위로 읽으면서 RGB 로 변환하고 jpeg_write_scanlines 메소드로 COLOR_PROFILE을 사용하지 않은 RGB 형태의 JPEG 이미지를 만들어 준다.
static int DoTransform(cmsHTRANSFORM hXForm, int OutputColorSpace)
{
JSAMPROW ScanLineIn;
JSAMPROW ScanLineOut;
//Preserve resolution values from the original
// (Thanks to Robert Bergs for finding out this bug)
Compressor.density_unit = Decompressor.density_unit;
Compressor.X_density = Decompressor.X_density;
Compressor.Y_density = Decompressor.Y_density;
// Compressor.write_JFIF_header = 1;
jpeg_start_decompress(&Decompressor);
jpeg_start_compress(&Compressor, TRUE);
if (OutputColorSpace == PT_Lab)
SetITUFax(&Compressor);
// Embed the profile if needed
if (EmbedProfile && cOutProf)
DoEmbedProfile(cOutProf);
ScanLineIn = (JSAMPROW) malloc(Decompressor.output_width * Decompressor.num_components);
ScanLineOut = (JSAMPROW) malloc(Compressor.image_width * Compressor.num_components);
while (Decompressor.output_scanline < Decompressor.output_height) {
jpeg_read_scanlines(&Decompressor, &ScanLineIn, 1);
cmsDoTransform(hXForm, ScanLineIn, ScanLineOut, Decompressor.output_width);
jpeg_write_scanlines(&Compressor, &ScanLineOut, 1);
}
free(ScanLineIn);
free(ScanLineOut);
jpeg_finish_decompress(&Decompressor);
jpeg_finish_compress(&Compressor);
return TRUE;
}
LCMS2 #8 - 색상 변경 예제 (0) | 2020.01.21 |
---|---|
LCMS2 #7 - jpgicc.c (0) | 2020.01.20 |
LCMS2 #5 - Github Contributor 승인 (0) | 2020.01.09 |
LCMS2 #4 - Pull Request Github (1) | 2020.01.09 |
LCMS2 #3 - Fork Github VS2019 환경 설정(2) (0) | 2020.01.08 |
댓글 영역