Merge pull request #962 from hugovk/sane

Remove Sane directory now it has its own repo
This commit is contained in:
Alex Clark ☺ 2014-10-15 09:21:58 -04:00
commit ffa8852ac0
8 changed files with 0 additions and 2143 deletions

View File

@ -1,34 +0,0 @@
from V1.0 to V2.0
_sane.c:
- Values for option constraints are correctly translated to floats
if value type is TYPE_FIXED for SANE_CONSTRAINT_RANGE and
SANE_CONSTRAINT_WORD_LIST
- added constants INFO_INEXACT, INFO_RELOAD_OPTIONS,
INFO_RELOAD_PARAMS (possible return values of set_option())
to module dictionnary.
- removed additional return variable 'i' from SaneDev_get_option(),
because it is only set when SANE_ACTION_SET_VALUE is used.
- scanDev.get_parameters() now returns the scanner mode as 'format',
no more the typical PIL codes. So 'L' became 'gray', 'RGB' is now
'color', 'R' is 'red', 'G' is 'green', 'B' is 'red'. This matches
the way scanDev.mode is set.
This should be the only incompatibility vs. version 1.0.
sane.py
- ScanDev got new method __load_option_dict() called from __init__()
and from __setattr__() if backend reported that the frontend should
reload the options.
- Nice human-readable __repr__() method added for class Option
- if __setattr__ (i.e. set_option) reports that all other options
have to be reloaded due to a change in the backend then they are reloaded.
- due to the change in SaneDev_get_option() only the 'value' is
returned from get_option().
- in __setattr__ integer values are automatically converted to floats
if SANE backend expects SANE_FIXED (i.e. fix-point float)
- The scanner options can now directly be accessed via scanDev[optionName]
instead scanDev.opt[optionName]. (The old way still works).
V1.0:
A.M. Kuchling's original pysane package.

View File

@ -1,22 +0,0 @@
Python SANE module V1.1 (30 Sep. 2004)
================================================================================
The SANE module provides an interface to the SANE scanner and frame
grabber interface for Linux. This module was contributed by Andrew
Kuchling and is extended and currently maintained by Ralph Heinkel
(rheinkel-at-email.de). If you write to me please make sure to have the
word 'SANE' or 'sane' in the subject of your mail, otherwise it might
be classified as spam in the future.
To build this module, type (in the Sane directory)::
python setup.py build
In order to install the module type::
python setup.py install
For some basic documentation please look at the file sanedoc.txt
The two demo_*.py scripts give basic examples on how to use the software.

File diff suppressed because it is too large Load Diff

View File

@ -1,41 +0,0 @@
#!/usr/bin/env python
#
# Shows how to scan a 16 bit grayscale image into a numarray object
#
from __future__ import print_function
# Get the path set up to find PIL modules if not installed yet:
import sys ; sys.path.append('../PIL')
from numarray import *
import sane
import Image
def toImage(arr):
if arr.type().bytes == 1:
# need to swap coordinates btw array and image (with [::-1])
im = Image.frombytes('L', arr.shape[::-1], arr.tostring())
else:
arr_c = arr - arr.min()
arr_c *= (255./arr_c.max())
arr = arr_c.astype(UInt8)
# need to swap coordinates btw array and image (with [::-1])
im = Image.frombytes('L', arr.shape[::-1], arr.tostring())
return im
print('SANE version:', sane.init())
print('Available devices=', sane.get_devices())
s = sane.open(sane.get_devices()[0][0])
# Set scan parameters
s.mode = 'gray'
s.br_x=320. ; s.br_y=240.
print('Device parameters:', s.get_parameters())
s.depth=16
arr16 = s.arr_scan()
toImage(arr16).show()

View File

@ -1,35 +0,0 @@
#!/usr/bin/env python
#
# Shows how to scan a color image into a PIL rgb-image
#
from __future__ import print_function
# Get the path set up to find PIL modules if not installed yet:
import sys ; sys.path.append('../PIL')
import sane
print('SANE version:', sane.init())
print('Available devices=', sane.get_devices())
s = sane.open(sane.get_devices()[0][0])
s.mode = 'color'
s.br_x=320. ; s.br_y=240.
print('Device parameters:', s.get_parameters())
# Initiate the scan
s.start()
# Get an Image object
# (For my B&W QuickCam, this is a grey-scale image. Other scanning devices
# may return a
im=s.snap()
# Write the image out as a GIF file
#im.save('foo.gif')
# The show method() simply saves the image to a temporary file and calls "xv".
im.show()

View File

@ -1,288 +0,0 @@
# sane.py
#
# Python wrapper on top of the _sane module, which is in turn a very
# thin wrapper on top of the SANE library. For a complete understanding
# of SANE, consult the documentation at the SANE home page:
# http://www.mostang.com/sane/ .
__version__ = '2.0'
__author__ = ['Andrew Kuchling', 'Ralph Heinkel']
from PIL import Image
import _sane
from _sane import *
TYPE_STR = { TYPE_BOOL: "TYPE_BOOL", TYPE_INT: "TYPE_INT",
TYPE_FIXED: "TYPE_FIXED", TYPE_STRING: "TYPE_STRING",
TYPE_BUTTON: "TYPE_BUTTON", TYPE_GROUP: "TYPE_GROUP" }
UNIT_STR = { UNIT_NONE: "UNIT_NONE",
UNIT_PIXEL: "UNIT_PIXEL",
UNIT_BIT: "UNIT_BIT",
UNIT_MM: "UNIT_MM",
UNIT_DPI: "UNIT_DPI",
UNIT_PERCENT: "UNIT_PERCENT",
UNIT_MICROSECOND: "UNIT_MICROSECOND" }
class Option:
"""Class representing a SANE option.
Attributes:
index -- number from 0 to n, giving the option number
name -- a string uniquely identifying the option
title -- single-line string containing a title for the option
desc -- a long string describing the option; useful as a help message
type -- type of this option. Possible values: TYPE_BOOL,
TYPE_INT, TYPE_STRING, and so forth.
unit -- units of this option. Possible values: UNIT_NONE,
UNIT_PIXEL, etc.
size -- size of the value in bytes
cap -- capabilities available; CAP_EMULATED, CAP_SOFT_SELECT, etc.
constraint -- constraint on values. Possible values:
None : No constraint
(min,max,step) Integer values, from min to max, stepping by
list of integers or strings: only the listed values are allowed
"""
def __init__(self, args, scanDev):
self.scanDev = scanDev # needed to get current value of this option
self.index, self.name = args[0], args[1]
self.title, self.desc = args[2], args[3]
self.type, self.unit = args[4], args[5]
self.size, self.cap = args[6], args[7]
self.constraint = args[8]
def f(x):
if x=='-': return '_'
else: return x
if not isinstance(self.name, str): self.py_name=str(self.name)
else: self.py_name=''.join(map(f, self.name))
def is_active(self):
return _sane.OPTION_IS_ACTIVE(self.cap)
def is_settable(self):
return _sane.OPTION_IS_SETTABLE(self.cap)
def __repr__(self):
if self.is_settable():
settable = 'yes'
else:
settable = 'no'
if self.is_active():
active = 'yes'
curValue = repr(getattr(self.scanDev, self.py_name))
else:
active = 'no'
curValue = '<not available, inactive option>'
s = """\nName: %s
Cur value: %s
Index: %d
Title: %s
Desc: %s
Type: %s
Unit: %s
Constr: %s
active: %s
settable: %s\n""" % (self.py_name, curValue,
self.index, self.title, self.desc,
TYPE_STR[self.type], UNIT_STR[self.unit],
repr(self.constraint), active, settable)
return s
class _SaneIterator:
""" intended for ADF scans.
"""
def __init__(self, device):
self.device = device
def __iter__(self):
return self
def __del__(self):
self.device.cancel()
def next(self):
try:
self.device.start()
except error as v:
if v == 'Document feeder out of documents':
raise StopIteration
else:
raise
return self.device.snap(1)
class SaneDev:
"""Class representing a SANE device.
Methods:
start() -- initiate a scan, using the current settings
snap() -- snap a picture, returning an Image object
arr_snap() -- snap a picture, returning a numarray object
cancel() -- cancel an in-progress scanning operation
fileno() -- return the file descriptor for the scanner (handy for select)
Also available, but rather low-level:
get_parameters() -- get the current parameter settings of the device
get_options() -- return a list of tuples describing all the options.
Attributes:
optlist -- list of option names
You can also access an option name to retrieve its value, and to
set it. For example, if one option has a .name attribute of
imagemode, and scanner is a SaneDev object, you can do:
print scanner.imagemode
scanner.imagemode = 'Full frame'
scanner.['imagemode'] returns the corresponding Option object.
"""
def __init__(self, devname):
d=self.__dict__
d['sane_signature'] = self._getSaneSignature(devname)
d['scanner_model'] = d['sane_signature'][1:3]
d['dev'] = _sane._open(devname)
self.__load_option_dict()
def _getSaneSignature(self, devname):
devices = get_devices()
if not devices:
raise RuntimeError('no scanner available')
for dev in devices:
if devname == dev[0]:
return dev
raise RuntimeError('no such scan device "%s"' % devname)
def __load_option_dict(self):
d=self.__dict__
d['opt']={}
optlist=d['dev'].get_options()
for t in optlist:
o=Option(t, self)
if o.type!=TYPE_GROUP:
d['opt'][o.py_name]=o
def __setattr__(self, key, value):
dev=self.__dict__['dev']
optdict=self.__dict__['opt']
if key not in optdict:
self.__dict__[key]=value ; return
opt=optdict[key]
if opt.type==TYPE_GROUP:
raise AttributeError("Groups can't be set: "+key)
if not _sane.OPTION_IS_ACTIVE(opt.cap):
raise AttributeError('Inactive option: '+key)
if not _sane.OPTION_IS_SETTABLE(opt.cap):
raise AttributeError("Option can't be set by software: "+key)
if isinstance(value, int) and opt.type == TYPE_FIXED:
# avoid annoying errors of backend if int is given instead float:
value = float(value)
self.last_opt = dev.set_option(opt.index, value)
# do binary AND to find if we have to reload options:
if self.last_opt & INFO_RELOAD_OPTIONS:
self.__load_option_dict()
def __getattr__(self, key):
dev=self.__dict__['dev']
optdict=self.__dict__['opt']
if key=='optlist':
return list(self.opt.keys())
if key=='area':
return (self.tl_x, self.tl_y),(self.br_x, self.br_y)
if key not in optdict:
raise AttributeError('No such attribute: '+key)
opt=optdict[key]
if opt.type==TYPE_BUTTON:
raise AttributeError("Buttons don't have values: "+key)
if opt.type==TYPE_GROUP:
raise AttributeError("Groups don't have values: "+key)
if not _sane.OPTION_IS_ACTIVE(opt.cap):
raise AttributeError('Inactive option: '+key)
value = dev.get_option(opt.index)
return value
def __getitem__(self, key):
return self.opt[key]
def get_parameters(self):
"""Return a 5-tuple holding all the current device settings:
(format, last_frame, (pixels_per_line, lines), depth, bytes_per_line)
- format is one of 'L' (grey), 'RGB', 'R' (red), 'G' (green), 'B' (blue).
- last_frame [bool] indicates if this is the last frame of a multi frame image
- (pixels_per_line, lines) specifies the size of the scanned image (x,y)
- lines denotes the number of scanlines per frame
- depth gives number of pixels per sample
"""
return self.dev.get_parameters()
def get_options(self):
"Return a list of tuples describing all the available options"
return self.dev.get_options()
def start(self):
"Initiate a scanning operation"
return self.dev.start()
def cancel(self):
"Cancel an in-progress scanning operation"
return self.dev.cancel()
def snap(self, no_cancel=0):
"Snap a picture, returning a PIL image object with the results"
(mode, last_frame,
(xsize, ysize), depth, bytes_per_line) = self.get_parameters()
if mode in ['gray', 'red', 'green', 'blue']:
format = 'L'
elif mode == 'color':
format = 'RGB'
else:
raise ValueError('got unknown "mode" from self.get_parameters()')
im=Image.new(format, (xsize,ysize))
self.dev.snap( im.im.id, no_cancel )
return im
def scan(self):
self.start()
return self.snap()
def multi_scan(self):
return _SaneIterator(self)
def arr_snap(self, multipleOf=1):
"""Snap a picture, returning a numarray object with the results.
By default the resulting array has the same number of pixels per
line as specified in self.get_parameters()[2][0]
However sometimes it is necessary to obtain arrays where
the number of pixels per line is e.g. a multiple of 4. This can then
be achieved with the option 'multipleOf=4'. So if the scanner
scanned 34 pixels per line, you will obtain an array with 32 pixels
per line.
"""
(mode, last_frame, (xsize, ysize), depth, bpl) = self.get_parameters()
if not mode in ['gray', 'red', 'green', 'blue']:
raise RuntimeError('arr_snap() only works with monochrome images')
if multipleOf < 1:
raise ValueError('option "multipleOf" must be a positive number')
elif multipleOf > 1:
pixels_per_line = xsize - divmod(xsize, 4)[1]
else:
pixels_per_line = xsize
return self.dev.arr_snap(pixels_per_line)
def arr_scan(self, multipleOf=1):
self.start()
return self.arr_snap(multipleOf=multipleOf)
def fileno(self):
"Return the file descriptor for the scanning device"
return self.dev.fileno()
def close(self):
self.dev.close()
def open(devname):
"Open a device for scanning"
new=SaneDev(devname)
return new

View File

@ -1,294 +0,0 @@
The _sane_ module is an Python interface to the SANE (Scanning is Now
Easy) library, which provides access to various raster scanning
devices such as flatbed scanners and digital cameras. For more
information about SANE, consult the SANE Web site at
http://www.mostang.com/sane/ . Note that this
documentation doesn't duplicate all the information in the SANE
documentation, which you must also consult to get a complete
understanding.
This module has been originally developed by A.M. Kuchling (amk1@erols.com),
now development has been taken over by Ralph Heinkel (rheinkel-at-email.de).
If you write to me please make sure to have the word 'SANE' or 'sane' in
the subject of your mail, otherwise it might be classified as spam in the
future.
The module exports two object types, a bunch of constants, and two
functions.
get_devices()
Return a list of 4-tuples containing the available scanning
devices. Each tuple contains 4 strings: the device name, suitable for
passing to _open()_; the device's vendor; the model; and the type of
device, such as 'virtual device' or 'video camera'.
>>> import sane ; sane.get_devices()
[('epson:libusb:001:004', 'Epson', 'GT-8300', 'flatbed scanner')]
open(devicename)
Open a device, given a string containing its name. SANE
devices have names like 'epson:libusb:001:004'. If the attempt
to open the device fails, a _sane.error_ exception will be raised. If
there are no problems, a SaneDev object will be returned.
As an easy way to open the scanner (if only one is available) just type
>>> sane.open(sane.get_devices()[0][0])
SaneDev objects
===============
The basic process of scanning an image consists of getting a SaneDev
object for the device, setting various parameters, starting the scan,
and then reading the image data. Images are composed of one or more
frames; greyscale and one-pass colour scanners return a single frame
containing all the image data, but 3-pass scanners will usually return
3 frames, one for each of the red, green, blue channels.
Methods:
--------
fileno()
Returns a file descriptor for the scanning device. This
method's existence means that SaneDev objects can be used by the
select module.
get_parameters()
Return a tuple containing information about the current settings of
the device and the current frame: (format, last_frame,
pixels_per_line, lines, depth, bytes_per_line).
mode -- 'gray' for greyscale image, 'color' for RGB image, or
one of 'red', 'green', 'blue' if the image is a single
channel of an RGB image (from PIL's point of view,
this is equivalent to 'L').
last_frame -- A Boolean value, which is true if this is the
last frame of the image, and false otherwise.
pixels_per_line -- Width of the frame.
lines -- Height of the frame.
depth -- Depth of the image, measured in bits. SANE will only
allow using 8, 16, or 24-bit depths.
bytes_per_line -- Bytes required to store a single line of
data, as computed from pixels_per_line and depth.
start()
Start a scan. This function must be called before the
_snap()_ method can be used.
cancel()
Cancel a scan already in progress.
snap(no_cancel=0)
Snap a single frame of data, returning a PIL Image object
containing the data. If no_cancel is false, the Sane library function
sane_cancel is called after the scan. This is reasonable in most cases,
but may cause backends for duplex ADF scanners to drop the backside image,
when snap() is called for the front side image. If no_cancel is true,
cancel() should be called manually, after all scans are finished.
scan()
This is just a shortcut for s.start(); s.snap()
Returns a PIL image
multi_scan()
This method returns an iterator. It is intended to be used for
scanning with an automatic document feeder. The next() method of the
iterator tries to start a scan. If this is successful, it returns a
PIL Image object, like scan(); if the document feeder runs out of
paper, it raises StopIteration, thereby signaling that the sequence
is ran out of items.
arr_snap(multipleOf=1)
same as snap, but the result is a NumArray object. (Not that
num_array must be installed already at compilation time, otherwise
this feature will not be activated).
By default the resulting array has the same number of pixels per
line as specified in self.get_parameters()[2][0]
However sometimes it is necessary to obtain arrays where
the number of pixels per line is e.g. a multiple of 4. This can then
be achieved with the option 'multipleOf=4'. So if the scanner
scanned 34 pixels per line, you will obtain an array with 32 pixels
per line.
Note that this only works with monochrome images (e.g. gray-scales)
arr_scan(multipleOf=1)
This is just a shortcut for s.start(); s.arr_snap(multipleOf=1)
Returns a NumArray object
close()
Closes the object.
Attributes:
-----------
SaneDev objects have a few fixed attributes which are always
available, and a larger collection of attributes which vary depending
on the device. An Epson 1660 photo scanner has attributes like
'mode', 'depth', etc.
Another (pseudo scanner), the _pnm:0_ device, takes a PNM file and
simulates a scanner using the image data; a SaneDev object
representing the _pnm:0_ device therefore has a _filename_ attribute
which can be changed to specify the filename, _contrast_ and
_brightness_ attributes to modify the returned image, and so forth.
The values of the scanner options may be an integer, floating-point
value, or string, depending on the nature of the option.
sane_signature
The tuple for this scandev that is returned by sane.get_devices()
e.g. ('epson:libusb:001:006', 'Epson', 'GT-8300', 'flatbed scanner')
scanner_model
same as sane_signature[1:3], i.e. ('Epson', 'GT-8300') for the case above.
optlist
A list containing the all the options supported by this device.
>>> import sane ; s=sane.open('epson:libusb:001:004') ; s.optlist
['focus_position', 'color_correction', 'sharpness', ...., 'br_x']
A closer look at all options listed in s.optlist can be obtained
through the SaneOption objects.
SaneOption objects
==================
SANE's option handling is its most elaborate subsystem, intended to
allow automatically generating dialog boxes and prompts for user
configuration of the scanning device. The SaneOption object can be
used to get a human-readable name and description for an option, the
units to use, and what the legal values are. No information about the
current value of the option is available; for that, read the
corresponding attribute of a SaneDev object.
This documentation does not explain all the details of SANE's option
handling; consult the SANE documentation for all the details.
A scandevice option is accessed via __getitem__. For example
s['mode'] returns the option descriptor for the mode-option which
controls whether the scanner works in color, grayscale, or b/w mode.
>>> s['mode']
Name: mode
Cur value: Color
Index: 2
Title: Scan mode
Desc: Selects the scan mode (e.g., lineart, monochrome, or color).
Type: TYPE_STRING
Unit: UNIT_NONE
Constr: ['Binary', 'Gray', 'Color']
active: yes
settable: yes
In order to change 'mode' to 'gray', just type:
>>> s.mode = 'gray'
With the attributes and methods of sane-option objects it is possible
to access individual option values:
is_active()
Returns true if the option is active.
is_settable()
Returns true if the option can be set under software control.
Attributes:
cap
An integer containing various flags about the object's
capabilities; whether it's active, whether it's settable, etc. Also
available as the _capability_ attribute.
constraint
The constraint placed on the value of this option. If it's
_None_, there are essentially no constraint of the value. It may also
be a list of integers or strings, in which case the value *must* be
one of the possibilities in the list. Numeric values may have a
3-tuple as the constraint; this 3-tuple contains _(minimum, maximum,
increment)_, and the value must be in the defined range.
desc
A lengthy description of what the option does; it may be shown
to the user for clarification.
index
An integer giving the option's index in the option list.
name
A short name for the option, as it comes from the sane-backend.
py_name
The option's name, as a legal Python identifier. The name
attribute may contain the '-' character, so it will be converted to
'_' for the py_name attribute.
size
For a string-valued option, this is the maximum length allowed.
title
A single-line string that can be used as a title string.
type
A constant giving the type of this option: will be one of the following
constants found in the SANE module:
TYPE_BOOL
TYPE_INT
TYPE_FIXED
TYPE_STRING
TYPE_BUTTON
TYPE_GROUP
unit
For numeric-valued options, this is a constant representing
the unit used for this option. It will be one of the following
constants found in the SANE module:
UNIT_NONE
UNIT_PIXEL
UNIT_BIT
UNIT_MM
UNIT_DPI
UNIT_PERCENT
Example us usage:
=================
>>> import sane
>>> print 'SANE version:', sane.init()
>>> print 'Available devices=', sane.get_devices()
SANE version: (16777230, 1, 0, 14)
>>> s = sane.open(sane.get_devices()[0][0])
>>> print 'Device parameters:', s.get_parameters()
Device parameters: ('L', 1, (424, 585), 1, 53)
>>> print s.resolution
50
## In order to scan a color image into a PIL object:
>>> s.mode = 'color'
>>> s.start()
>>> img = s.snap()
>>> img.show()
## In order to obtain a 16-bit grayscale image at 100DPI in a numarray object
## with bottom-right coordinates set to (160, 120) [in millimeter] :
>>> s.mode = 'gray'
>>> s.br_x=160. ; s.br_y=120.
>>> s.resolution = 100
>>> s.depth=16
>>> s.start()
>>> s.get_parameters()[2] # just check the size
(624, 472)
>>> arr16 = s.arr_snap()
>>> arr16
array([[63957, 64721, 65067, ..., 65535, 65535, 65535],
[63892, 64342, 64236, ..., 65535, 65535, 65535],
[64286, 64248, 64705, ..., 65535, 65535, 65535],
...,
[65518, 65249, 65058, ..., 65535, 65535, 65535],
[64435, 65047, 65081, ..., 65535, 65535, 65535],
[65309, 65438, 65535, ..., 65535, 65535, 65535]], type=UInt16)
>>> arr16.shape # inverse order of coordinates, first y, then x!
(472, 624)

View File

@ -1,24 +0,0 @@
from distutils.core import setup, Extension
PIL_BUILD_DIR = '..'
PIL_IMAGING_DIR = PIL_BUILD_DIR+'/libImaging'
defs = []
try:
import numarray
defs.append(('WITH_NUMARRAY',None))
except ImportError:
pass
sane = Extension('_sane',
include_dirs = [PIL_IMAGING_DIR],
libraries = ['sane'],
library_dirs = [PIL_IMAGING_DIR],
define_macros = defs,
sources = ['_sane.c'])
setup (name = 'pysane',
version = '2.0',
description = 'This is the pysane package',
py_modules = ['sane'],
ext_modules = [sane])