Home › Forums › TWAIN Classic › 8-Bit image, memory transfer, algorithm problem
- This topic has 14 replies, 3 voices, and was last updated 14 years, 1 month ago by mcohen.
- AuthorPosts
Hy all!
I have a problem with my code doing a memory transfer of a 8-Bit-Image with the old Twain Sample App.
My image looks like this:
My Code is written in Java:
while (true) {
byteBuffer.clear();
TWRC twRC = dsmEntry(appID, dsID, DG.IMAGE, DAT.IMAGEMEMXFER, MSG.GET, memXferBuf);
if (TWRC.SUCCESS.equals(twRC) || TWRC.XFERDONE.equals(twRC)) {
System.out.println(memXferBuf);
int rows = memXferBuf.getRows();
int columns = memXferBuf.getColumns();
for (int i = 0; i < rows; i++) {
for (int x = 0; x < columns; x++) {
byte b = byteBuffer.get();
rgb[0] = b;
rgb[1] = b;
rgb[2] = b;
bfImage.setRGB(x, y, (rgb[0] & 0xff) << 16
| (rgb[1] & 0xff) << 8
| (rgb[2] & 0xff));
}
y++;
}
if (TWRC.XFERDONE.equals(twRC)) {
try {
ImageIO.write(bfImage, "jpg", new File("C:\TEMP.JPG"));
} catch (Exception e) {
e.printStackTrace();
}
break;
}
} else {
try {
checkTWRC(twRC);
} catch (TwainException e) {
e.printStackTrace();
}
break;
}
}The byteBuffer.get() method reads one byte from the buffer, because the image is 8-Bit. When I transfer a 24-Bit image I read three bytes and set each byte for rgb[0], rgb[1] and rgb[2] everything works fine.
Also some Output (System.out.println(memXferBuf)):
BytesPerRow=852,Columns=850,Rows=76,XOffset=0,YOffset=0,BytesWritten=64752
BytesPerRow=852,Columns=850,Rows=76,XOffset=0,YOffset=76,BytesWritten=64752
BytesPerRow=852,Columns=850,Rows=76,XOffset=0,YOffset=152,BytesWritten=64752
BytesPerRow=852,Columns=850,Rows=76,XOffset=0,YOffset=228,BytesWritten=64752
BytesPerRow=852,Columns=850,Rows=76,XOffset=0,YOffset=304,BytesWritten=64752
BytesPerRow=852,Columns=850,Rows=76,XOffset=0,YOffset=380,BytesWritten=64752
BytesPerRow=852,Columns=850,Rows=76,XOffset=0,YOffset=456,BytesWritten=64752
BytesPerRow=852,Columns=850,Rows=76,XOffset=0,YOffset=532,BytesWritten=64752
BytesPerRow=852,Columns=850,Rows=76,XOffset=0,YOffset=608,BytesWritten=64752
BytesPerRow=852,Columns=850,Rows=76,XOffset=0,YOffset=684,BytesWritten=64752
BytesPerRow=852,Columns=850,Rows=76,XOffset=0,YOffset=760,BytesWritten=64752
BytesPerRow=852,Columns=850,Rows=76,XOffset=0,YOffset=836,BytesWritten=64752
BytesPerRow=852,Columns=850,Rows=76,XOffset=0,YOffset=912,BytesWritten=64752
BytesPerRow=852,Columns=850,Rows=76,XOffset=0,YOffset=988,BytesWritten=64752
BytesPerRow=852,Columns=850,Rows=35,XOffset=0,YOffset=1064,BytesWritten=29820
Does anybode see the failure?!
Thanks for Help!
Best regards,
BillieYour image is mis-aligned.
If you try in native mode, you won’t have this behavior right ?
In memory mode, you must re-align each line in memory if necessary. Add the necessary extra bytes at the end.
I don’t remember if it has to be 32bits aligned, but it is something like that.
It looks like the output is correct anyway -> 850 columns -> 852 bytes.
So try to dump it in BMP uncompressed format instead of JPG, I don’t know how your object is working.Yes, native mode works fine – but I need the memory transfer.
So why is there a difference between the Columns and BytesPerRow? The Image has a Resolution of 850 x 1100 Pixels. For 8-Bit grayscale image there would only be need for 850 bytes per row?
Saveing the File as BMP (in memory mode) has the same effect, but the File-Format doesn’t matter at this point. A have a BufferedImage with 850 x 1100 Pixels and set every Pixel explicitly:
bfImage.setRGB(x, y, (rgb[0] & 0xff) << 16
| (rgb[1] & 0xff) << 8
| (rgb[2] & 0xff));So x, y is in fact the x- and y-Position of the Pixel in the Image. My memory buffer is a java.nio.ByteBuffer which is allocates with the preferred size of bytes:
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(bufferSizes.getPreferred());
So after every DG_IMAGE, DAT_IMAGEMEMXFER, MSG_GET Triplet, the bytes are written to the byteBuffer.
The byteBuffer.get() Method reads 1 byte from the buffer’s current position. The position begins with 0 (clear() method) and is incremented with every get() call.
So at the moment I only read 850 bytes (columns=850) from the buffer per row and do not care about the 2 bytes still … Oops…
Fixed it! I have to increment the buffer position by 2 after every row, now I have a nice Picture!
So it is O.K. not careing about the 2 extra bytes in every row?
Thanks for your Help!!!
Billie
Ok, another Question:
The Image (850 x 1100 Pixels) is transfered in 15 chunks.
14 x 76 Rows = 1064
1 x 35 Rows = 35
----
1099
====
One row is missing?!
In memory transfer you need to anticipate the real size of your buffer based on columns*rows with the correct size in bytes for each row (depends on columns, bits per pixels, and alignment per row). Generally you should have a function in your code to handle mis-aligned rows in memory (I code in C++ so I can’t help you a lot in how to do that in Java).
@Billie wrote:
Ok, another Question:
The Image (850 x 1100 Pixels) is transfered in 15 chunks.
14 x 76 Rows = 1064
1 x 35 Rows = 35
----
1099
====
One row is missing?!
You can take a look at the source code for the DS, maybe it’s a bug, assuming a loop 0 to 1099 gives 1100, but in your output you should have 36 lines at the end.
I’m pretty sure its a bug, they mix offsets and ‘number of lines’, offsets start at 0, but ‘number of lines’ starts at 1 …
Billie
Would it be possible for you to post the complete code of your function and any other function called from it? I am trying to make Memory Buffer Method work but am having problems. Would greatly apprectiate it.
Unfortunately there are not too many examples of Memory Transfer method.
Hy McOhen!
Heres the complete Method for the memory transfer. I use Java as programming language and JNI (Java Native Interface) to map the Java-Objects to C-Structures. Ask if you don’t understand parts of the code. A great Help was the Twain Sample App Source.
private void initiateTransfer_Memory() {
String method = "initiateTransfer_Memory()";
TwainImageInfo twImageInfo = null;
TwainSetupMemXfer bufferSizes = null;
TwainPendingXfers pendingXfers = null;
ListbufferedImages = null;
try {
twImageInfo = new TwainImageInfo();
bufferSizes = new TwainSetupMemXfer();
pendingXfers = new TwainPendingXfers();
do {
checkTWRC(dsmEntry(appID, dsID, DG.IMAGE, DAT.IMAGEINFO, MSG.GET, twImageInfo));
checkTWRC(dsmEntry(appID, dsID, DG.CONTROL, DAT.SETUPMEMXFER, MSG.GET, bufferSizes));
if (LOG.isDebugEnabled()) {
LOG.debug(method, "twImageInfo=" + twImageInfo + ",bufferSizes=" + bufferSizes);
}
BufferedImage bfImage = new BufferedImage(twImageInfo.getImageWidth(), twImageInfo
.getImageLength(), BufferedImage.TYPE_INT_RGB);
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(bufferSizes.getPreferred());
TwainImageMemXfer memXferBuf = null;
try {
memXferBuf = new TwainImageMemXfer();
memXferBuf.setBytesPerRow(TWON_DONTCARE32);
memXferBuf.setBytesWritten(TWON_DONTCARE32);
memXferBuf.setColumns(TWON_DONTCARE32);
memXferBuf.setCompression(TWON_DONTCARE16);
memXferBuf.setRows(TWON_DONTCARE32);
memXferBuf.setXOffset(TWON_DONTCARE32);
memXferBuf.setYOffset(TWON_DONTCARE32);
memXferBuf.getMemory().setFlags(EnumSet.of(TWMF.APPOWNS, TWMF.POINTER));
memXferBuf.getMemory().setLength(bufferSizes.getPreferred());
memXferBuf.getMemory().setTheMem(byteBuffer);
int bitsPerPixel = twImageInfo.getBitsPerPixel();
int y = 0;
byte[] rgb = new byte[3];
while (true) {
byteBuffer.clear();
TWRC twRC = dsmEntry(appID, dsID, DG.IMAGE, DAT.IMAGEMEMXFER, MSG.GET, memXferBuf);
if (TWRC.SUCCESS.equals(twRC) || TWRC.XFERDONE.equals(twRC)) {
if (LOG.isDebugEnabled()) {
LOG.debug(method, "memXferBuf=" + memXferBuf);
}
int rows = memXferBuf.getRows();
int columns = memXferBuf.getColumns();
for (int i = 0; i < rows; i++) {
for (int x = 0; x < columns; x++) {
switch (bitsPerPixel) {
case 8:
byte b = byteBuffer.get();
rgb[0] = b;
rgb[1] = b;
rgb[2] = b;
break;
case 24:
byteBuffer.get(rgb);
break;
}
bfImage.setRGB(x, y, (rgb[0] & 0xff) << 16
| (rgb[1] & 0xff) << 8
| (rgb[2] & 0xff));
}
y++;
int extraBytes = 0;
switch (bitsPerPixel) {
case 8:
extraBytes = memXferBuf.getBytesPerRow() - columns;
break;
case 24:
extraBytes = memXferBuf.getBytesPerRow() - (columns * 3);
break;
}
byteBuffer.position(byteBuffer.position() + extraBytes);
}
if (TWRC.XFERDONE.equals(twRC)) {
if (bufferedImages == null) {
bufferedImages = new ArrayList();
}
bufferedImages.add(bfImage);
break;
}
} else {
try {
checkTWRC(twRC);
} catch (TwainException e) {
LOG.error(method, "Error transfering image", e);
}
break;
}
}
} finally {
if (memXferBuf != null) {
memXferBuf.free();
}
}
checkTWRC(dsmEntry(appID, dsID, DG.CONTROL, DAT.PENDINGXFERS, MSG.ENDXFER, pendingXfers));
} while (pendingXfers.getCount() != 0);
} catch (TwainException e) {
LOG.error(method, "Error initiating memory transfer", e);
} finally {
if (twImageInfo != null) {
twImageInfo.free();
}
if (bufferSizes != null) {
bufferSizes.free();
}
if (pendingXfers != null) {
pendingXfers.free();
}
}
notifyMemoryTransferListeners(bufferedImages);
}
Billie
Thanks a lot. I’ll let you know if have any other questions. Keep an eye.
Billie
I have a couple of questions. In calling the “DG_IMAGE, DAT_IMAGEMEMXFER, MSG_GET” tripplet, while I managed to get valid data, am having problems putting it together. The “TW_IMAGEMEMXFER” structure returns a value of “278” for compression. Beats me..! I don’t believe that value corresponds to any of the values from Twain:
TWCP_NONE = 0
TWCP_PACKBITS = 1
TWCP_GROUP31D = 2
TWCP_GROUP31DEOL = 3
TWCP_GROUP32D = 4
TWCP_GROUP4 = 5
TWCP_JPEG = 6
TWCP_LZW = 7
TWCP_JBIG = 8
TWCP_PNG = 9
TWCP_RLE4 = 10
TWCP_RLE8 = 11
TWCP_BITFIELDS = 12Is it wise to retrieve the image in compressed format? Wouldn’t that complicate the process of compiling the Tiles/Strips into meaningful bitmaps?
Do you happen to have an example of how to request accepted compression values from the source using capability negotiation?
Try negotiate ICAP_COMPRESSION before starting the transfer…
I don’t know what you expect from DAT_IMAGEMEMXFER concerning compression ?
Also inside my transfer loop I check again if the data received is compressed (sometimes, DS compresses images even if I negotiate TWCP_NONE), using DAT_IMAGEINFO after I receive a TWRC_XFERDONE from DAT_IMAGEMEMXFER.
Thierry
Thanks for your reply. As it turn out I found out the problem. I was retrieving information from DAT_IMAGEMEMXFER structure in the wrong place. That explains the gibberish I was getting. I now get TWCP_NONE for compression, the expected value. Twain specs makes it clear that TWCP_NONE should be the default behavior.
For those VB.net gurus up there, I finally managed to create a workable piece of code that captures images using BUFFERED MEMORY MODE. So if you need a sample code, post a reply.
Hi all again
I have a problem I can’t make any sense out of. While I managed to get the image back in order, the image looks like it was done at a very low resolution. In other words, there are no details.
Any feedback?
Ok guys
Ignore my last post. I figure it out. My bit operations were out of wack.
I was checking bit values on a byte using IF statements. For some reason the equality isn’t evaluated as an equality when dealing with bit operators. So, all I did is to modify the code to use a SELECT statements instead (SWITCH equivalent in C# and Java):THIS CODE DIDN’T WORK..!
If objByteRead And (2 ^ (intZ - 1)) = (2 ^ (intZ - 1)) Then
'Bitwise check of bit state (on/off).
objFrame.SetPixel(intCol - intZ, intRow, Color.White)
Else
objFrame.SetPixel(intCol - intZ, intRow, Color.Black)
End If
WHERE AS THIS CODE DID WORK…!
Select Case objByteRead And (2 ^ (intZ - 1))
Case (2 ^ (intZ - 1))
'Bitwise check of bit state (on/off).
objFrame.SetPixel(intCol - intZ, intRow, Color.White)
Case Else
objFrame.SetPixel(intCol - intZ, intRow, Color.Black)
End Select
- AuthorPosts