mirror of http://192.168.1.51:8099/lmh188/twain3.0
313 lines
11 KiB
C
313 lines
11 KiB
C
/*====================================================================*
|
|
- Copyright (C) 2001 Leptonica. All rights reserved.
|
|
-
|
|
- Redistribution and use in source and binary forms, with or without
|
|
- modification, are permitted provided that the following conditions
|
|
- are met:
|
|
- 1. Redistributions of source code must retain the above copyright
|
|
- notice, this list of conditions and the following disclaimer.
|
|
- 2. Redistributions in binary form must reproduce the above
|
|
- copyright notice, this list of conditions and the following
|
|
- disclaimer in the documentation and/or other materials
|
|
- provided with the distribution.
|
|
-
|
|
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
- ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
|
|
- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
- OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*====================================================================*/
|
|
|
|
/*!
|
|
* \file partify.c
|
|
* <pre>
|
|
*
|
|
* Top level
|
|
* l_int32 partifyFiles()
|
|
* l_int32 partifyPixac()
|
|
*
|
|
* Helpers
|
|
* static BOXA *pixLocateStaveSets()
|
|
* static l_int32 boxaRemoveVGaps()
|
|
* </pre>
|
|
*/
|
|
|
|
#include "allheaders.h"
|
|
|
|
/* Static helplers */
|
|
static BOXA *pixLocateStaveSets(PIX *pixs, l_int32 pageno, PIXA *pixadb);
|
|
static l_ok boxaRemoveVGaps(BOXA *boxa);
|
|
|
|
|
|
/*---------------------------------------------------------------------*
|
|
* Top level *
|
|
*---------------------------------------------------------------------*/
|
|
/*!
|
|
* \brief partifyFiles()
|
|
*
|
|
* \param[in] dirname directory of files
|
|
* \param[in] substr required filename substring; use NULL for all files
|
|
* \param[in] nparts number of parts to generate (counting from top)
|
|
* \param[in] outroot root name of output pdf files
|
|
* \param[in] debugfile [optional] set to NULL for no debug output
|
|
* \return 0 if OK, 1 on error
|
|
*
|
|
* <pre>
|
|
* Notes:
|
|
* (1) All page images are compressed in png format into a pixacomp.
|
|
* (2) Each page image is deskewed, binarized at 300 ppi,
|
|
* partified into %nparts, and saved in a set of pixacomps
|
|
* in tiff-g4 format.
|
|
* (3) Each partified pixacomp is rendered into a set of page images,
|
|
* and output as a pdf.
|
|
* </pre>
|
|
*/
|
|
l_ok
|
|
partifyFiles(const char *dirname,
|
|
const char *substr,
|
|
l_int32 nparts,
|
|
const char *outroot,
|
|
const char *debugfile)
|
|
{
|
|
PIXA *pixadb;
|
|
PIXAC *pixac;
|
|
|
|
PROCNAME("partifyFiles");
|
|
|
|
if (!dirname)
|
|
return ERROR_INT("dirname not defined", procName, 1);
|
|
if (nparts < 0 || nparts > 10)
|
|
return ERROR_INT("nparts not in [1 ... 10]", procName, 1);
|
|
if (!outroot || outroot[0] == '\n')
|
|
return ERROR_INT("outroot undefined or empty", procName, 1);
|
|
|
|
pixadb = (debugfile) ? pixaCreate(0) : NULL;
|
|
pixac = pixacompCreateFromFiles(dirname, substr, IFF_PNG);
|
|
partifyPixac(pixac, nparts, outroot, pixadb);
|
|
if (pixadb) {
|
|
L_INFO("writing debug output to %s\n", procName, debugfile);
|
|
pixaConvertToPdf(pixadb, 300, 1.0, L_FLATE_ENCODE, 0,
|
|
"Partify Debug", debugfile);
|
|
}
|
|
pixacompDestroy(&pixac);
|
|
pixaDestroy(&pixadb);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief partifyPixac()
|
|
*
|
|
* \param[in] pixac with at least one image
|
|
* \param[in] nparts number of parts to generate (counting from top)
|
|
* \param[in] outroot root name of output pdf files
|
|
* \param[in] pixadb [optional] debug pixa; can be NULL
|
|
* \return 0 if OK, 1 on error
|
|
*
|
|
* <pre>
|
|
* Notes:
|
|
* (1) See partifyPixac().
|
|
* (2) If the image files do not have a resolution, 300 ppi is assumed.
|
|
* </pre>
|
|
*/
|
|
l_ok
|
|
partifyPixac(PIXAC *pixac,
|
|
l_int32 nparts,
|
|
const char *outroot,
|
|
PIXA *pixadb)
|
|
{
|
|
char buf[512];
|
|
l_int32 i, j, pageno, res, npage, nbox, icount, line;
|
|
l_float32 factor;
|
|
L_BMF *bmf;
|
|
BOX *box1, *box2;
|
|
BOXA *boxa1, *boxa2, *boxa3;
|
|
PIX *pix1, *pix2, *pix3, *pix4, *pix5;
|
|
PIXAC **pixaca;
|
|
|
|
PROCNAME("partifyPixac");
|
|
|
|
if (!pixac)
|
|
return ERROR_INT("pixac not defined", procName, 1);
|
|
if ((npage = pixacompGetCount(pixac)) == 0)
|
|
return ERROR_INT("pixac is empty", procName, 1);
|
|
if (nparts < 1 || nparts > 10)
|
|
return ERROR_INT("nparts not in [1 ... 10]", procName, 1);
|
|
if (!outroot || outroot[0] == '\n')
|
|
return ERROR_INT("outroot undefined or empty", procName, 1);
|
|
|
|
/* Initialize the output array for each of the nparts */
|
|
pixaca = (PIXAC **)LEPT_CALLOC(nparts, sizeof(PIXAC *));
|
|
for (i = 0; i < nparts; i++)
|
|
pixaca[i] = pixacompCreate(0);
|
|
|
|
/* Process each page */
|
|
line = 1;
|
|
bmf = bmfCreate(NULL, 10);
|
|
for (pageno = 0; pageno < npage; pageno++) {
|
|
if ((pix1 = pixacompGetPix(pixac, pageno)) == NULL) {
|
|
L_ERROR("pix for page %d not found\n", procName, pageno);
|
|
continue;
|
|
}
|
|
|
|
/* Scale, binarize and deskew */
|
|
res = pixGetXRes(pix1);
|
|
if (res == 0 || res == 300 || res > 600) {
|
|
pix2 = pixClone(pix1);
|
|
} else {
|
|
factor = 300.0 / (l_float32)res;
|
|
if (factor > 3)
|
|
L_WARNING("resolution is very low\n", procName);
|
|
pix2 = pixScale(pix1, factor, factor);
|
|
}
|
|
pix3 = pixConvertTo1Adaptive(pix2);
|
|
pix4 = pixDeskew(pix3, 0);
|
|
pixDestroy(&pix1);
|
|
pixDestroy(&pix2);
|
|
pixDestroy(&pix3);
|
|
if (!pix4) {
|
|
L_ERROR("pix for page %d not deskewed\n", procName, pageno);
|
|
continue;
|
|
}
|
|
pix1 = pixClone(pix4); /* rename */
|
|
pixDestroy(&pix4);
|
|
|
|
/* Find the stave sets at 4x reduction */
|
|
boxa1 = pixLocateStaveSets(pix1, pageno, pixadb);
|
|
|
|
/* Break each stave set into the separate staves (parts).
|
|
* A typical set will have more than one part, but if one of
|
|
* the parts is a keyboard, it will usually have two staves
|
|
* (also called a Grand Staff), composed of treble and
|
|
* bass staves. For example, a classical violin sonata
|
|
* could have a staff for the violin and two staves for
|
|
* the piano. We would set nparts == 2, and extract both
|
|
* of the piano staves as the piano part. */
|
|
nbox = boxaGetCount(boxa1);
|
|
fprintf(stderr, "number of boxes in page %d: %d\n", pageno, nbox);
|
|
for (i = 0; i < nbox; i++, line++) {
|
|
snprintf(buf, sizeof(buf), "%d", line);
|
|
box1 = boxaGetBox(boxa1, i, L_COPY);
|
|
pix2 = pixClipRectangle(pix1, box1, NULL);
|
|
pix3 = pixMorphSequence(pix2, "d1.20 + o50.1 + o1.30", 0);
|
|
boxa2 = pixConnCompBB(pix3, 8);
|
|
boxa3 = boxaSort(boxa2, L_SORT_BY_Y, L_SORT_INCREASING, NULL);
|
|
boxaRemoveVGaps(boxa3);
|
|
icount = boxaGetCount(boxa3);
|
|
if (icount < nparts)
|
|
L_WARNING("nparts requested = %d, but only found %d\n",
|
|
procName, nparts, icount);
|
|
for (j = 0; j < icount && j < nparts; j++) {
|
|
box2 = boxaGetBox(boxa3, j, L_COPY);
|
|
if (j == nparts - 1) /* extend the box to the bottom */
|
|
boxSetSideLocations(box2, -1, -1, -1,
|
|
pixGetHeight(pix1) - 1);
|
|
pix4 = pixClipRectangle(pix2, box2, NULL);
|
|
pix5 = pixAddTextlines(pix4, bmf, buf, 1, L_ADD_LEFT);
|
|
pixacompAddPix(pixaca[j], pix5, IFF_TIFF_G4);
|
|
boxDestroy(&box2);
|
|
pixDestroy(&pix4);
|
|
pixDestroy(&pix5);
|
|
}
|
|
boxaDestroy(&boxa2);
|
|
boxaDestroy(&boxa3);
|
|
boxDestroy(&box1);
|
|
pixDestroy(&pix2);
|
|
pixDestroy(&pix3);
|
|
}
|
|
boxaDestroy(&boxa1);
|
|
pixDestroy(&pix1);
|
|
}
|
|
|
|
/* Output separate pdfs for each part */
|
|
for (i = 0; i < nparts; i++) {
|
|
snprintf(buf, sizeof(buf), "%s-%d.pdf", outroot, i);
|
|
L_INFO("writing part %d: %s\n", procName, i, buf);
|
|
pixacompConvertToPdf(pixaca[i], 300, 1.0, L_G4_ENCODE, 0, NULL, buf);
|
|
pixacompDestroy(&pixaca[i]);
|
|
}
|
|
LEPT_FREE(pixaca);
|
|
bmfDestroy(&bmf);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* \brief pixLocateStaveSets()
|
|
*
|
|
* \param[in] pixs 1 bpp, 300 ppi, deskewed
|
|
* \param[in] pageno page number; used for debug output
|
|
* \param[in] pixadb [optional] debug pixa; can be NULL
|
|
* \return boxa containing the stave sets at full resolution
|
|
*/
|
|
static BOXA *
|
|
pixLocateStaveSets(PIX *pixs,
|
|
l_int32 pageno,
|
|
PIXA *pixadb)
|
|
{
|
|
BOXA *boxa1, *boxa2, *boxa3, *boxa4;
|
|
PIX *pix1, *pix2;
|
|
|
|
/* Find the stave sets at 4x reduction */
|
|
pix1 = pixMorphSequence(pixs, "r11", 0);
|
|
boxa1 = pixConnCompBB(pix1, 8);
|
|
boxa2 = boxaSelectByArea(boxa1, 15000, L_SELECT_IF_GT, NULL);
|
|
boxa3 = boxaSort(boxa2, L_SORT_BY_Y, L_SORT_INCREASING, NULL);
|
|
if (pixadb) {
|
|
pix2 = pixConvertTo32(pix1);
|
|
pixRenderBoxaArb(pix2, boxa3, 2, 255, 0, 0);
|
|
pixaAddPix(pixadb, pix2, L_INSERT);
|
|
pixDisplay(pix2, 100 * pageno, 100);
|
|
}
|
|
boxaDestroy(&boxa1);
|
|
boxaDestroy(&boxa2);
|
|
|
|
boxaRemoveVGaps(boxa3);
|
|
if (pixadb) {
|
|
pix2 = pixConvertTo32(pix1);
|
|
pixRenderBoxaArb(pix2, boxa3, 2, 0, 255, 0);
|
|
pixaAddPix(pixadb, pix2, L_INSERT);
|
|
pixDisplay(pix2, 100 * pageno, 600);
|
|
}
|
|
boxa4 = boxaTransform(boxa3, 0, 0, 4.0, 4.0); /* back to full res */
|
|
boxaDestroy(&boxa3);
|
|
pixDestroy(&pix1);
|
|
return boxa4;
|
|
}
|
|
|
|
|
|
/*
|
|
* \brief boxaRemoveVGaps()
|
|
*
|
|
* \param[in] boxa
|
|
* \return 0 if OK, 1 on error
|
|
*
|
|
* <pre>
|
|
* Notes:
|
|
* (1) The boxes in %boxa are aligned vertically. Move the horizontal
|
|
* edges vertically to remove the gaps between boxes.
|
|
* </pre>
|
|
*/
|
|
static l_ok
|
|
boxaRemoveVGaps(BOXA *boxa)
|
|
{
|
|
l_int32 nbox, i, y1, h1, y2, h2, delta;
|
|
|
|
nbox = boxaGetCount(boxa);
|
|
for (i = 0; i < nbox - 1; i++) {
|
|
boxaGetBoxGeometry(boxa, i, NULL, &y1, NULL, &h1);
|
|
boxaGetBoxGeometry(boxa, i + 1, NULL, &y2, NULL, &h2);
|
|
delta = (y2 - y1 - h1) / 2;
|
|
boxaAdjustBoxSides(boxa, i, 0, 0, 0, delta);
|
|
boxaAdjustBoxSides(boxa, i + 1, 0, 0, -delta, 0);
|
|
}
|
|
boxaAdjustBoxSides(boxa, nbox - 1, 0, 0, 0, delta); /* bot of last */
|
|
return 0;
|
|
}
|