Files
argyllcms/spectro/synthread.c
T
gronod d0209ec246
Linux x86_64 Release Build / build (push) Failing after 1m5s
Windows x86_64 Release Build / build (push) Has been cancelled
macOS Release Build / build (arm64, macOS_arm64_bin, arm64-apple-darwin, macos-latest) (push) Has been cancelled
macOS Release Build / build (x86_64, osx10.6_x86_64_bin, x86_64-apple-darwin, macos-13) (push) Has been cancelled
Fix macOS UI link errors by adding missing ui.h includes
2026-08-22 21:27:56 +01:00

700 lines
20 KiB
C

/*
* Argyll Color Management System
* Synthetic device target chart reader
*
* Author: Graeme W. Gill
* Date: 10/7/2007
*
* Copyright 2002 - 2007 Graeme W. Gill
* All rights reserved.
*
* This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
* see the License.txt file for licencing details.
*
* Based on fakeread.c
*/
/*
Implements a synthetic RGB device response based on sRGB like
primaries.
*/
/*
* TTBD:
*
* Add non-linear mixing model
*
*/
#undef DEBUG
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <sys/types.h>
#include <time.h>
#include <string.h>
#include "copyright.h"
#include "aconfig.h"
#include "numlib.h"
#include "cgats.h"
#include "conv.h"
#include "xicc.h"
#include "icc.h"
void
usage(char *mes) {
fprintf(stderr,"Synthetic device model test chart reader - Version %s\n",
ARGYLL_VERSION_STR);
fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n");
if (mes != NULL)
fprintf(stderr,"Error '%s'\n",mes);
fprintf(stderr,"usage: synthread [-v] [-s] [separation.icm] filebase\n");
fprintf(stderr," -v Verbose mode\n");
fprintf(stderr," -p Use separation profile\n");
fprintf(stderr," -l Construct and output in Lab rather than XYZ\n");
fprintf(stderr," -i p1,p2,p3, Set input channel curve powers (default 1.0)\n");
fprintf(stderr," -k x1:y1,x2:y2,x3:y2 Set input channel inflection points (default 0.5,0.5)\n");
fprintf(stderr," -o p1,p2,p3, Set output channel curve powers (default 1.0)\n");
fprintf(stderr," -r level Add average random deviation of <level>%% to input device values (after sep.)\n");
fprintf(stderr," -R level Add average random deviation of <level>%% to output PCS values\n");
fprintf(stderr," -u Make random deviations have uniform distributions rather than normal\n");
fprintf(stderr," -b L,a,b Scale black point to target Lab value\n");
fprintf(stderr," [separation.icm] Device link separation profile\n");
fprintf(stderr," filebase Base name for input[ti1]/output[ti3] file\n");
exit(1);
}
typedef struct {
int dolab; /* Combine and output in Lab space */
double ipow[3]; /* Input power applied */
double ibpp[3]; /* Input breakpoint location, -ve if none */
double ibpv[3]; /* Input breakpoint value, -ve if none */
double col[3][3]; /* sRGB additive colorant values in XYZ :- [out][in] */
double wnf[3]; /* White normalization factor */
double opow[3]; /* Output power */
double omax[3]; /* Output maximum that power operates into */
} synthmodel;
/* Symetrical power function */
double spow(double val, double pp) {
if (val < 0.0)
return -pow(-val, pp);
else
return pow(val, pp);
}
/* Execute the device model */
static void domodel(synthmodel *p, double *out, double *in) {
double tmp[3];
int i, j;
/* Input power */
for (j = 0; j < 3; j++)
tmp[j] = pow(in[j], p->ipow[j]);
/* Input breakpoint */
for (j = 0; j < 3; j++) {
if (p->ibpp[j] >= 0.0 && p->ibpv[j] >= 0.0) {
double b;
if (tmp[j] <= p->ibpp[j]) {
b = (tmp[j] - 0.0)/(p->ibpp[j] - 0.0);
tmp[j] = b * p->ibpv[j] + (1.0 - b) * 0.0;
} else {
b = (tmp[j] - p->ibpp[j])/(1.0 - p->ibpp[j]);
tmp[j] = b * 1.0 + (1.0 - b) * p->ibpv[j];
}
}
}
/* Lookup primary values, sum them, and then */
/* apply output power */
/* (We're not allowing for non-linear mixing yet) */
for (j = 0; j < 3; j++) {
out[j] = 0.0;
for (i = 0; i < 3; i++)
out[j] += p->col[j][i] * tmp[i];
out[j] = spow(out[j]/p->omax[j], p->opow[j]) * p->omax[j];
}
}
#include "ui.h"
int main(int argc, char *argv[]) {
int i, j, rv = 0;
int fa,nfa; /* current argument we're looking at */
int verb = 0; /* Verbose flag */
int dosep = 0; /* Use separation before profile */
int gfudge = 0; /* Do grey fudge, 1 = W->RGB, 2 = K->xxxK */
double rdlevel = 0.0; /* Random device average deviation level (0.0 - 1.0) */
double rplevel = 0.0; /* Random PCS average deviatio level (0.0 - 1.0) */
int unidist = 0; /* Use uniform distribution of errors */
double tbp[3] = { -1.0, 0.0, 0.0 }; /* Target black point */
static char sepname[MAXNAMEL+1] = { 0 }; /* ICC separation profile */
static char inname[MAXNAMEL+10+1] = { 0 }; /* Input cgats file base name */
static char outname[MAXNAMEL+10+1] = { 0 }; /* Output cgats file base name */
cgats *icg; /* input cgats structure */
cgats *ocg; /* output cgats structure */
int nmask = 0; /* Test chart device colorant mask */
int nchan = 0; /* Test chart number of device chanels */
int npat; /* Number of patches */
int si; /* Sample id index */
int ti; /* Temp index */
int fi; /* Colorspace index */
synthmodel md; /* Synthetic model */
/* ICC separation device link profile */
icmFile *sep_fp = NULL; /* Color profile file */
icmErr err = { 0, { '\000'} };
icc *sep_icco = NULL; /* Profile object */
icmLuSpace *sep_luo = NULL; /* Conversion object */
icColorSpaceSignature sep_ins, sep_outs; /* Type of input and output spaces */
int sep_inn; /* Number of input channels to separation */
inkmask sep_nmask = 0; /* Colorant mask for separation input */
double wp[3], bp[3]; /* ICC profile Lab white and black points */
double bpt[3][3]; /* Black point transform matrix (Lab->Lab) */
int inn, outn; /* Number of channels for conversion input, output */
icColorSpaceSignature ins, outs; /* Type of conversion input and output spaces */
int cnv_nmask = 0; /* Conversion input nmask */
time_t clk = time(0);
struct tm *tsp = localtime(&clk);
char *atm = asctime(tsp); /* Ascii time */
char *xyzfname[3] = { "XYZ_X", "XYZ_Y", "XYZ_Z" };
char *labfname[3] = { "LAB_L", "LAB_A", "LAB_B" };
error_program = "Synthread";
if (argc < 1)
usage("Too few arguments");
/* Initialise the default model */
inn = 3;
ins = icSigRgbData;
outn = 3;
outs = icSigXYZData;
md.dolab = 0;
md.ipow[0] = 1.0;
md.ipow[1] = 1.0;
md.ipow[2] = 1.0;
md.ibpp[0] = -1.0;
md.ibpv[0] = -1.0;
md.ibpp[1] = -1.0;
md.ibpv[1] = -1.0;
md.ibpp[2] = -1.0;
md.ibpv[2] = -1.0;
md.col[0][0] = 0.412424; /* X from R */
md.col[0][1] = 0.357579; /* X from G */
md.col[0][2] = 0.180464; /* X from B */
md.col[1][0] = 0.212656; /* Y from R */
md.col[1][1] = 0.715158; /* Y from G */
md.col[1][2] = 0.0721856; /* Y from B */
md.col[2][0] = 0.0193324; /* Z from R */
md.col[2][1] = 0.119193; /* Z from G */
md.col[2][2] = 0.950444; /* Z from B */
md.opow[0] = 1.0;
md.opow[1] = 1.0;
md.opow[2] = 1.0;
md.omax[0] = 1.0;
md.omax[1] = 1.0;
md.omax[2] = 1.0;
/* Process the arguments */
for (fa = 1;fa < argc;fa++) {
nfa = fa; /* skip to nfa if next argument is used */
if (argv[fa][0] == '-') { /* Look for any flags */
char *na = NULL; /* next argument after flag, null if none */
if (argv[fa][2] != '\000')
na = &argv[fa][2]; /* next is directly after flag */
else
{
if ((fa+1) < argc)
{
if (argv[fa+1][0] != '-')
{
nfa = fa + 1;
na = argv[nfa]; /* next is seperate non-flag argument */
}
}
}
if (argv[fa][1] == '?')
usage("Usage requested");
/* Verbose */
else if (argv[fa][1] == 'v' || argv[fa][1] == 'V')
verb = 1;
/* Separation */
else if (argv[fa][1] == 'p' || argv[fa][1] == 'P')
dosep = 1;
/* Lab */
else if (argv[fa][1] == 'l' || argv[fa][1] == 'L')
md.dolab = 1;
/* Input curve power */
else if (argv[fa][1] == 'i' || argv[fa][1] == 'I') {
fa = nfa;
if (na == NULL) usage("Expect argument to -i");
if (sscanf(na, " %lf,%lf,%lf ", &md.ipow[0], &md.ipow[1], &md.ipow[2]) != 3)
usage("Argument to -i does not parse");
}
/* Input curve inflexction point */
else if (argv[fa][1] == 'k' || argv[fa][1] == 'K') {
fa = nfa;
if (na == NULL) usage("Expect argument to -k");
if (sscanf(na, " %lf:%lf,%lf:%lf,%lf:%lf ",
&md.ibpp[0], &md.ibpv[0], &md.ibpp[1],
&md.ibpv[1], &md.ibpp[2], &md.ibpv[2]) != 6)
usage("Argument to -k does not parse");
}
/* Output curve power */
else if (argv[fa][1] == 'o' || argv[fa][1] == 'O') {
fa = nfa;
if (na == NULL) usage("Expect argument to -o");
if (sscanf(na, " %lf,%lf,%lf ", &md.opow[0], &md.opow[1], &md.opow[2]) != 3)
usage("Argument to -o does not parse");
}
/* Uniform distrivuted errors */
else if (argv[fa][1] == 'u' || argv[fa][1] == 'U')
unidist = 1;
/* Random addition to device levels */
else if (argv[fa][1] == 'r') {
fa = nfa;
if (na == NULL) usage("Expect argument to -r");
rdlevel = 0.01 * atof(na);
rand32(time(NULL)); /* Init seed randomly */
}
/* Random addition to PCS levels */
else if (argv[fa][1] == 'R') {
fa = nfa;
if (na == NULL) usage("Expect argument to -R");
rplevel = 0.01 * atof(na);
rand32(time(NULL)); /* Init seed randomly */
}
/* Black point scale */
else if (argv[fa][1] == 'b' || argv[fa][1] == 'B') {
if (na == NULL) usage("Expect argument to -b");
fa = nfa;
if (sscanf(na, " %lf , %lf , %lf ",&tbp[0], &tbp[1], &tbp[2]) != 3)
usage("Couldn't parse argument to -b");
if (tbp[0] < 0.0 || tbp[0] > 100.0) usage("-b L* value out of range");
if (tbp[1] < -128.0 || tbp[1] > 128.0) usage("-b a* value out of range");
if (tbp[2] < -128.0 || tbp[2] > 128.0) usage("-b b* value out of range");
}
else
usage("Unrecognised flag");
}
else
break;
}
/* Get the file name argument */
if (dosep) {
if (fa >= argc || argv[fa][0] == '-') usage("Missing separation profile filename argument");
strncpy(sepname,argv[fa++],MAXNAMEL); sepname[MAXNAMEL] = '\000';
}
if (fa >= argc || argv[fa][0] == '-') usage("Missing basename argument");
strncpy(inname,argv[fa],MAXNAMEL); inname[MAXNAMEL] = '\000';
strcat(inname,".ti1");
strncpy(outname,argv[fa++],MAXNAMEL); outname[MAXNAMEL] = '\000';
strcat(outname,".ti3");
/* Convert colorants to Lab, and scale white point */
if (md.dolab) {
double white[3] = { 100.0, 0, 0 };
double rot[3][3];
for (i = 0; i < 3; i++) {
double val[3];
val[0] = md.col[0][i];
val[1] = md.col[1][i];
val[2] = md.col[2][i];
icmXYZ2Lab(&icmD50, val, val);
md.col[0][i] = val[0];
md.col[1][i] = val[1];
md.col[2][i] = val[2];
}
/* Compute white sum */
for (i = 0; i < 3; i++) {
md.omax[i] = 0.0;
for (j = 0; j < 3; j++)
md.omax[i] += md.col[i][j];
}
/* Compute rotate and scale to map to white target */
icmRotMat(rot, md.omax, white);
/* Rotate and primaries to sum to white */
for (i = 0; i < 3; i++) {
double val[3];
val[0] = md.col[0][i];
val[1] = md.col[1][i];
val[2] = md.col[2][i];
icmMulBy3x3(val, rot, val);
md.col[0][i] = val[0];
md.col[1][i] = val[1];
md.col[2][i] = val[2];
}
/* Compute output maximum factors to set out power range */
for (i = 0; i < 3; i++) {
md.omax[i] = 0.0;
for (j = 0; j < 3; j++) {
if (i == 0)
md.omax[i] += md.col[i][j];
else {
if (fabs(md.col[i][j]) > md.omax[i])
md.omax[i] = fabs(md.col[i][j]);
}
}
}
} else {
/* Compute output maximum factors to set out power range */
for (i = 0; i < 3; i++) {
md.omax[i] = 0.0;
for (j = 0; j < 3; j++)
md.omax[i] += md.col[i][j];
}
}
//printf("~1 omax = %f %f %f\n", md.omax[0], md.omax[1], md.omax[2]);
/* Deal with separation */
if (dosep) {
if ((sep_fp = new_icmFileStd_name(&err, sepname,"r")) == NULL)
error ("Can't open file '%s' (0x%x, '%s')",sepname,err.c,err.m);
if ((sep_icco = new_icc(&err)) == NULL)
error ("Creation of ICC object failed (0x%x, '%s')",err.c,err.m);
/* Deal with ICC separation */
if ((rv = sep_icco->read(sep_icco,sep_fp,0)) == 0) {
icmCSInfo ini, outi;
/* Get a conversion object */
if ((sep_luo = (icmLuSpace *)sep_icco->get_luobj(sep_icco, icmFwd, icmDefaultIntent, icmSigDefaultData, icmLuOrdNorm)) == NULL) {
error ("%d, %s",sep_icco->e.c, sep_icco->e.m);
}
/* Get details of conversion */
sep_luo->spaces(sep_luo, &ini, &outi, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
sep_ins = ini.sig;
sep_inn = ini.nch;
sep_outs = outi.sig;
sep_nmask = icx_icc_to_colorant_comb(sep_ins, sep_icco->header->deviceClass);
}
}
/* Deal with input CGATS files */
icg = new_cgats(); /* Create a CGATS structure */
icg->add_other(icg, "CTI1"); /* our special input type is Calibration Target Information 1 */
if (icg->read_name(icg, inname))
error("CGATS file read error : %s",icg->e.m);
if (icg->ntables == 0 || icg->t[0].tt != tt_other || icg->t[0].oi != 0)
error ("Input file isn't a CTI1 format file");
if (icg->ntables != 1 && icg->ntables != 2 && icg->ntables != 3)
error ("Input file doesn't contain one, two or three tables");
if ((npat = icg->t[0].nsets) <= 0)
error ("No sets of data");
/* Setup output cgats file */
ocg = new_cgats(); /* Create a CGATS structure */
ocg->add_other(ocg, "CTI3"); /* our special type is Calibration Target Information 3 */
ocg->add_table(ocg, tt_other, 0); /* Start the first table */
ocg->add_kword(ocg, 0, "DESCRIPTOR", "Argyll Calibration Target chart information 3",NULL);
ocg->add_kword(ocg, 0, "ORIGINATOR", "Argyll synthread", NULL);
atm[strlen(atm)-1] = '\000'; /* Remove \n from end */
ocg->add_kword(ocg, 0, "CREATED",atm, NULL);
/* Assume a general output type device */
ocg->add_kword(ocg, 0, "DEVICE_CLASS","OUTPUT", NULL);
if ((ti = icg->find_kword(icg, 0, "SINGLE_DIM_STEPS")) >= 0)
ocg->add_kword(ocg, 0, "SINGLE_DIM_STEPS",icg->t[0].kdata[ti], NULL);
if ((ti = icg->find_kword(icg, 0, "COMP_GREY_STEPS")) >= 0)
ocg->add_kword(ocg, 0, "COMP_GREY_STEPS",icg->t[0].kdata[ti], NULL);
if ((ti = icg->find_kword(icg, 0, "MULTI_DIM_STEPS")) >= 0)
ocg->add_kword(ocg, 0, "MULTI_DIM_STEPS",icg->t[0].kdata[ti], NULL);
if ((ti = icg->find_kword(icg, 0, "FULL_SPREAD_PATCHES")) >= 0)
ocg->add_kword(ocg, 0, "FULL_SPREAD_PATCHES",icg->t[0].kdata[ti], NULL);
if ((ti = icg->find_kword(icg, 0, "DARK_REGION_EMPHASIS")) >= 0)
ocg->add_kword(ocg, 0, "DARK_REGION_EMPHASIS",icg->t[0].kdata[ti], NULL);
/* Fields we want */
ocg->add_field(ocg, 0, "SAMPLE_ID", nqcs_t);
if ((si = icg->find_field(icg, 0, "SAMPLE_ID")) < 0)
error ("Input file doesn't contain field SAMPLE_ID");
/* Figure out the color space */
if ((fi = icg->find_kword(icg, 0, "COLOR_REP")) < 0)
error ("Input file doesn't contain keyword COLOR_REP");
if ((nmask = icx_char2inkmask(icg->t[0].kdata[fi])) == 0)
error ("Input file keyword COLOR_REP has unknown value");
{
int i, j, ii;
int chix[ICX_MXINKS]; /* Device chanel indexes */
char *ident, *bident;
int nsetel = 0;
cgats_set_elem *setel; /* Array of set value elements */
nchan = icx_noofinks(nmask);
ident = icx_inkmask2char(nmask, 1);
bident = icx_inkmask2char(nmask, 0);
/* Sanity check what we're going to do */
if (dosep) {
/* Check if sep ICC input is compatible with .ti1 */
if (nmask == ICX_W && sep_ins == icSigRgbData)
gfudge = 1;
else if (nmask == ICX_K && sep_ins == icSigCmykData)
gfudge = 2;
else if (icx_colorant_comb_match_icc(nmask, sep_ins) == 0) {
error("Separation ICC device space '%s' doesn't match TI1 '%s'",
icm2str(icmColorSpaceSig, sep_ins),
ident); /* Should free(). */
}
/* Check if separation ICC output is compatible with ICC/MPP/TI3 conversion */
if (sep_outs != ins)
error("Synthetic device space '%s' doesn't match Separation ICC '%s'",
icm2str(icmColorSpaceSig, ins),
icm2str(icmColorSpaceSig, sep_outs));
} else {
/* Check if synthetic device is compatible with .ti1 */
if (nmask == ICX_W && ins == icSigRgbData)
gfudge = 1;
else if (nmask == ICX_K && ins == icSigCmykData)
gfudge = 2; /* Should allow for other colorant combo's that include black */
else if (icx_colorant_comb_match_icc(nmask, ins) == 0) {
error("Synthetic device space '%s' doesn't match TI1 '%s'",
icm2str(icmColorSpaceSig, ins),
ident); // Should free().
}
}
if ((ii = icg->find_kword(icg, 0, "TOTAL_INK_LIMIT")) >= 0)
ocg->add_kword(ocg, 0, "TOTAL_INK_LIMIT",icg->t[0].kdata[ii], NULL);
nsetel += 1; /* For id */
nsetel += nchan; /* For device values */
nsetel += 3; /* For XYZ/Lab */
for (j = 0; j < nchan; j++) {
int imask;
char fname[100];
imask = icx_index2ink(nmask, j);
sprintf(fname,"%s_%s",nmask == ICX_W || nmask == ICX_K ? "GRAY" : bident,
icx_ink2char(imask));
if ((ii = icg->find_field(icg, 0, fname)) < 0)
error ("Input file doesn't contain field %s",fname);
if (icg->t[0].ftype[ii] != r_t)
error ("Field %s is wrong type",fname);
ocg->add_field(ocg, 0, fname, r_t);
chix[j] = ii;
}
/* Add PCS fields */
for (j = 0; j < 3; j++) {
ocg->add_field(ocg, 0, md.dolab ? labfname[j] : xyzfname[j], r_t);
}
{
char fname[100];
sprintf(fname, md.dolab ? "%s_LAB" : "%s_XYZ", ident);
ocg->add_kword(ocg, 0, "COLOR_REP", fname, NULL);
}
if ((setel = (cgats_set_elem *)malloc(sizeof(cgats_set_elem) * nsetel)) == NULL)
error("Malloc failed!");
/* Read all the test patches in, convert them, */
/* and write them out. */
for (i = 0; i < npat; i++) {
int k = 0;
char *id;
double odev[ICX_MXINKS], dev[ICX_MXINKS], sep[ICX_MXINKS], PCS[3];
xspect out;
id = ((char *)icg->t[0].fdata[i][si]);
for (j = 0; j < nchan; j++) {
double dv = *((double *)icg->t[0].fdata[i][chix[j]]) / 100.0;
odev[j] = dev[j] = sep[j] = dv;
}
if (gfudge) {
int nch;
if (dosep) /* Figure number of channels into conversion */
nch = sep_inn;
else
nch = inn;
if (gfudge == 1) { /* Convert W -> RGB */
double wval = dev[0];
for (j = 0; j < nch; j++)
dev[j] = sep[j] = wval;
} else { /* Convert K->xxxK */
int kch;
int inmask;
double kval = dev[0];
if (dosep) /* Figure number of channels into conversion */
inmask = sep_nmask;
else
inmask = cnv_nmask;
if (inmask == 0)
error("Input colorspace ambiguous - can't determine if it has black");
if ((kch = icx_ink2index(inmask, ICX_BLACK)) == -1)
error("Can't find black colorant for K fudge");
for (j = 0; j < nch; j++) {
if (j == kch)
dev[j] = sep[j] = kval;
else
dev[j] = sep[j] = 0.0;
}
}
}
if (dosep)
if (sep_luo->lookup_fwd(sep_luo, sep, dev) & icmPe_lurv_err)
error ("%d, %s",sep_icco->e.c,sep_icco->e.m);
/* Add randomness and non-linearity (rdlevel is avg. dev.) */
/* Note dev/sep is 0-1.0 at this stage */
for (j = 0; j < inn; j++) {
double dv = sep[j];
if (rdlevel > 0.0) {
double rr;
if (unidist)
rr = d_rand(-2.0 * rdlevel, 2.0 * rdlevel);
else
rr = 1.2533 * rdlevel * norm_rand();
dv += rr;
if (dv < 0.0)
dv = 0.0;
else if (dv > 1.0)
dv = 1.0;
}
sep[j] = dv;
}
/* Do color conversion */
domodel(&md, PCS, sep);
if (tbp[0] >= 0) { /* Doing black point scaling */
for (j = 0; j < 3; j++)
PCS[j] -= wp[j];
icmMulBy3x3(PCS, bpt, PCS);
for (j = 0; j < 3; j++)
PCS[j] += wp[j];
}
setel[k++].c = id;
for (j = 0; j < nchan; j++)
setel[k++].d = 100.0 * odev[j];
if (md.dolab == 0) {
PCS[0] *= 100.0;
PCS[1] *= 100.0;
PCS[2] *= 100.0;
}
/* Add randomness (rplevel is avg. dev.) */
/* Note PCS is 0..100 XYZ or Lab at this point */
if (rplevel > 0.0) {
for (j = 0; j < 3; j++) {
double dv = PCS[j];
double rr;
if (unidist)
rr = 100.0 * d_rand(-2.0 * rplevel, 2.0 * rplevel);
else
rr = 100.0 * 1.2533 * rplevel * norm_rand();
dv += rr;
/* Don't let L*, X, Y or Z go negative */
if ((!md.dolab || j == 0) && dv < 0.0)
dv = 0.0;
PCS[j] = dv;
}
}
setel[k++].d = PCS[0];
setel[k++].d = PCS[1];
setel[k++].d = PCS[2];
ocg->add_setarr(ocg, 0, setel);
}
free(setel);
free(ident);
free(bident);
}
if (sep_luo != NULL) {
sep_luo->del(sep_luo);
sep_icco->del(sep_icco);
sep_fp->del(sep_fp);
}
if (ocg->write_name(ocg, outname))
error("Write error : %s",ocg->e.m);
ocg->del(ocg); /* Clean up */
icg->del(icg); /* Clean up */
return 0;
}