python-pillow / sane Goto Github PK
View Code? Open in Web Editor NEWPython interface to the SANE scanner and frame grabber
License: Other
Python interface to the SANE scanner and frame grabber
License: Other
Transferred from python-pillow/Pillow#1052
from @hanishkvc
Hi,
I am trying to use python-sane to access a Sane supported Scanner which uses the sane-genesys backend (from the device name), also sane-genesys man page matchs it Ambir PS665.
After init,open,start on doing snap, it fails with a MemoryError in PIL Image. On debugging it further found that basically get_parameters returns a size of (318,-1). And Because of this ysize of -1, PIL Image fails and inturn snap() fails.
As I am not much of a python expert and equally new to sane, for now I have modified sane.py snap code to check if ysize is -1 and then change it to xsize*3. But you all will have a better idea of calculating ysize, maybe using geometry info (I am assuming) or so. So please look at fixing this issue in python-sane by checking if xsize,ysize are valid or not and inturn mapping to some saner calculated value.
When I follow your description I get the following error:
PS D:\devel\python-sane-2.9.1\python-sane-2.9.1> python setup.py build
running build
running build_py
creating build
creating build\lib.win-amd64-cpython-310
copying sane.py -> build\lib.win-amd64-cpython-310
running build_ext
building '_sane' extension
creating build\temp.win-amd64-cpython-310
creating build\temp.win-amd64-cpython-310\Release
"C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.35.32215\bin\HostX86\x64\cl.exe" /c /nologo /O2 /W3 /GL /DNDEBUG /MD "-IC:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\include" "-IC:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\Include" "-IC:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.35.32215\include" "-IC:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.35.32215\ATLMFC\include" "-IC:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\VS\include" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.22000.0\ucrt" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.22000.0\um" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.22000.0\shared" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.22000.0\winrt" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.22000.0\cppwinrt" /Tc_sane.c /Fobuild\temp.win-amd64-cpython-310\Release_sane.obj
_sane.c
_sane.c(30): fatal error C1083: Datei (Include) kann nicht geöffnet werden: "sane/sane.h": No such file or directory
error: command 'C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.35.32215\bin\HostX86\x64\cl.exe' failed with exit code 2
OS X:
$ python setup.py build
running build
running build_py
running build_ext
building '_sane' extension
clang -fno-strict-aliasing -fno-common -dynamic -I/usr/local/include -I/usr/local/opt/sqlite/include -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -I/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c _sane.c -o build/temp.macosx-10.9-x86_64-2.7/_sane.o
_sane.c:29:10: fatal error: 'sane/sane.h' file not found
#include <sane/sane.h>
^
1 error generated.
error: command 'clang' failed with exit status 1
$
$ python setup.py install
running install
running build
running build_py
running build_ext
building '_sane' extension
clang -fno-strict-aliasing -fno-common -dynamic -I/usr/local/include -I/usr/local/opt/sqlite/include -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -I/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c _sane.c -o build/temp.macosx-10.9-x86_64-2.7/_sane.o
_sane.c:29:10: fatal error: 'sane/sane.h' file not found
#include <sane/sane.h>
^
1 error generated.
error: command 'clang' failed with exit status 1
$
Similar error on Linux. Where's sane.h?
_sane.c(30): fatal error C1083: Cannot open include file: 'sane/sane.h': No such file or directory
error: command 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.38.33130\bin\HostX86\x64\cl.exe' failed with exit code 2
In the past days I have been using this sane.py to connect to my scanner and did find some "quirks" (while learning a lot). See the list below. Except for point "f)", I think I have also found the reason and/or a solution. Could you please verify my comments, and make repairs where you think it is necessary. (running python 2.7.6, on ubuntu 14.04/ linux 3.13.0-58 , 64 bit, but I don't think this matters much).
To summarize: bitdepth-1 images are garbled and black/white reversed. While trying to resolve these, I met some other, minor, inconveniences. I start with those, the easy ones.
Thanks.
= = = = = =
sane.py refers to
version = '2.8.1'
author = ['Andrew Kuchling', 'Ralph Heinkel', 'Sandro Mani']
a) in snap() the error Document feeder out of docs... will never be risen
in sane.py
line 125 : if e == 'Document feeder out of documents':
should be: if str(e) == 'Document feeder out of documents':
b) in Option.init()
line 65: self.py_name = self.name.replace("-", "_")
somewhere else a similiar replacement is necessary, as the sanedev getoptions() still shows the "-"
i.e. minus signs e.g. tl-x, tl-y, which causes confusion.
The replacement of minus by underscore should be mentioned in the documentation, as it is confusing
when the same scanner is approched by different libsane frontends
c) in _sane.c
line 488: imgBuf[imgBufOffset + i] = lineBuf[i / 8] & (0x80 >> (i % 8)) ? 0 : 255;
the Sane spec (version 1.05, end of section 3.2.1) states that for 1-bit images zero is white and one is black, while for
other (gray, R, G, B) zero is darkest and one 1 brightest
should be: imgBuf[imgBufOffset + i] = lineBuf[i / 8] & (0x80 >> (i % 8)) ? 255 : 0;
d) in section 4.3.8 of the sane spec (v1.05) it is stated that the number of bytes per line should be calculated differently
in case if bit-depth 1. It should be rounded up to nearest 8-fold.
byter per line = nrchannles * ( (nrpixelsperline + 7) / 8 ) if bitdepth == 1
this influences _sane.c
line 428: int imgBytesPerLine = imgPixelsPerLine * imgSampelsPerPixel * imgSampleSize;
which is only correct for bitdepths > 1 (I do not know whether bitdepths 2 through 7 can be found "in the wild".)
Bitdepth-1 images are returned by sane packed 8 pixels in a byte, padded to the next byte boundary.
_sane.c returns bitdepth-1 images (PIL(LOW) style) as byte per pixel, which conversion, IMHO, should be a separate step or option.
In fact the bytes_per_line returned by the get_params() should be used and checked against one own calculation, as sane backends may
return more bytes_per_line than the minimum required amount.
e) in -sane.c
lines 411,412:
int allow16bitsamples = 0;
if(!PyArg_ParseTuple(args, "|ii", &noCancel, &allow16bitsamples))
This "allow16bitsamples" option is not in the documentation (or is it not meant to be a documented feature?)
f) the number of bytes read, as returnred by get_params() is not equal to the size of the PIL.Image, even if mode = gray.
in _sane.c nRead and lineBufUsed seem - to me - not to be checked against a get_params immediately after the scan.
I could not find the reason for this, even though I expect that an integer division where a float division is needed might be the cause.
Would you mind renaming the master branch to main, as per python-pillow/Pillow#5671?
It should be a simple matter of going to https://github.com/python-pillow/Sane/settings/branches and renaming the default branch to main.
In your build instructions, you might want to mention that SANE development packages are required to build this package. On Fedora, for example, I had to install the package sane-backends-devel. Before that, the built failed with the error message "fatal error: sane/sane.h: No such file or directory".
I've realised that there is no LICENSE for this repository. Anyone have any thoughts on that?
If I e.g. use the iterator to scan 3 pages, a 4th one is also fed. Below is a minimal example which scans one page but feeds two sheets.
#!/usr/bin/env python
import sane
import numpy
from PIL import Image
#
# Initialize sane
#
ver = sane.init()
print('SANE version:', ver)
# prints: SANE version: (16777245, 1, 0, 29)
#
# Get devices
#
#devices = sane.get_devices()
#print('Available devices:', devices)
sc_dev = ('hpaio:/net/hp_officejet_pro_9020_series?ip=192.168.0.57&queue=false', 'Hewlett-Packard', 'hp_officejet_pro_9020_series', 'all-in-one')
print('\n Opening: \n', sc_dev[1], sc_dev[2])
#
# Open first device
#
dev = sane.open(sc_dev[0])
try:
dev.depth = 8
dev.mode = 'Color'
dev.source = 'ADF'
dev.resolution = 200
except:
print('problem setting params')
quit()
#
# Start a scan and get a PIL.Image object
#
dev.start()
im = dev.snap()
im.save('test_pil.png')
#
# Close the device
#
dev.close()
I've got an HP flatbed with an ADF attached.
Using SANE directly I can run batch scans and it will correctly quit after the last page is scanned. However the python-sane library (v2.8.2 installed with PIP) doesn't seem to exit.
A quick scan through the code looks like it uses an exception to tell the iterator to stop, any ideas why that might not be thrown by the SANE backend?
I'm a novice Python developer, so I'm probably doing it wrong, but I've tried:
for i, pageim in enumerate(dev.multi_scan()):
pageim.save('test ' + str(i) + '.png')
and
i = 0
while dev.multi_scan():
dev.multi_scan().next().save('while_test_' + str(i) + '.png')
i = i + 1
But to be honest even a single scan will continue if there is no paper:
dev.start()
im = dev.snap()
im.save('test_pil.png')
Let me know what you need from me to debug this.
I installed python3-sane from the Debian/Ubuntu repositories. It installed version (2.8.3-4build1). In sane.py, the class _SaneIterator has a def next(self):
method. For Python 3 it should be def __next__(self):
After manually working around python-pillow/Pillow#1076, the following build failure occurs if NumPy is installed:
$ python3.4 setup.py build
running build
running build_py
creating build
creating build/lib.linux-x86_64-3.4
copying sane.py -> build/lib.linux-x86_64-3.4
running build_ext
building '_sane' extension
creating build/temp.linux-x86_64-3.4
x86_64-pc-linux-gnu-gcc -pthread -fPIC -DWITH_NUMPY -I/usr/include/python3.4/Imaging -I/usr/include/python3.4 -c _sane.c -o build/temp.linux-x86_64-3.4/_sane.o -Wunused-function
_sane.c:972:32: fatal error: numpy/ndarraytypes.h: No such file or directory
#include "numpy/ndarraytypes.h"
^
compilation terminated.
error: command 'x86_64-pc-linux-gnu-gcc' failed with exit status 1
$ python3.4 -c 'import numpy; print(numpy.get_include())'
/usr/lib64/python3.4/site-packages/numpy/core/include
Fix:
--- setup.py
+++ setup.py
@@ -4,6 +4,7 @@
defs = []
extra_compile_args = []
+include_dirs = [os.path.join(distutils.sysconfig.get_python_inc(), "Imaging")]
try:
import numarray
defs.append(('WITH_NUMARRAY',None))
@@ -13,13 +14,12 @@
import numpy
defs.append(('WITH_NUMPY',None))
extra_compile_args.append('-Wunused-function')
+ include_dirs.append(numpy.get_include())
except ImportError:
pass
sane = Extension('_sane',
- include_dirs = [
- os.path.join(distutils.sysconfig.get_python_inc(), "Imaging")
- ],
+ include_dirs = include_dirs,
libraries = ['sane'],
define_macros = defs,
extra_compile_args = extra_compile_args,
I couldn't find anything in the docs about how to use the progress
callback function available in SaneDev.snap()
. My first try was to pass in a function with only one argument, and this caused a segmentation fault in the python interpreter. (For anyone looking, you need to pass in a function that accepts two arguments. The first will be the number of lines scanned so far, and the second will be the total number of lines).
I'm not super familiar with CPython but it looks like passing two arguments into a python callable that only accepts one causes a segfault. I couldn't find a way in the docs to check the number of arguments in CPython (that must exist right?) so I don't know how to fix it in _sane.c
. However one simple fix within sane.py
would be to check the type of the progress object passed in to snap()
, and wrap it in a function that accepts two arguments.
It would be nice to have them as this is the usual location devs look for python packages.
My scanner supports customisation of colour curve table. "scanimage" utility reports this option as
--red-gamma-table 0..255,...
Gamma-correction table for the red band.
--green-gamma-table 0..255,...
Gamma-correction table for the green band.
--blue-gamma-table 0..255,...
Gamma-correction table for the blue band.
It is an array of integers, SANE reports option size 1024. Alas, Python demands a single-value int/long option.
How can we support value tables?
Hello:
I am trying to run the example.py file and I get the following error:
Available devices: []
Traceback (most recent call last):
file "example.py", line 29, in
dev = sane.open (devices [0] [0])
IndexError: list index out of range.
I understand that it is because it does not recognize any scanner / printer.
It's a network scanner and my operating system is recognizing it.
Regards.
Hey,
I'm working on a project where I create an app that uses python-sane that detects scanners and scans, etc. The scanner I have works by itself if I use the program that comes with the scanner. However, sane.get_devices()
does not recognize any scanners and returns an empty list. I'm a bit confused why this is happening amd how to troubleshoot it. TIA
Hi there!
First of all, thanks for doing this, it's really great!
By using sane.get_devices()
I get the device_name [OK], vendor [OK], Friendly-Name [instead of model?], machine type (exact model name) [OK].
As being not very good at the english language I maybe have the wrong idea about the use of the word model in this context.
Or should this element 'model' usually contain the exact scanner type/model?
(I'm using a brother network scanner device. Model: MFC-J6520DW)
Thank you 😸
The destructor of a _SaneIterator
object calls device.cancel()
, which may lead to exceptions if the user has already closed the device.
Consider the following code:
sane.init()
dev = sane.open("device")
iterator = dev.multi_scan()
while True:
try:
image = iterator.next()
except:
break
dev.cancel()
dev.close()
sane.exit()
This will cause an exception when the garbage collector destroys iterator
. The only reasonable work-around I found is to manually destroy the _SaneIterator
object before closing the device, but having to do that is not what I would expect at all.
class _SaneIterator, method next(self):
the Exception 'Document feeder out of documents' was raised by self.device.snap(True). Could you put it also inside "try ... except" block?
It would be nice if while scanning there was an indication of the progress. In scanimage
this is done like this:
hundred_percent = parm.bytes_per_line * parm.lines
* ((parm.format == SANE_FRAME_RGB || parm.format == SANE_FRAME_GRAY) ? 1:3);
while (1)
{
double progr;
status = sane_read (device, buffer, buffer_size, &len);
total_bytes += (SANE_Word) len;
progr = ((total_bytes * 100.) / (double) hundred_percent);
if (progr > 100.)
progr = 100.;
if (progress)
fprintf (stderr, "Progress: %3.1f%%\r", progr);
Of course in python-sane the progress should be exposed through an API. Maybe something like this:
def display_progress(progr):
print("\x1b[1F\x1b[2KProgress: %3.1f%%" % (progr * 100))
sane.init()
scanner = sane.open(sane.get_devices()[0][0])
scanner.scan(progress = display_progress)
Hi, great job.
Sane example code looks not working ?
> python test-scan.py
> SANE version: (16777241, 1, 0, 25)
> Available devices: [('pixma:MG3500_192.168.1.70', 'CANON', 'Canon PIXMA MG3500 Series', 'multi-function peripheral'), ('pixma:04A9176E_40BBB1', 'CANON', 'Canon PIXMA MG3500 Series', 'multi-function peripheral')]
> Traceback (most recent call last):
> File "test-scan.py", line 29, in <module>
> dev = sane.open(devices[0][0])
> File "/usr/local/lib/python2.7/dist-packages/sane.py", line 400, in open
> return SaneDev(devname)
> File "/usr/local/lib/python2.7/dist-packages/sane.py", line 160, in __init__
> d['dev'] = _sane._open(devname)
> TypeError: argument 1 must be string, not tuple
>
>
Debian and Redhat currently package their sane packages from the Pillow source package. It would be good to notify them before the code drops for a new release.
I have a couple of scanners (a Fujitsu SnapScan 1500 and a Canon CanoScan LiDE 210) that have front-panel buttons that can initiate scans. Unfortunately, the primary scan buttons are named "scan" and that collides with the bound method to ask the scanner to begin scanning.
>>> import sane
>>>
>>> sane.init()
(16777247, 1, 0, 31)
>>> sane.get_devices()
[('genesys:libusb:001:008', 'Canon', 'LiDE 210', 'flatbed scanner'), ('fujitsu:ScanSnap S1500:56050', 'FUJITSU', 'ScanSnap S1500', 'scanner')]
>>>
>>> scanner = sane.open('fujitsu:ScanSnap S1500:56050')
>>> scanner.opt['scan']
Name: scan
Cur value: <bound method SaneDev.scan of <sane.SaneDev object at 0x7f8567e60f40>>
Index: 91
Title: Scan button
Desc: Scan button
Type: TYPE_BOOL
Unit: UNIT_NONE
Constr: None
active: yes
settable: no
Modifying sane.py
on line 307 to change the method name from scan
to, say, doscan
"fixes" the issue for me:
>>> scanner.opt['scan']
Name: scan
Cur value: 0
Index: 91
Title: Scan button
Desc: Scan button
Type: TYPE_BOOL
Unit: UNIT_NONE
Constr: None
active: yes
settable: no
I'm not sure what the proper way to fix this for real. :/
I forgot to close the SaneDev
that I opened, but I did issue a sane.exit
. This caused a segfault when exiting python. E.g.:
Python 3.4.3 (default, Jan 29 2016, 15:13:46)
[GCC 4.9.3] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sane
>>> sane.init()
(16777240, 1, 0, 24)
>>> dev = sane.get_devices()[0][0]
>>> sane.open(dev)
<sane.SaneDev object at 0x7f17a14950f0>
>>> sane.exit()
>>> exit()
zsh: segmentation fault python
The reason is, that when the SaneDev
is destroyed on exit, sane_close
is called implicitly, even if sane_exit
was already called.
I don’t know if it should be valid to skip sane_close
, as the documentation only says:
When done using the device, the handle should be closed by a call to sane_close().
In any case, I think that sane.exit
should complain about any open devices, because it does invalidate the SaneDev
instances.
Aside, the documentation goes on to say:
Finally, before exiting the application, function sane_exit() must be called. It is important not to forget to call this function since otherwise some resources (e.g., temporary files or locks) may remain unclaimed.
So sane.exit
should probably also be called implicitly on exit.
The latest version of python-sane on PyPI is 2.8.2, whereas the latest tag is v2.8.3.
Could you upload v2.8.3 to PyPI?
Hi, I am trying to do duplex scanning using Python-sane but unable to do duplex scanning please help me ... regards
import scanPython
from PIL import Image
mode = 'grey'
var = scanPython.init()
print("sane version ", var)
devices = scanPython.get_devices()
print('Available devices:', devices)
dev = scanPython.open(devices[0][0])
dev.__setattr__(2,'ADF Duplex')
print("Current Source",dev.__getattr__(2))
dev.set_mode =mode
dev.resolution = 200
dev.start()
iterator = dev.multi_scan()
progress = True
while progress:
try:
image = iterator.next()
except:
progress = False
break
#dev.cancel()
dev.close()
Travis has started failing Sane builds - https://travis-ci.org/python-pillow/Sane/jobs/100606154 - with the error message -
E: Unable to locate package libsane-dev
The command "sudo apt-get install libsane-dev" failed and exited with 100 during .
I found travis-ci/travis-ci#3994 where a request was submitted to whitelist the the package. Would this just be a case of the package disappearing off the whitelist, and a new request needing to be submitted?
error:
running build
running build_py
running build_ext
building '_sane' extension
x86_64-linux-gnu-gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -flto -fuse-linker-plugin -ffat-lto-objects -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC -I/usr/include/python3.7m -c _sane.c -o build/temp.linux-x86_64-3.7/_sane.o
_sane.c:30:10: fatal error: sane/sane.h: No such file or directory
#include <sane/sane.h>
^~~~~~~~~~~~~
compilation terminated.
error: command 'x86_64-linux-gnu-gcc' failed with exit status 1
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.