|
First get in your hand the bitmap from scanner and after apply the structure transformation that you want. I recommend that not try to get directly the image as you want.
Mosquets
|
|
|
|
|
It seems that when using auto document feeder the message TransferReady is not fired until the documents have finished scanning entirely. This solution consumes a prohibitively large amount of memory for many scans.
Is it possible to fire that message after a specific number of scanned pages have occured in order to cap the amount of memory consumed?
This way the images can be handled and the memory in the buffer can be released?
thankyou
|
|
|
|
|
Again, this minimal project shows how to interop with TWAIN,
but first you have to fully understand TWAIN (in C++) yourself.
Get the standard
http://www.twain.org/docs/Spec1_9_197.pdf[^]
and read all about the different transfer modes.
|
|
|
|
|
I started playing with this code and it seemed to crash when I tried to aquire. I realized that the app becomes disabled and frozen when you launch the scanner app and then cancel the app. For example: I am using a Microtek SlimScan C6. When launching the app is says "Cannot find a scanner". If you click the close (X) on that dialog - the calling app (twaingui) remains disabled.
To fix this, I changed the Aquire() method. It now returns true or false. If the close (X) button is clicked, it will return false.
public bool Acquire()
{
TwRC rc;
CloseSrc();
if( appid.Id == IntPtr.Zero )
{
Init( hwnd );
if( appid.Id == IntPtr.Zero )
//return;
return false;
}
rc = DSMident( appid, IntPtr.Zero, TwDG.Control, TwDAT.Identity, TwMSG.OpenDS, srcds );
if( rc != TwRC.Success )
//return;
return false;
TwCapability cap = new TwCapability( TwCap.XferCount, 1 );
rc = DScap( appid, srcds, TwDG.Control, TwDAT.Capability, TwMSG.Set, cap );
if( rc != TwRC.Success )
{
CloseSrc();
//return;
return false;
}
TwUserInterface guif = new TwUserInterface();
guif.ShowUI = 1;
guif.ModalUI = 1;
guif.ParentHand = hwnd;
rc = DSuserif( appid, srcds, TwDG.Control, TwDAT.UserInterface, TwMSG.EnableDS, guif );
if( rc != TwRC.Success )
{
CloseSrc();
//return;
return false;
}
return true;
}
Then in MainFrame.cs I modified the scan menu to include this:
if (!tw.Acquire())
{
msgfilter = false;
Application.RemoveMessageFilter(this);
this.Enabled = true;
this.Activate();
}
It seems to fix the issue.
|
|
|
|
|
I have tried to scan with an extra form open and the dialog for the scanner just hangs around with no respond to start scanning!
Help
Maundu
|
|
|
|
|
Before you open the child form, you should called the Endscan() function. Other wise you cant call the scan function from the child form.
|
|
|
|
|
I try to change the capability like this:
I want to change the IPixelType to black and white
cap = new TwCapability( TwCap.IPixelType , 0);// 0 --> black and white
rc = DScap( appid, srcds, TwDG.Control, TwDAT.Capability, TwMSG.Set , cap );
if( rc != TwRC.Success )
{
CloseSrc();
return;
}
but the rc is Failure
I use with hp scanjet 5590 scanner
with other scanner I can change the capability on this way
Can someone tell me how I can to fix it
Thanks
-- modified at 5:56 Thursday 22nd December, 2005
|
|
|
|
|
ok
I found the problem
I change TwType.Int16 to TwType.Fix32 or to TwType.UInt16
it's work
<br />
public TwCapability( TwCap cap, short sval)<br />
{<br />
<br />
Cap = (short) cap;<br />
ConType = (short) TwOn.One;<br />
Handle = Twain.GlobalAlloc( 0x42, 6 );<br />
IntPtr pv = Twain.GlobalLock( Handle );<br />
<br />
Marshal.WriteInt16( pv, 0, (short) TwType.Fix32 );<br />
<br />
Marshal.WriteInt32( pv, 2, (int) sval );<br />
Twain.GlobalUnlock( Handle );<br />
}<br />
<br />
-- modified at 5:56 Thursday 22nd December, 2005
|
|
|
|
|
THX AlexKak.
My problem is solved by your reply.
i think constructor overload is good implement.
public TwCapability(TwCap cap, TwFix32 sval)
{
Cap = (short) cap;
ConType = (short) TwOn.One;
Handle = Twain.GlobalAlloc( 0x42, 6 );
IntPtr pv = Twain.GlobalLock( Handle );
Marshal.WriteInt16(pv, 0, (short)TwType.Fix32);
Marshal.StructureToPtr(sval, IntPtr.Add(pv,2), false);
Twain.GlobalUnlock( Handle );
}
|
|
|
|
|
Hello,
I am totally new and totally lost!!
Somebody tell me where i can learn to use the twain api's....
I need to start off with ABC's
Thanks!!
!!C before you C++!!
|
|
|
|
|
http://www.twain.org is the place to start, however they bone-headedly do not have a link to the specification anywhere I can find. Go here to get the full specification (1.9, they are working on 2.0 and 2.1 now but they are backward-compatible):
http://www.twain.org/docs/Spec1_9_197.pdf
They have a kit with a sample driver and scanning application also. It's definitely a hard protocol to follow, but if you read the spec and don't skip too much it isn't that difficult. Basically there is one function to TWAIN, that is ALL. The first two parameters are the source and destination (Your app is always the source, the destination is the data source manager (twain itself) or a data source (scanner, webcam, etc.). The next three parameters taken together define a function call, and the final parameter is a structure to hold the data, both input and output. You move TWAIN through seven steps, the first three picking out a source and the last four setting up the source and aquiring an image (or images). The most difficult part I think is having the results come back through the Windows message loop.
Jason Goemaat
jasong@netins.net
|
|
|
|
|
Thanx!! Infact I did just that!! Amd I had a lot of help from the CTwain class too!!
THANK GOD FOR CODE PROJECT!!
I am proud to say I did a good job and my Project Manager really liked my App!!
THANX GUYS!!
!!C before you C++!!
|
|
|
|
|
Hi
how i can set default output type to black and white (1-bit) ?
Thanks
Alex
|
|
|
|
|
Very helpful, thank you NETMaster!!
|
|
|
|
|
I have made an automated version of scan. When the user clicks on the browser this windows applicaiton is called. Scanning is Automated and hence whole page is scanned.
The User Interface is not displayed.i.ee. made false.
When I display the user interface I get the exact image, but when I make the
user interface to false the whole document is scanned instead of just the area of the photo.
Has anyone done it. If so, please send the solution.
Thanks in advance.
nilesh j. gupte
|
|
|
|
|
I'm working on a program for doing multipage scans. I'm converting the DIB to a Bitmap object. My issue is, one image's memory footprint is way too large. I think I could save the image with a format that has good compression and reload it but ideally I would like to compress the image while in memory. There could be a 300 page scan comming through this application at any given time so cutting down the memory usage for a single page is necessary. I think saving to disk and reloading for so many pages is a bad idea all together. My question is, how can I can I add some kind of compression to this image while still in memory? Do I need to do it before I convert it to a Bitmap object or would it be easier using .net tools after it's a Bitmap object? I don't need any fine grain control over the compression really, just decent quality with a signifigant loss in the memory usage for the image. If save the black and white image scanned in as a bmp, it's ~14,200 on disk. As a jpeg it's ~200k. I would like to get something like this jpeg compression without ever writing to disk.
Thanks in advance for any help.
|
|
|
|
|
hi, I found this code and I send you as is, RESPECTING THE AUTHOR'S RIGHT, I'M JUST LEARNING.
I've changed a small part of code. instead of use this:
///////////////////////////////////////////////////////////////////////////////////77
byte[] data = new byte[ fh.Size ]; // file-sized byte[]
RawSerializeInto( fh, data ); // serialize BITMAPFILEHEADER into byte[]
Marshal.Copy( dibPtr, data, fhSize, dibSize ); // mem-copy DIB into byte[]
MemoryStream stream = new MemoryStream( data ); // file-sized stream
Bitmap tmp = new Bitmap( stream ); // 'tmp' is wired to stream (unfortunately)
Bitmap result = new Bitmap( tmp ); // THIS IS THE LINE I'VE HAD CHANGED
tmp.Dispose(); tmp = null;
stream.Close(); stream = null; data = null;
return result;
///////////////////////////////////////////////////////////////////////////////////////////
I'VE USED THIS:
////////////////////////////////////////////////////////////////
tmp.Save(stream,System.Drawing.Imaging.ImageFormat.Jpeg);
Bitmap result = new Bitmap( tmp ); // 'result' is a copy (stand-alone)
result.Save(stream,System.Drawing.Imaging.ImageFormat.Jpeg);
////////////////////////////////////////////////////////////////
AN THIS IS THE WHOLE CODE
// *******************************************************************************************
/* **************************************************************************
Converting memory DIB to .NET 'Bitmap' object
EXPERIMENTAL, USE AT YOUR OWN RISK
http://dnetmaster.net/
*****************************************************************************/
//
// The 'DibToImage' class provides three different methods [Stream/scan0/HBITMAP alive]
//
// The parameter 'IntPtr dibPtr' is a pointer to
// a classic GDI 'packed DIB bitmap', starting with a BITMAPINFOHEADER
//
// Note, all this methods will use MUCH memory!
// (multiple copies of pixel datas)
//
// Whatever I used, all Bitmap/Image constructors
// return objects still beeing backed by the underlying Stream/scan0/HBITMAP.
// Thus you would have to keep the Stream/scan0/HBITMAP alive!
//
// So I tried to make an exact copy/clone of the Bitmap:
// But e.g. Bitmap.Clone() doesn't make a stand-alone duplicate.
// The working method I used here is : Bitmap copy = new Bitmap( original );
// Unfortunately, the returned Bitmap will always have a pixel-depth of 32bppARGB !
// But this is a pure GDI+/.NET problem... maybe somebody else can help?
//
//
// ----------------------------
// Note, Microsoft should really wrap GDI+ 'GdipCreateBitmapFromGdiDib' in .NET!
// This would be very useful!
//
// There is a :
// Bitmap Image.FromHbitmap( IntPtr hbitmap )
// so there is NO reason to not add a:
// Bitmap Image.FromGdiDib( IntPtr dibptr )
//
// PLEASE SEND EMAIL TO: netfwsdk@microsoft.com
// OR mswish@microsoft.com
// OR http://register.microsoft.com/mswish/suggestion.asp
// ------------------------------------------------------------------------
using System;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
namespace TwainGui
{
public class DibToImage
{
///
/// Get .NET 'Bitmap' object from memory DIB via stream constructor.
/// This should work for most DIBs.
///
/// <param name="dibPtr" />Pointer to memory DIB, starting with BITMAPINFOHEADER.
public static Bitmap WithStream( IntPtr dibPtr )
{
BITMAPFILEHEADER fh = new BITMAPFILEHEADER();
Type bmiTyp = typeof(BITMAPINFOHEADER);
BITMAPINFOHEADER bmi = (BITMAPINFOHEADER) Marshal.PtrToStructure( dibPtr, bmiTyp );
if( bmi.biSizeImage == 0 )
bmi.biSizeImage = ((((bmi.biWidth * bmi.biBitCount) + 31) & ~31) >> 3) * Math.Abs( bmi.biHeight );
if( (bmi.biClrUsed == 0) && (bmi.biBitCount < 16) )
bmi.biClrUsed = 1 << bmi.biBitCount;
int fhSize = Marshal.SizeOf( typeof(BITMAPFILEHEADER) );
int dibSize = bmi.biSize + (bmi.biClrUsed * 4) + bmi.biSizeImage; // info + rgb + pixels
fh.Type = new Char[] { 'B', 'M' }; // "BM"
fh.Size = fhSize + dibSize; // final file size
fh.OffBits = fhSize + bmi.biSize + (bmi.biClrUsed * 4); // offset to pixels
byte[] data = new byte[ fh.Size ]; // file-sized byte[]
RawSerializeInto( fh, data ); // serialize BITMAPFILEHEADER into byte[]
Marshal.Copy( dibPtr, data, fhSize, dibSize ); // mem-copy DIB into byte[]
MemoryStream stream = new MemoryStream( data ); // file-sized stream
Bitmap tmp = new Bitmap( stream ); // 'tmp' is wired to stream (unfortunately)
tmp.Save(stream,System.Drawing.Imaging.ImageFormat.Jpeg);
Bitmap result = new Bitmap( tmp ); // 'result' is a copy (stand-alone)
result.Save(stream,System.Drawing.Imaging.ImageFormat.Jpeg);
tmp.Dispose(); tmp = null;
stream.Close(); stream = null; data = null;
return result;
}
///
/// Get .NET 'Bitmap' object from memory DIB via 'scan0' constructor.
/// This only works for 16..32 pixel-depth RGB DIBs (no color palette)!
///
/// <param name="dibPtr" />Pointer to memory DIB, starting with BITMAPINFOHEADER.
public static Bitmap WithScan0( IntPtr dibPtr )
{
Type bmiTyp = typeof(BITMAPINFOHEADER);
BITMAPINFOHEADER bmi = (BITMAPINFOHEADER) Marshal.PtrToStructure( dibPtr, bmiTyp );
if( bmi.biCompression != 0 )
throw new ArgumentException( "Invalid bitmap format (non-RGB)", "BITMAPINFOHEADER.biCompression" );
PixelFormat fmt = PixelFormat.Undefined;
if( bmi.biBitCount == 24 )
fmt = PixelFormat.Format24bppRgb;
else if( bmi.biBitCount == 32 )
fmt = PixelFormat.Format32bppRgb;
else if( bmi.biBitCount == 16 )
fmt = PixelFormat.Format16bppRgb555;
else // we don't support a color palette...
throw new ArgumentException( "Invalid pixel depth (<16-Bits)", "BITMAPINFOHEADER.biBitCount" );
int scan0 = ((int) dibPtr) + bmi.biSize + (bmi.biClrUsed * 4); // pointer to pixels
int stride = (((bmi.biWidth * bmi.biBitCount) + 31) & ~31) >> 3; // bytes/line
if( bmi.biHeight > 0 )
{ // bottom-up
scan0 += stride * (bmi.biHeight - 1);
stride = -stride;
}
Bitmap tmp = new Bitmap( bmi.biWidth, Math.Abs( bmi.biHeight ),
stride, fmt, (IntPtr) scan0 ); // 'tmp' is wired to scan0 (unfortunately)
Bitmap result = new Bitmap( tmp ); // 'result' is a copy (stand-alone)
tmp.Dispose(); tmp = null;
return result;
}
///
/// Get .NET 'Bitmap' object from memory DIB via HBITMAP.
/// Uses many temporary copies [huge memory usage]!
///
/// <param name="dibPtr" />Pointer to memory DIB, starting with BITMAPINFOHEADER.
public static Bitmap WithHBitmap( IntPtr dibPtr )
{
Type bmiTyp = typeof(BITMAPINFOHEADER);
BITMAPINFOHEADER bmi = (BITMAPINFOHEADER) Marshal.PtrToStructure( dibPtr, bmiTyp );
if( bmi.biSizeImage == 0 )
bmi.biSizeImage = ((((bmi.biWidth * bmi.biBitCount) + 31) & ~31) >> 3) * Math.Abs( bmi.biHeight );
if( (bmi.biClrUsed == 0) && (bmi.biBitCount < 16) )
bmi.biClrUsed = 1 << bmi.biBitCount;
IntPtr pixPtr = new IntPtr( (int) dibPtr + bmi.biSize + (bmi.biClrUsed * 4) ); // pointer to pixels
IntPtr img = IntPtr.Zero;
int st = GdipCreateBitmapFromGdiDib( dibPtr, pixPtr, ref img );
if( (st != 0) || (img == IntPtr.Zero) )
throw new ArgumentException( "Invalid bitmap for GDI+", "IntPtr dibPtr" );
IntPtr hbitmap;
st = GdipCreateHBITMAPFromBitmap( img, out hbitmap, 0 );
if( (st != 0) || (hbitmap == IntPtr.Zero) )
{
GdipDisposeImage( img );
throw new ArgumentException( "can't get HBITMAP with GDI+", "IntPtr dibPtr" );
}
Bitmap tmp = Image.FromHbitmap( hbitmap ); // 'tmp' is wired to hbitmap (unfortunately)
Bitmap result = new Bitmap( tmp ); // 'result' is a copy (stand-alone)
tmp.Dispose(); tmp = null;
bool ok = DeleteObject( hbitmap ); hbitmap = IntPtr.Zero;
st = GdipDisposeImage( img ); img = IntPtr.Zero;
return result;
}
/// Copy structure into Byte-Array.
private static void RawSerializeInto( object anything, byte[] datas )
{
int rawsize = Marshal.SizeOf( anything );
if( rawsize > datas.Length )
throw new ArgumentException( " buffer too small ", " byte[] datas " );
GCHandle handle = GCHandle.Alloc( datas, GCHandleType.Pinned );
IntPtr buffer = handle.AddrOfPinnedObject();
Marshal.StructureToPtr( anything, buffer, false );
handle.Free();
}
// GDI imports : read MSDN!
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi, Pack=1)]
private class BITMAPFILEHEADER
{
[MarshalAs( UnmanagedType.ByValArray, SizeConst=2)]
public Char[] Type;
public Int32 Size;
public Int16 reserved1;
public Int16 reserved2;
public Int32 OffBits;
}
[StructLayout(LayoutKind.Sequential, Pack=2)]
private class BITMAPINFOHEADER
{
public int biSize;
public int biWidth;
public int biHeight;
public short biPlanes;
public short biBitCount;
public int biCompression;
public int biSizeImage;
public int biXPelsPerMeter;
public int biYPelsPerMeter;
public int biClrUsed;
public int biClrImportant;
}
[DllImport("gdi32.dll", ExactSpelling=true)]
private static extern bool DeleteObject( IntPtr obj );
// GDI+ from GdiplusFlat.h : http://msdn.microsoft.com/library/en-us/gdicpp/gdi+/gdi+reference/flatapi.asp
[DllImport("gdiplus.dll", ExactSpelling=true)]
private static extern int GdipCreateBitmapFromGdiDib( IntPtr bminfo, IntPtr pixdat, ref IntPtr image );
// GpStatus WINGDIPAPI GdipCreateBitmapFromGdiDib( GDIPCONST BITMAPINFO* gdiBitmapInfo, VOID* gdiBitmapData, GpBitmap** bitmap);
[DllImport("gdiplus.dll", ExactSpelling=true)]
private static extern int GdipCreateHBITMAPFromBitmap( IntPtr image, out IntPtr hbitmap, int bkg );
// GpStatus WINGDIPAPI GdipCreateHBITMAPFromBitmap( GpBitmap* bitmap, HBITMAP* hbmReturn, ARGB background);
[DllImport("gdiplus.dll", ExactSpelling=true)]
private static extern int GdipDisposeImage( IntPtr image );
} // class DibToImage
} // namespace
// *******************************************************************************************
|
|
|
|
|
Hai dude,
I am also working in a project with the same situation like you. I found something during my work. Thats ,
1. You can change the resolution in the Scanner Twain driver UI from 300*300 to 100*100 that will make the scanner faster and it ll create small image. Not like what you are getting now. Otherwise you need to compress the file. Eventhough you have to scan 300 page.. So if you scan with 300*300 resolution your Virtual memory ll be not enough even you use 3 GB for virtual memory. So the application ll automatically close.
2.You can use Marshal.FreeHGlobal(img) after saving the img using Gdip.SaveDIBAs(newpic.Text, newpic.GlobalLock(img), newpic.GetPixelInfo(newpic.GlobalLock(img)))
This ll free the virtual memory after saving the image.
But its better you can change the resolution to 100*100 in the before scanning. And to make more faster, change the compression type to JPEG, which you can find in the option which is also in the Twain driver scanner UI.
I think it ll be very helpful for you. If you have any other doubt... Dont hesitate. And I posted some code for Multi image Doc File and MultiImage tif file in this same topic. You can find that also
Raja Chandrasekaran
Dewsoft Solution
-- modified at 10:45 Monday 20th February, 2006
|
|
|
|
|
Hey, I found that if we do not select a source theres a major crash :P
Can someone tell me how to avoid the crash
|
|
|
|
|
To all the interested, to avoid the infinite loop to crash you app do the following...
On the Event to Acquire the image, change the order on tw.Acquire() with the if to open the message interceptor...
Create a public static bool and tell it that it's true,then on Twain and on the function Init right before the end of the method put on the var value true.
Finally come back to the mainform and then only start the message intercceptor if the var is false
Here's some snippet code
<br />
private void menuItemScan_Click(object sender, System.EventArgs e)<br />
{ <br />
tw.Acquire();<br />
if (TwainLib.Twain.failed == false)<br />
{<br />
if( ! msgfilter )<br />
{<br />
this.Enabled = false;<br />
msgfilter = true;<br />
Application.AddMessageFilter( this );<br />
}<br />
}<br />
}<br />
<br />
public static bool failed = false;<br />
public void Init( IntPtr hwndp )<br />
{<br />
Finish();<br />
TwRC rc = DSMparent( appid, IntPtr.Zero, TwDG.Control, TwDAT.Parent, TwMSG.OpenDSM, ref hwndp );<br />
if( rc == TwRC.Success )<br />
{<br />
rc = DSMident( appid, IntPtr.Zero, TwDG.Control, TwDAT.Identity, TwMSG.GetDefault, srcds );<br />
if( rc == TwRC.Success )<br />
hwnd = hwndp;<br />
else<br />
rc = DSMparent( appid, IntPtr.Zero, TwDG.Control, TwDAT.Parent, TwMSG.CloseDSM, ref hwndp );<br />
}<br />
else <br />
failed = true;<br />
}<br />
|
|
|
|
|
Still it doesn't work, if i cancel on the message asking for a scanner my app crashes instantly.
basha
|
|
|
|
|
Hi,
I tried above changes and some other but it is not working and my application goes to infinite loop.
Can u suggest me the changes to be made or else the new mail me the updated version.......its very very urgent sir.....so plz respond soon.
Thx in advance
Sujan
.Net Programmer, Software Engineer
|
|
|
|
|
Hello, I would like to know if using this code can be adapted to sacn directly without opening the scanner GUI, thanks in advance.
|
|
|
|
|
That's a good question...
|
|
|
|
|
Echooff3 has written:
Try this:
TwUserInterface guif = new TwUserInterface();
guif.ShowUI = 0; //***False
guif.ModalUI = 0; //***False
guif.ParentHand = IntPtr.Zero ; //***Nulled
rc = DSuserif( appid, srcds, TwDG.Control, TwDAT.UserInterface, TwMSG.EnableDS, guif );
if( rc != TwRC.Success )
{
CloseSrc();
return;
}
And it works.
|
|
|
|
|