基于Windows STI API的Python基础图像采集实现求助
Hey there! As someone coming from Linux and new to Windows API interop, I get how daunting this can feel at first. Let's walk through how to use PyWin32 to interact with the STI (Still Image Architecture) API for your scanner, since WIA isn't an option here.
First, a quick STI primer
STI is a lower-level Windows API for still image devices, and it uses both COM interfaces and raw Win32 functions. PyWin32 gives us two main tools to work with this: win32com.client for COM objects, and ctypes for calling unmanaged Win32 functions directly. We'll use both where needed.
Step 1: Enumerate STI Devices
First, let's make sure Python can see your Vidar scanner via STI. We'll use the IStiDeviceControl COM interface to list devices:
import win32com.client import pythoncom def enumerate_sti_devices(): try: # Initialize COM (required for Windows API calls) pythoncom.CoInitialize() # Create the STI device manager sti_manager = win32com.client.Dispatch("Sti.StiDeviceManager") # Get the collection of STI devices devices = sti_manager.Devices print("Found STI devices:") for idx, device in enumerate(devices): print(f"{idx+1}. Name: {device.Name}, DeviceID: {device.DeviceID}") return devices except Exception as e: print(f"Error enumerating devices: {str(e)}") finally: pythoncom.CoUninitialize() # Run the enumeration sti_devices = enumerate_sti_devices()
Run this script—you should see your Vidar scanner listed. Note down the DeviceID or index, since we'll need it to connect to the device.
Step 2: Connect to the Scanner & Initialize a Scan Session
Next, we'll open the device and prepare it for scanning. We'll need to use some raw Win32 functions via ctypes here, since some STI operations aren't exposed via COM:
import ctypes from ctypes import wintypes # Define necessary STI constants (from Windows SDK headers) STI_DIRECT = 0x00000001 STI_DEVICE_CREATE_BEST_ACCESS = 0x00000002 # Load the sti.dll sti_dll = ctypes.WinDLL("sti.dll") # Define function prototypes for STI API calls sti_dll.StiCreateInstance.restype = wintypes.HRESULT sti_dll.StiCreateInstance.argtypes = [ wintypes.LPCWSTR, # Device ID wintypes.LPVOID, # Reserved (NULL) wintypes.DWORD, # Access mode (STI_DIRECT) wintypes.DWORD, # Creation flags wintypes.LPVOID, # Pointer to IStiDevice interface ] def open_sti_device(device_id): try: pythoncom.CoInitialize() # Prepare pointer to receive the IStiDevice interface sti_device = ctypes.c_void_p() hr = sti_dll.StiCreateInstance( device_id, None, STI_DIRECT, STI_DEVICE_CREATE_BEST_ACCESS, ctypes.byref(sti_device) ) if hr != 0: # HRESULT S_OK is 0 raise Exception(f"Failed to open device. HRESULT: {hr}") print("Successfully connected to the scanner!") return sti_device except Exception as e: print(f"Error opening device: {str(e)}") finally: pythoncom.CoUninitialize() # Use the DeviceID from Step 1 here target_device_id = "YOUR_SCANNER_DEVICE_ID" device_handle = open_sti_device(target_device_id)
Step 3: Configure Scan Settings & Trigger a Scan
Now, we need to send scan commands to the device. For basic scanning, we'll use the IStiDevice::RawReadData method to get the image data. Note that STI requires handling the device's native image format—for Vidar scanners, this is often TIFF or raw bitmap data.
Here's a simplified example to capture a scan and save it to a file:
def capture_scan(device_handle, output_path): try: # Convert the raw pointer to a COM object (IStiDevice) sti_device = win32com.client.CastTo(device_handle, "IStiDevice") # Initialize scan (send a command to the device to start scanning) # Note: Exact commands depend on your scanner's STI implementation # You may need to send a vendor-specific command here # For basic scans, some scanners respond to STI_CMD_SCAN sti_device.DeviceIoControl(0x00000001, None, None) # Adjust control code as needed # Read the scanned image data buffer_size = 1024 * 1024 # 1MB buffer (adjust based on expected image size) buffer = ctypes.create_string_buffer(buffer_size) bytes_read = wintypes.DWORD() hr = sti_device.RawReadData(buffer, buffer_size, ctypes.byref(bytes_read)) if hr != 0: raise Exception(f"Failed to read scan data. HRESULT: {hr}") # Save the raw data to a file (you may need to convert to TIFF/BMP first) with open(output_path, "wb") as f: f.write(buffer[:bytes_read.value]) print(f"Scan saved to {output_path}") except Exception as e: print(f"Error capturing scan: {str(e)}") # Capture a scan and save to output.tif capture_scan(device_handle, "output.tif")
Important Notes & Troubleshooting
- Vendor-Specific Commands: STI is device-agnostic, but some scanners require vendor-specific control codes or settings. You may need to refer to the Vidar scanner's STI documentation (included with their driver) for exact commands to trigger scans, set resolution, or define scan areas.
- COM Initialization: Always call
pythoncom.CoInitialize()before using COM objects, andpythoncom.CoUninitialize()afterward to clean up resources. - Admin Rights: Some STI operations require elevated privileges—run your Python script as Administrator if you get access denied errors.
- Image Processing: The raw data from
RawReadDatamay need post-processing. For TIFF data, you can use libraries likePillowto load and manipulate the image for batch processing:from PIL import Image with Image.open("output.tif") as img: # Perform batch operations (resize, convert format, etc.) img.save("processed_scan.png")
Final Tips
Since you're used to Linux, think of STI as similar to sane (Scanner Access Now Easy) but for Windows—lower-level, requiring device-specific knowledge in some cases. PyWin32 bridges the gap between Python and Windows' native APIs, so take time to experiment with the COM interfaces and Win32 functions.
内容的提问来源于stack exchange,提问作者moo

