add support for sanei_magic to kvs1025 backend

merge-requests/1/head
m. allan noah 2011-06-06 20:52:25 -04:00
rodzic 28ccc11d91
commit e4f634d68e
10 zmienionych plików z 389 dodań i 18 usunięć

Wyświetl plik

@ -2,10 +2,11 @@
* docs/*kvs40xx*, backend/kvs40xx*: New Panasonic KV-S40xx/70xx
backend, originally by Panasonic Russia.
* acinclude.m4, */Makefile.am, configure*: build new kvs40xx backend
* po/POTFILES: add new kvs40xx backend
* po/POTFILES: add kvs* backends
* po/.gitignore: ignore sane-backends.pot
* include/sane/sanei_magic.h, sanei/sanei_magic.c:
add new blank detection and rotation detection routines
* backend/kvs1025*, backend/Makefile*: add support for sanei_magic
2011-06-02 Julien Blache <jb@jblache.org>
* tools/sane-desc.c: add udev+acl output mode, udev rules using ACLs

Wyświetl plik

@ -628,7 +628,7 @@ libkvs1025_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kvs1025
nodist_libsane_kvs1025_la_SOURCES = kvs1025-s.c
libsane_kvs1025_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kvs1025
libsane_kvs1025_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
libsane_kvs1025_la_LIBADD = $(COMMON_LIBS) libkvs1025.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(USB_LIBS) $(RESMGR_LIBS)
libsane_kvs1025_la_LIBADD = $(COMMON_LIBS) libkvs1025.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_magic.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS)
libkvs20xx_la_SOURCES = kvs20xx.c kvs20xx_cmd.c kvs20xx_opt.c \
kvs20xx_cmd.h kvs20xx.h

Wyświetl plik

@ -727,7 +727,8 @@ libsane_kodak_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
libsane_kvs1025_la_DEPENDENCIES = $(COMMON_LIBS) libkvs1025.la \
../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \
../sanei/sanei_config.lo sane_strstatus.lo \
../sanei/sanei_usb.lo $(am__DEPENDENCIES_1) \
../sanei/sanei_usb.lo ../sanei/sanei_magic.lo \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1)
nodist_libsane_kvs1025_la_OBJECTS = libsane_kvs1025_la-kvs1025-s.lo
libsane_kvs1025_la_OBJECTS = $(nodist_libsane_kvs1025_la_OBJECTS)
@ -2127,7 +2128,7 @@ libkvs1025_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kvs1025
nodist_libsane_kvs1025_la_SOURCES = kvs1025-s.c
libsane_kvs1025_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kvs1025
libsane_kvs1025_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
libsane_kvs1025_la_LIBADD = $(COMMON_LIBS) libkvs1025.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo $(USB_LIBS) $(RESMGR_LIBS)
libsane_kvs1025_la_LIBADD = $(COMMON_LIBS) libkvs1025.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo ../sanei/sanei_usb.lo ../sanei/sanei_magic.lo $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS)
libkvs20xx_la_SOURCES = kvs20xx.c kvs20xx_cmd.c kvs20xx_opt.c \
kvs20xx_cmd.h kvs20xx.h

Wyświetl plik

@ -1,6 +1,6 @@
/*
Copyright (C) 2008, Panasonic Russia Ltd.
Copyright (C) 2010, m. allan noah
Copyright (C) 2010-2011, m. allan noah
*/
/* sane - Scanner Access Now Easy.
Panasonic KV-S1020C / KV-S1025C USB scanners.
@ -282,11 +282,11 @@ sane_start (SANE_Handle handle)
{
if (dev->current_side == SIDE_FRONT)
{
/* back image data already read, so just return */
dev->current_side = SIDE_BACK;
/* return; image data already read */
DBG (DBG_proc, "sane_start: exit\n");
return SANE_STATUS_GOOD;
DBG (DBG_proc, "sane_start: duplex back\n");
status = SANE_STATUS_GOOD;
goto cleanup;
}
else
{
@ -326,6 +326,48 @@ sane_start (SANE_Handle handle)
if (status)
return status;
}
/* software based enhancement functions from sanei_magic */
/* these will modify the image, and adjust the params */
/* at this point, we are only looking at the front image */
/* of simplex or duplex data, back side has already exited */
/* so, we do both sides now, if required */
if (dev->val[OPT_SWDESKEW].w){
buffer_deskew(dev,SIDE_FRONT);
}
if (dev->val[OPT_SWCROP].w){
buffer_crop(dev,SIDE_FRONT);
}
if (dev->val[OPT_SWDESPECK].w){
buffer_despeck(dev,SIDE_FRONT);
}
if (dev->val[OPT_SWDEROTATE].w || dev->val[OPT_ROTATE].w){
buffer_rotate(dev,SIDE_FRONT);
}
if (IS_DUPLEX (dev)){
if (dev->val[OPT_SWDESKEW].w){
buffer_deskew(dev,SIDE_BACK);
}
if (dev->val[OPT_SWCROP].w){
buffer_crop(dev,SIDE_BACK);
}
if (dev->val[OPT_SWDESPECK].w){
buffer_despeck(dev,SIDE_BACK);
}
if (dev->val[OPT_SWDEROTATE].w || dev->val[OPT_ROTATE].w){
buffer_rotate(dev,SIDE_BACK);
}
}
cleanup:
/* check if we need to skip this page */
if (dev->val[OPT_SWSKIP].w && buffer_isblank(dev,dev->current_side)){
DBG (DBG_proc, "sane_start: blank page, recurse\n");
return sane_start(handle);
}
DBG (DBG_proc, "sane_start: exit\n");
return status;
}

Wyświetl plik

@ -16,7 +16,7 @@
#define BACKEND_NAME kvs1025
/* Build version */
#define V_BUILD 3
#define V_BUILD 5
/* Paper range supported -- MAX A4 */
#define KV_MAX_X_RANGE 210

Wyświetl plik

@ -27,6 +27,7 @@
#include "../include/sane/sanei_backend.h"
#include "../include/sane/sanei_config.h"
#include "../include/lassert.h"
#include "../include/sane/sanei_magic.h"
#include "kvs1025.h"
#include "kvs1025_low.h"
@ -966,3 +967,212 @@ ReadImageData (PKV_DEV dev, int page)
return status;
}
/* Look in image for likely upper and left paper edges, then rotate
* image so that upper left corner of paper is upper left of image.
* FIXME: should we do this before we binarize instead of after? */
SANE_Status
buffer_deskew(PKV_DEV s, int side)
{
SANE_Status ret = SANE_STATUS_GOOD;
int bg_color = 0xd6;
int side_index = (side == SIDE_FRONT)?0:1;
int resolution = s->val[OPT_RESOLUTION].w;
DBG (10, "buffer_deskew: start\n");
/*only find skew on first image from a page, or if first image had error */
if(side == SIDE_FRONT || s->deskew_stat){
s->deskew_stat = sanei_magic_findSkew(
&s->params[side_index],s->img_buffers[side_index],
resolution,resolution,
&s->deskew_vals[0],&s->deskew_vals[1],&s->deskew_slope);
if(s->deskew_stat){
DBG (5, "buffer_despeck: bad findSkew, bailing\n");
goto cleanup;
}
}
/* backside images can use a 'flipped' version of frontside data */
else{
s->deskew_slope *= -1;
s->deskew_vals[0]
= s->params[side_index].pixels_per_line - s->deskew_vals[0];
}
ret = sanei_magic_rotate(&s->params[side_index],s->img_buffers[side_index],
s->deskew_vals[0],s->deskew_vals[1],s->deskew_slope,bg_color);
if(ret){
DBG(5,"buffer_deskew: rotate error: %d",ret);
ret = SANE_STATUS_GOOD;
goto cleanup;
}
cleanup:
DBG (10, "buffer_deskew: finish\n");
return ret;
}
/* Look in image for likely left/right/bottom paper edges, then crop image.
* Does not attempt to rotate the image, that should be done first.
* FIXME: should we do this before we binarize instead of after? */
SANE_Status
buffer_crop(PKV_DEV s, int side)
{
SANE_Status ret = SANE_STATUS_GOOD;
int side_index = (side == SIDE_FRONT)?0:1;
int resolution = s->val[OPT_RESOLUTION].w;
DBG (10, "buffer_crop: start\n");
/*only find edges on first image from a page, or if first image had error */
if(side == SIDE_FRONT || s->crop_stat){
s->crop_stat = sanei_magic_findEdges(
&s->params[side_index],s->img_buffers[side_index],
resolution,resolution,
&s->crop_vals[0],&s->crop_vals[1],&s->crop_vals[2],&s->crop_vals[3]);
if(s->crop_stat){
DBG (5, "buffer_crop: bad edges, bailing\n");
goto cleanup;
}
DBG (15, "buffer_crop: t:%d b:%d l:%d r:%d\n",
s->crop_vals[0],s->crop_vals[1],s->crop_vals[2],s->crop_vals[3]);
/* we dont listen to the 'top' value, since the top is not padded */
/*s->crop_vals[0] = 0;*/
}
/* backside images can use a 'flipped' version of frontside data */
else{
int left = s->crop_vals[2];
int right = s->crop_vals[3];
s->crop_vals[2] = s->params[side_index].pixels_per_line - right;
s->crop_vals[3] = s->params[side_index].pixels_per_line - left;
}
/* now crop the image */
ret = sanei_magic_crop(&s->params[side_index],s->img_buffers[side_index],
s->crop_vals[0],s->crop_vals[1],s->crop_vals[2],s->crop_vals[3]);
if(ret){
DBG (5, "buffer_crop: bad crop, bailing\n");
ret = SANE_STATUS_GOOD;
goto cleanup;
}
/* update image size counter to new, smaller size */
s->img_size[side_index]
= s->params[side_index].lines * s->params[side_index].bytes_per_line;
cleanup:
DBG (10, "buffer_crop: finish\n");
return ret;
}
/* Look in image for disconnected 'spots' of the requested size.
* Replace the spots with the average color of the surrounding pixels.
* FIXME: should we do this before we binarize instead of after? */
SANE_Status
buffer_despeck(PKV_DEV s, int side)
{
SANE_Status ret = SANE_STATUS_GOOD;
int side_index = (side == SIDE_FRONT)?0:1;
DBG (10, "buffer_despeck: start\n");
ret = sanei_magic_despeck(
&s->params[side_index],s->img_buffers[side_index],s->val[OPT_SWDESPECK].w
);
if(ret){
DBG (5, "buffer_despeck: bad despeck, bailing\n");
ret = SANE_STATUS_GOOD;
goto cleanup;
}
cleanup:
DBG (10, "buffer_despeck: finish\n");
return ret;
}
/* Look if image has too few dark pixels.
* FIXME: should we do this before we binarize instead of after? */
int
buffer_isblank(PKV_DEV s, int side)
{
SANE_Status ret = SANE_STATUS_GOOD;
int side_index = (side == SIDE_FRONT)?0:1;
int status = 0;
DBG (10, "buffer_isblank: start\n");
ret = sanei_magic_isBlank(
&s->params[side_index],s->img_buffers[side_index],
SANE_UNFIX(s->val[OPT_SWSKIP].w)
);
if(ret == SANE_STATUS_NO_DOCS){
DBG (5, "buffer_isblank: blank!\n");
status = 1;
}
else if(ret){
DBG (5, "buffer_isblank: error %d\n",ret);
}
DBG (10, "buffer_isblank: finished\n");
return status;
}
/* Look if image needs rotation
* FIXME: should we do this before we binarize instead of after? */
SANE_Status
buffer_rotate(PKV_DEV s, int side)
{
SANE_Status ret = SANE_STATUS_GOOD;
int angle = 0;
int side_index = (side == SIDE_FRONT)?0:1;
int resolution = s->val[OPT_RESOLUTION].w;
DBG (10, "buffer_rotate: start\n");
if(s->val[OPT_SWDEROTATE].w){
ret = sanei_magic_findTurn(
&s->params[side_index],s->img_buffers[side_index],
resolution,resolution,&angle);
if(ret){
DBG (5, "buffer_rotate: error %d\n",ret);
ret = SANE_STATUS_GOOD;
goto cleanup;
}
}
angle += s->val[OPT_ROTATE].w;
/*90 or 270 degree rotations are reversed on back side*/
if(side == SIDE_BACK && s->val[OPT_ROTATE].w % 180){
angle += 180;
}
ret = sanei_magic_turn(
&s->params[side_index],s->img_buffers[side_index],
angle);
if(ret){
DBG (5, "buffer_rotate: error %d\n",ret);
ret = SANE_STATUS_GOOD;
goto cleanup;
}
/* update image size counter to new, smaller size */
s->img_size[side_index]
= s->params[side_index].lines * s->params[side_index].bytes_per_line;
cleanup:
DBG (10, "buffer_rotate: finished\n");
return ret;
}

Wyświetl plik

@ -130,6 +130,14 @@ typedef enum
OPT_INVERSE, /* Inverse image */
OPT_MIRROR, /* Mirror image */
OPT_JPEG, /* JPEG Compression */
OPT_ROTATE, /* Rotate image */
OPT_SWDESKEW, /* Software deskew */
OPT_SWDESPECK, /* Software despeckle */
OPT_SWDEROTATE, /* Software detect/correct 90 deg. rotation */
OPT_SWCROP, /* Software autocrop */
OPT_SWSKIP, /* Software blank page skip */
/* must come last: */
OPT_NUM_OPTIONS
} KV_OPTION;
@ -180,8 +188,17 @@ typedef struct kv_scanner_dev
int current_page; /* the current page number, 0 is page 1 */
int current_side; /* the current side */
int bytes_to_read[2]; /* bytes to read */
/* Support info */
/* --------------------------------------------------------------------- */
/* values used by the software enhancment code (deskew, crop, etc) */
SANE_Status deskew_stat;
int deskew_vals[2];
double deskew_slope;
SANE_Status crop_stat;
int crop_vals[4];
/* Support info */
KV_SUPPORT_INFO support_info;
SANE_Range x_range, y_range;
@ -264,4 +281,10 @@ SANE_Status ReadImageDataSimplex (PKV_DEV dev, int page);
SANE_Status ReadImageDataDuplex (PKV_DEV dev, int page);
SANE_Status ReadImageData (PKV_DEV dev, int page);
SANE_Status buffer_deskew (PKV_DEV dev, int side);
SANE_Status buffer_crop (PKV_DEV dev, int side);
SANE_Status buffer_despeck (PKV_DEV dev, int side);
int buffer_isblank (PKV_DEV dev, int side);
SANE_Status buffer_rotate(PKV_DEV dev, int side);
#endif /* #ifndef __KVS1025_LOW_H */

Wyświetl plik

@ -255,6 +255,12 @@ static SANE_Range go_value_range = { 0, 255, 0 };
static SANE_Range go_jpeg_compression_range = { 0, 0x64, 0 };
static SANE_Range go_rotate_range = { 0, 270, 90 };
static SANE_Range go_swdespeck_range = { 0, 9, 1 };
static SANE_Range go_swskip_range = { SANE_FIX(0), SANE_FIX(100), 1 };
static const char *go_option_name[] = {
"OPT_NUM_OPTS",
@ -294,9 +300,15 @@ static const char *go_option_name[] = {
"OPT_LAMP", /* Lamp -- color drop out */
"OPT_INVERSE", /* Inverse image */
"OPT_MIRROR", /* Mirror image */
"OPT_JPEG", /* */
"OPT_SEPARATION_SHEET", /* Detect Separation Sheet */
"OPT_CONTROL_SHEET", /* Detect Control Sheet */
"OPT_JPEG", /* JPEG Compression */
"OPT_ROTATE", /* Rotate image */
"OPT_SWDESKEW", /* Software deskew */
"OPT_SWDESPECK", /* Software despeckle */
"OPT_SWDEROTATE", /* Software detect/correct 90 deg. rotation */
"OPT_SWCROP", /* Software autocrop */
"OPT_SWSKIP", /* Software blank page skip */
/* must come last: */
"OPT_NUM_OPTIONS"
};
@ -843,6 +855,67 @@ kv_init_options (PKV_DEV dev)
dev->opt[OPT_JPEG].constraint.range = &(go_jpeg_compression_range);
dev->val[OPT_JPEG].w = 0;
/* Image Rotation */
dev->opt[OPT_ROTATE].name = "rotate";
dev->opt[OPT_ROTATE].title = SANE_I18N ("Rotate image clockwise");
dev->opt[OPT_ROTATE].desc =
SANE_I18N("Request driver to rotate pages by a fixed amount");
dev->opt[OPT_ROTATE].type = SANE_TYPE_INT;
dev->opt[OPT_ROTATE].unit = SANE_UNIT_NONE;
dev->opt[OPT_ROTATE].size = sizeof (SANE_Int);
dev->opt[OPT_ROTATE].constraint_type = SANE_CONSTRAINT_RANGE;
dev->opt[OPT_ROTATE].constraint.range = &(go_rotate_range);
dev->val[OPT_ROTATE].w = 0;
/* Software Deskew */
dev->opt[OPT_SWDESKEW].name = "swdeskew";
dev->opt[OPT_SWDESKEW].title = SANE_I18N ("Software deskew");
dev->opt[OPT_SWDESKEW].desc =
SANE_I18N("Request driver to rotate skewed pages digitally");
dev->opt[OPT_SWDESKEW].type = SANE_TYPE_BOOL;
dev->opt[OPT_SWDESKEW].unit = SANE_UNIT_NONE;
dev->val[OPT_SWDESKEW].w = SANE_FALSE;
/* Software Despeckle */
dev->opt[OPT_SWDESPECK].name = "swdespeck";
dev->opt[OPT_SWDESPECK].title = SANE_I18N ("Software despeckle diameter");
dev->opt[OPT_SWDESPECK].desc =
SANE_I18N("Maximum diameter of lone dots to remove from scan");
dev->opt[OPT_SWDESPECK].type = SANE_TYPE_INT;
dev->opt[OPT_SWDESPECK].unit = SANE_UNIT_NONE;
dev->opt[OPT_SWDESPECK].size = sizeof (SANE_Int);
dev->opt[OPT_SWDESPECK].constraint_type = SANE_CONSTRAINT_RANGE;
dev->opt[OPT_SWDESPECK].constraint.range = &(go_swdespeck_range);
dev->val[OPT_SWDESPECK].w = 0;
/* Software Derotate */
dev->opt[OPT_SWDEROTATE].name = "swderotate";
dev->opt[OPT_SWDEROTATE].title = SANE_I18N ("Software derotate");
dev->opt[OPT_SWDEROTATE].desc =
SANE_I18N("Request driver to detect and correct 90 degree image rotation");
dev->opt[OPT_SWDEROTATE].type = SANE_TYPE_BOOL;
dev->opt[OPT_SWDEROTATE].unit = SANE_UNIT_NONE;
dev->val[OPT_SWDEROTATE].w = SANE_FALSE;
/* Software Autocrop*/
dev->opt[OPT_SWCROP].name = "swcrop";
dev->opt[OPT_SWCROP].title = SANE_I18N ("Software automatic cropping");
dev->opt[OPT_SWCROP].desc =
SANE_I18N("Request driver to remove border from pages digitally");
dev->opt[OPT_SWCROP].type = SANE_TYPE_BOOL;
dev->opt[OPT_SWCROP].unit = SANE_UNIT_NONE;
dev->val[OPT_SWCROP].w = SANE_FALSE;
/* Software blank page skip */
dev->opt[OPT_SWSKIP].name = "swskip";
dev->opt[OPT_SWSKIP].title = SANE_I18N ("Software blank skip percentage");
dev->opt[OPT_SWSKIP].desc
= SANE_I18N("Request driver to discard pages with low numbers of dark pixels");
dev->opt[OPT_SWSKIP].type = SANE_TYPE_FIXED;
dev->opt[OPT_SWSKIP].unit = SANE_UNIT_PERCENT;
dev->opt[OPT_SWSKIP].constraint_type = SANE_CONSTRAINT_RANGE;
dev->opt[OPT_SWSKIP].constraint.range = &(go_swskip_range);
/* Lastly, set the default scan mode. This might change some
* values previously set here. */
sane_control_option (dev, OPT_PAPER_SIZE, SANE_ACTION_SET_VALUE,
@ -918,6 +991,12 @@ kv_control_option (PKV_DEV dev, SANE_Int option,
case OPT_MIRROR:
case OPT_FEED_TIMEOUT:
case OPT_JPEG:
case OPT_ROTATE:
case OPT_SWDESKEW:
case OPT_SWDESPECK:
case OPT_SWDEROTATE:
case OPT_SWCROP:
case OPT_SWSKIP:
case OPT_FIT_TO_PAGE:
*(SANE_Word *) val = dev->val[option].w;
DBG (DBG_error, "opt value = %d\n", *(SANE_Word *) val);
@ -1083,6 +1162,12 @@ kv_control_option (PKV_DEV dev, SANE_Int option,
case OPT_MIRROR:
case OPT_AUTOMATIC_SEPARATION:
case OPT_JPEG:
case OPT_ROTATE:
case OPT_SWDESKEW:
case OPT_SWDESPECK:
case OPT_SWDEROTATE:
case OPT_SWCROP:
case OPT_SWSKIP:
case OPT_FIT_TO_PAGE:
dev->val[option].w = *(SANE_Word *) val;
return SANE_STATUS_GOOD;

Wyświetl plik

@ -77,7 +77,10 @@ attach_scanner_usb (const char *device_name)
strcpy (dev->scsi_type_str, "ADF Scanner");
strcpy (dev->scsi_vendor, "Panasonic");
strcpy (dev->scsi_product,
product == (int) KV_S1025C ? "KV-S1025C" : "KV-S1020C");
product == (int) KV_S1020C ? "KV-S1020C" :
product == (int) KV_S1025C ? "KV-S1025C" :
product == (int) KV_S1045C ? "KV-S1045C" :
"KV-S10xxC");
strcpy (dev->scsi_version, "1.00");
/* Set SANE_Device */

Wyświetl plik

@ -30,9 +30,6 @@
../backend/genesys.c
../backend/kodak.c
../backend/kodak.h
../backend/gt68xx.c
../backend/gt68xx_low.h
@ -43,6 +40,15 @@
../backend/hp-option.c
../backend/hp-option.h
../backend/kodak.c
../backend/kodak.h
../backend/kvs1025.h
../backend/kvs1025_opt.c
../backend/kvs20xx.c
../backend/kvs20xx_opt.c
../backend/kvs40xx.c
../backend/kvs40xx_opt.c