Use Gdip to split a single image into multiple images

Get help with using AutoHotkey and its commands and hotkeys
User avatar
JoeWinograd
Posts: 1537
Joined: 10 Feb 2014, 20:00

Use Gdip to split a single image into multiple images

11 Mar 2017, 12:15

Hi Gdip experts,

I'm wondering if anyone has attempted to split a single image into multiple images with Gdip. The purpose is to place multiple photos on a flatbed scanner, perform a scan to a single, 24-bit color image (probably JPG, but would be fine with a different image format), then run an AHK script that calls Gdip to split the single image into multiple images, one for each photo. The photos will be placed on the flatbed with ample separation so that there is plenty of "white space" to look for when attempting to perform the split.

The goal is to do something similar to Fred Weinhaus' ImageMagick multicrop script (and his updated multicrop2 script), but in AHK, utilizing Gdip. I don't need all the options that Fred has in his ImageMagick scripts — just the primary capability to do the split will be fine.

I'd appreciate any ideas on how to achieve this, especially existing AHK scripts that may be useful. Thanks very much, Joe
Helgef
Posts: 4447
Joined: 17 Jul 2016, 01:02
Contact:

Re: Use Gdip to split a single image into multiple images

14 Mar 2017, 12:12

Hi JoeWinograd. :wave:
I give this topic a little bump because I wonder:
Did you work on this? What is your apporach?
I wouldn't consider the main problem to be related to gdip, the main problem is finding and implementing a good algorithm. Gdip could be used to retreive the pixel array and saveing to file and such. I don't think gdip has any specific functions for this, but I don't really know much about gdip.

I follow this with interest, but I don't think I will work on it (now).
User avatar
JoeWinograd
Posts: 1537
Joined: 10 Feb 2014, 20:00

Re: Use Gdip to split a single image into multiple images

14 Mar 2017, 15:03

Hi Helgef,
Thanks for your reply. My first approach was to use a RunWait call in an AHK script to Fred's multicrop2 script, but he's geared towards Unix usage, and I wasn't having much luck with it in my AHK/Windows environment, which is why I posted this question — hoping that I could create a Gdip-based solution, since I have many AHK scripts that call Gdip and they all work well. While waiting for responses here, I tried another approach — using GIMP with a Divide Scanned Images filter. This approach is working well and I even published a 5-minute video tutorial about it:

How to divide/split a single image file into multiple image files

I'm pleased with that approach, which has worked fine on everything I've thrown at it, but I would still love to have a "pure" AHK script that does it, i.e., all AHK code with calls to only AHK libraries, such as Gdip. But I haven't figured out yet how to even get that approach off the ground. Regards, Joe
Helgef
Posts: 4447
Joined: 17 Jul 2016, 01:02
Contact:

Re: Use Gdip to split a single image into multiple images

15 Mar 2017, 10:35

Very nice tutorial JoeWinograd, good information. :thumbup:
User avatar
JoeWinograd
Posts: 1537
Joined: 10 Feb 2014, 20:00

Re: Use Gdip to split a single image into multiple images

15 Mar 2017, 10:59

Thank you, Helgef, I appreciate the kind words.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Use Gdip to split a single image into multiple images

15 Mar 2017, 15:12

Will somebody please post some relevant code! Arghhh!

OK, the code below will give a Picasso effect, it takes a printscreen, quarters it, and cycles the quarters round the screen like a merry-go-round.

Nice tutorial video, you could use GetPixel to check for whitespace, just got to see if you can work out a satisfactory algorithm. Start off with something and improve it, trial and error. E.g. check every 50th or 25th pixel to begin with, and narrow down.

Maybe you could get some info from an external command line tool, and pass information to AHK and Gdip functions.

A potential problem could be if the images are at an angle and not orthogonal.

It uses SplashImage which I hope will be kept in AHK v2 because it's so convenient.

Code: Select all

q:: ;Picasso effect, take a printscreen, quarter it, and cycle quarters like a merry-go-round
;ControlGet, hCtl, Hwnd, , Edit1, A
;WinGetPos, vPosX, vPosY, vPosW, vPosH, ahk_id %hCtl%
vPosX := 0, vPosY := 0, vPosW := A_ScreenWidth, vPosH := A_ScreenHeight

vList := "X,Y,W,H"
;make each dimension an even number
Loop, Parse, vList, `,
	if vPos%A_LoopField% & 1
		vPos%A_LoopField%--

vPosX1 := 0, vPosY1 := 0
vPosX2 := vPosW/2, vPosY2 := 0
vPosX3 := vPosW/2, vPosY3 := vPosH/2
vPosX4 := 0, vPosY4 := vPosH/2

pToken := Gdip_Startup()
pBitmap := Gdip_BitmapFromScreen(vPosX "|" vPosY "|" vPosW "|" vPosH)
pBitmapNew := Gdip_CreateBitmap(vPosW, vPosH)
G := Gdip_GraphicsFromImage(pBitmapNew)
Loop, 12
{
	vOffset := A_Index-1
	Loop, 4
	{
		;1->2, 2->3, 3->4, 4->1 ;vIndex := Mod(A_Index,4)+1
		;note: Gdip_DrawImage, destination coordinates, then source coordinates
		vIndex := Mod(A_Index+vOffset,4)+1
		Gdip_DrawImage(G, pBitmap, vPosX%vIndex%, vPosY%vIndex%, vPosW/2, vPosH/2, vPosX%A_Index%, vPosY%A_Index%, vPosW/2, vPosH/2)
	}

	;vPath = %A_Desktop%\z temp %A_Now%.png
	;Gdip_SaveBitmapToFile(pBitmapNew, vPath)

	hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmapNew)
	SplashImage, HBITMAP:%hBitmap%, B
	Sleep 300
}
SplashImage, Off

Gdip_DisposeImage(pBitmap)
Gdip_DeleteGraphics(G)
Gdip_DisposeImage(pBitmapNew)
Gdip_Shutdown(pToken)
MsgBox % "done"
Return
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 4447
Joined: 17 Jul 2016, 01:02
Contact:

Re: Use Gdip to split a single image into multiple images

15 Mar 2017, 15:50

jeeswg wrote:Will somebody please post some relevant code! Arghhh!
That is interesting jeeswg :wave:
User avatar
JoeWinograd
Posts: 1537
Joined: 10 Feb 2014, 20:00

Re: Use Gdip to split a single image into multiple images

16 Mar 2017, 23:54

Hi jeeswg,
it takes a printscreen, quarters it, and cycles the quarters round the screen like a merry-go-round
Very nice!
use GetPixel to check for whitespace
Here's some test code that I played with a while ago to display the RGB of the current mouse position in both decimal and hex:

Code: Select all

; get color at mouse pos
!^F6:: ; pick whatever hotkey you want
CoordMode,Mouse,Screen
CoordMode,Pixel,Screen
MouseGetPos,Xpos,Ypos
PixelGetColor,HexColor,%Xpos%,%Ypos%,Slow RGB
If (ErrorLevel<>0)
{
  MsgBox,4112,Fatal Error,Error Level from PixelGetColor: %ErrorLevel%`nx%Xpos%/y%Ypos%
  ExitApp
}
RedDec:=(16*Hex2Dec(SubStr(HexColor,3,1)))+Hex2Dec(SubStr(HexColor,4,1))
GreenDec:=(16*Hex2Dec(SubStr(HexColor,5,1)))+Hex2Dec(SubStr(HexColor,6,1))
BlueDec:=(16*Hex2Dec(SubStr(HexColor,7,1)))+Hex2Dec(SubStr(HexColor,8,1))
MsgBox,4096,x%Xpos%/y%Ypos%,HexRGB=%HexColor%`nDecimalRGB=%RedDec% %GreenDec% %BlueDec%
Return

Hex2Dec(hex)
{
  If hex is digit
  {
    If (hex>9 or hex="")
    {
      MsgBox,4112,Fatal Error,Bad input sent to Hex2Dec: %hex%
      ExitApp
    }
    Return hex
  }
  If (hex="a")
    Return 10
  If (hex="b")
    Return 11
  If (hex="c")
    Return 12
  If (hex="d")
    Return 13
  If (hex="e")
    Return 14
  If (hex="f")
    Return 15
  MsgBox,4112,Fatal Error,Non-hex character sent to Hex2Dec: %hex%
  ExitApp
}
I could utilize it to check for white space.
Maybe you could get some info from an external command line tool, and pass information to AHK and Gdip functions.
If I can't do it with all AHK code and libraries, that's definitely a way to go.
A potential problem could be if the images are at an angle and not orthogonal.
Yes, that's why the GIMP-based script can optionally run a GIMP-based plugin called deskew.exe (although my experience with it is that it doesn't work very well).

Thanks for your interest in this and your ideas! Regards, Joe
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Use Gdip to split a single image into multiple images

17 Mar 2017, 00:42

I don't do that much with images. An algorithm I might use, that just occurred to me, subject to speed:

Using Gdip_GetPixel (sorry, I meant this, not AHK's PixelGetColor), check every 10th horizontal/vertical pixel's colour. If you find 16 white pixels like so:
. . . .
. . . .
. . . .
. . . .
and all the pixels in all 9 squares are white, then we decide that the central square will not appear in any of the final images. Some of those surrounding squares may be also end up as central squares and discarded. We might refine and discard some further pixels, and leave some bordering whitespace around each image as in your video.

Obviously there is a danger if the image itself contains white pixels.

To test that algorithm you could make all of those central squares red, and view the resulting image.

Then you have to decide which lines to cut. And my script from earlier can cut up images.

Interesting re. deskew.exe.

Yeah I like to do everything by AHK, but then again do I want to recreate SoX and FFmpeg! Although I've found Gdip can do everything I want to do with images ultimately. [EDIT: Actually if I ever find out how these codecs work, I'll probably be wanting to amend them in some way as well.]

Btw did you win some Experts Exchange award or something. I came across an article the other day, I thought the name looked familiar!

Re. dec and hex:

Code: Select all

q:: ;dec and hex

;for hex2dec:
vNum := 0xFFFFFF
MsgBox % vNum+0 ;16777215

;for dec2hex
vNum := 16777215
MsgBox % Format("0x{:X}", vNum) ;0xFFFFFF

;for splitting hex
vText := 0x0A0B0C
vNum1 := Format("{:d}", "0x" SubStr(vText, 3, 2))
vNum2 := Format("{:d}", "0x" SubStr(vText, 5, 2))
vNum3 := Format("{:d}", "0x" SubStr(vText, 7, 2))
MsgBox % vNum1 " " vNum2 " " vNum3 ;10 11 12
Return
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Use Gdip to split a single image into multiple images

17 Mar 2017, 01:40

A vertical/horizontal conveyor belt (filmstrip) script, similar to the 4 quarters one above.

Code: Select all

q:: ;vertical/horizontal conveyor belt (filmstrip)
vPosX := 0, vPosY := 0, vPosW := A_ScreenWidth, vPosH := A_ScreenHeight

;number of strips
vNum := 8
;strips
vType := "h" ;horizontal
vType := "v" ;vertical

;make each dimension a multiple of n
vPosW := vPosW - Mod(vPosW, vNum)
vPosH := vPosH - Mod(vPosH, vNum)
vStripW := Round(vPosW / vNum)
vStripH := Round(vPosH / vNum)

Loop, % vNum
	vPosX%A_Index% := 0, vPosY%A_Index% := (vPosH/vNum) * (A_Index-1)

pToken := Gdip_Startup()
pBitmap := Gdip_BitmapFromScreen(vPosX "|" vPosY "|" vPosW "|" vPosH)
pBitmapNew := Gdip_CreateBitmap(vPosW, vPosH)
G := Gdip_GraphicsFromImage(pBitmapNew)
Loop, % vNum * 3
{
	;vOffset := A_Index-1 ;upwards/leftwards
	vOffset := (vNum*A_Index)+1-A_Index ;downwards/rightwards ;keep it positive because of Mod
	Loop, % vNum
	{
		vIndex1 := Mod(A_Index+vOffset,vNum)+1
		vIndex2 := Mod(A_Index,vNum)+1
		vPosX1 := vStripW*(vIndex1-1)
		vPosX2 := vStripW*(vIndex2-1)
		vPosY1 := vStripH*(vIndex1-1)
		vPosY2 := vStripH*(vIndex2-1)

		if (vType = "h")
			vPosX1 := 0, vPosX2 := 0, vStripW := vPosW
		if (vType = "v")
			vPosY1 := 0, vPosY2 := 0, vStripH := vPosH

		;note: Gdip_DrawImage, destination coordinates, then source coordinates
		Gdip_DrawImage(G, pBitmap, vPosX2, vPosY2, vStripW, vStripH, vPosX1, vPosY1, vStripW, vStripH)
	}

	;vPath = %A_Desktop%\z temp %A_Now%.png
	;Gdip_SaveBitmapToFile(pBitmapNew, vPath)

	hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmapNew)
	SplashImage, HBITMAP:%hBitmap%, B
	Sleep 300
}
SplashImage, Off

Gdip_DisposeImage(pBitmap)
Gdip_DeleteGraphics(G)
Gdip_DisposeImage(pBitmapNew)
Gdip_Shutdown(pToken)
MsgBox % "done"
Return
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Use Gdip to split a single image into multiple images

17 Mar 2017, 14:43

I was just sitting down, AFK, not thinking about AutoHotkey, and suddenly a basic simpler algorithm occurred to me, namely group together non-whitespace pixels, rather than whitespace pixels.

First determine the criteria for pixels that are whitespace.
- Choose ranges for R, G, B, replace such pixels with red.
- Does that red area correspond to what you consider whitespace?
- Tweak until it's satisfactory.

Imagine dots like so in a grid pattern: '::', all over the image.
- Any such dots that are non-whitespace, and the pixels in the square around them, will end up in one of our final images.
- The size of such squares might be for example, the maximum desired amount of border whitespace around each image.
- Check for adjacent (and nearby) non-whitespace dots, and build up imaginary 'blobs' of nearby squares, ultimately make these into rectangles.
- (If you scanned in a circular coin for example, or an L-shape, the rules would still give you a rectangle.)
- Also extend the edges of those rectangles, based on how much border whitespace you want.
- Those rectangles will then be cut as images.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
JoeWinograd
Posts: 1537
Joined: 10 Feb 2014, 20:00

Re: Use Gdip to split a single image into multiple images

18 Mar 2017, 01:51

Thank you, jeeswg, for so many interesting ideas. I'll study them over the coming days. Your filmstrip script, like the Picasso one, is cool! Thanks, too, for the dec/hex code.
Btw did you win some Experts Exchange award or something. I came across an article the other day, I thought the name looked familiar!
Yes, that's me. I participate a lot at Experts Exchange and they gave me their annual Most Valuable Expert (MVE) Award, both last year and this year. The article you saw was probably their blog entry announcing this year's awards. Thanks for noticing! :D
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Use Gdip to split a single image into multiple images

12 Oct 2017, 19:11

One thing relevant to the initial problem is trimming whitespace from an image, here's an example:

Code: Select all

;[Gdip functions]
;GDI+ standard library 1.45 by tic - AutoHotkey Community
;https://autohotkey.com/boards/viewtopic.php?f=6&t=6517

q:: ;image on the clipboard - crop whitespace
pToken := Gdip_Startup()
pBitmap := Gdip_CreateBitmapFromClipboard()
Gdip_GetDimensions(pBitmap, vImgW, vImgH)

vCol2 := 0xFFFFFF ;whitespace colour, which colour is 'white'

Loop, 4
{
	vIndex := A_Index
	if (vIndex = 1) ;get first non-blank row ;y
		vRowOrCol := vImgH, vPixel := vImgW, vDiff := 1, vNumRC := -1
	else if (vIndex = 2) ;get last non-blank row ;y
		vRowOrCol := vImgH, vPixel := vImgW, vDiff := -1, vNumRC := vImgH
	else if (vIndex = 3) ;get first non-blank column ;x
		vPixel := vImgH, vRowOrCol := vImgW, vDiff := 1, vNumRC := -1
	else if (vIndex = 4) ;get last non-blank column ;x
		vPixel := vImgH, vRowOrCol := vImgW, vDiff := -1, vNumRC := vImgW
	vIsCol := 0
	Loop, % vRowOrCol
	{
		vNumRC += vDiff
		Loop, % vPixel
		{
			vNumP := A_Index-1
			if (vIndex = 1) || (vIndex = 2)
				vCol := Gdip_GetPixel(pBitmap, vNumP, vNumRC)
			else if (vIndex = 3) || (vIndex = 4)
				vCol := Gdip_GetPixel(pBitmap, vNumRC, vNumP)
			if !((vCol & 0xFFFFFF) = vCol2)
			{
				vIsCol := 1
				break
			}
		}
		if vIsCol
			break
		if (A_Index = vImgH)
		{
			MsgBox, % "entire image is one colour"
			return
		}
	}
	vPos%vIndex% := vNumRC
}

pBitmap2 := Gdip_CloneBitmapArea(pBitmap, vPos3, vPos1, vPos4-vPos3+1, vPos2-vPos1+1)
Gdip_SetBitmapToClipboard(pBitmap2)
Gdip_DisposeImage(pBitmap)
Gdip_DisposeImage(pBitmap2)
Gdip_Shutdown(pToken)
MsgBox, % "done"
return
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Use Gdip to split a single image into multiple images

12 Oct 2017, 19:12

I've created a more versatile script cf. the moving tiles scripts above, this one can do vertical/horizontal movement if you adjust the settings, but it's initially set to do diagonal movement:

Code: Select all

;[Gdip functions]
;GDI+ standard library 1.45 by tic - AutoHotkey Community
;https://autohotkey.com/boards/viewtopic.php?f=6&t=6517

q:: ;gdip diagonal shift (split screen into rectangles, move down and right each time)
vPosX := 0, vPosY := 0, vPosW := A_ScreenWidth, vPosH := A_ScreenHeight

vCountW := 3, vCountH := 4, vNum := 1
vCountW := 3, vCountH := 3, vNum := 3
vCountW := 6, vCountH := 6, vNum := 1
;vCountW := 12, vCountH := 12, vNum := 1
;vCountW := 24, vCountH := 24, vNum := 1

vAdjustW := 1, vAdjustH := 1
;vAdjustW := 1, vAdjustH := -1
;vAdjustW := -1, vAdjustH := 1
;vAdjustW := -1, vAdjustH := -1

;vAdjustW := 1, vAdjustH := 0
;vAdjustW := -1, vAdjustH := 0
;vAdjustW := 0, vAdjustH := 1
;vAdjustW := 0, vAdjustH := -1

;make each dimension a multiple of n
vPosW := vPosW - Mod(vPosW, vCountW)
vPosH := vPosH - Mod(vPosH, vCountH)
vStripW := Round(vPosW / vCountW)
vStripH := Round(vPosH / vCountH)

pToken := Gdip_Startup()
pBitmap := Gdip_BitmapFromScreen(vPosX "|" vPosY "|" vPosW "|" vPosH)
pBitmapNew := Gdip_CreateBitmap(vPosW, vPosH)
G := Gdip_GraphicsFromImage(pBitmapNew)
vNow := A_Now
vOffsetW := 0
vOffsetH := 0
Loop, % vNum * ((vCountW=vCountH) ? vCountW : (vCountW*vCountH))
{
	vOffsetW += vAdjustW
	vOffsetH += vAdjustH
	Loop, % vCountH
	{
		vIndexY1 := A_Index-1
		vIndexY2 := Mod(A_Index-1+vOffsetH, vCountH)
		if (vIndexY2 < 0)
			vIndexY2 += vCountH
		Loop, % vCountW
		{
			vIndexX1 := A_Index-1
			vIndexX2 := Mod(A_Index-1+vOffsetW, vCountW)
			if (vIndexX2 < 0)
				vIndexX2 += vCountW
			vPosX1 := vStripW*(vIndexX1)
			vPosY1 := vStripH*(vIndexY1)
			vPosX2 := vStripW*(vIndexX2)
			vPosY2 := vStripH*(vIndexY2)

			;note: Gdip_DrawImage, destination coordinates, then source coordinates
			Gdip_DrawImage(G, pBitmap, vPosX2, vPosY2, vStripW, vStripH, vPosX1, vPosY1, vStripW, vStripH)
		}
	}

	;vIndex := Format("{:04}", A_Index)
	;vPath = %A_Desktop%\z temp %vNow% %A_Index%.png
	;Gdip_SaveBitmapToFile(pBitmapNew, vPath)

	hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmapNew)
	SplashImage, % "HBITMAP:" hBitmap, B
	Sleep, 300
}
SplashImage, Off

Gdip_DisposeImage(pBitmap)
Gdip_DeleteGraphics(G)
Gdip_DisposeImage(pBitmapNew)
Gdip_Shutdown(pToken)
MsgBox, % "done"
return

;==================================================

;e.g. shift down + right
;ABC
;DEF
;HIJ

;JHI
;CAB
;FDE

;EFD
;IJH
;BCA

;ABC
;DEF
;HIJ

;==================================================
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Use Gdip to split a single image into multiple images

12 Oct 2017, 19:19

I have a working script for the original problem:

Code: Select all

;split a single image into multiple images introduction

;links:
;Gdip: image binary data to hex string for OCR - AutoHotkey Community
;https://autohotkey.com/boards/viewtopic.php?f=5&t=35339
;check if rectangles (windows) overlap - AutoHotkey Community
;https://autohotkey.com/boards/viewtopic.php?f=6&t=30809

;convert pixels to text (pixels to RGB hex)

;check each pixel:
;if pixel is a background colour, continue
;if pixel is not a background colour,
;... check against existing rectangles:
;- if it matches 2 or more rectangles, combine those rectangles, expand rectangle if necessary
;- if it matches 1 rectangle, expand rectangle if necessary
;- else, create new rectangle

;to finish:
;compare rectangles, combine any that overlap

;creating/expanding rectangles works like this:
;if border distance is 2:
;point (10,10) will have rectangle (8,8) to (12,12)
;if border distance is 0:
;point (10,10) will have rectangle (10,10) to (10,10)

;==================================================

;[Gdip functions]
;GDI+ standard library 1.45 by tic - AutoHotkey Community
;https://autohotkey.com/boards/viewtopic.php?f=6&t=6517

q:: ;split a single image into multiple images
pToken := Gdip_Startup()
vPath = %A_Desktop%\MyFile.png
vDirOut = %A_Desktop%
pBitmap := Gdip_CreateBitmapFromFile(vPath)
Gdip_GetImageDimensions(pBitmap, vImgW, vImgH)

hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap)
VarSetCapacity(DIBSECTION, vSizeDS:=A_PtrSize=8?104:84, 0)
DllCall("gdi32\GetObject", Ptr,hBitmap, Int,vSizeDS, Ptr,&DIBSECTION)
vAddr := NumGet(&DIBSECTION, A_PtrSize=8?24:20, "Ptr") ;bmBits
vSize := NumGet(&DIBSECTION, A_PtrSize=8?52:44, "UInt") ;biSizeImage
vHex := JEE_BinDataToHex(vAddr, vSize)

vOutput := RegExReplace(vHex, "(.{6})(.{2})", ",$1")
vOutput := RegExReplace(vOutput, ".{" (7*vImgW) "}", "$0`n")
;note: image hex data is upside down (top row of pixels are at bottom of string)
vOutput := JEE_StrReverseLines(vOutput, "`n", "")
vOutput := SubStr(vOutput, 2)

vBdrL := 2, vBdrR := 2
vBdrT := 2, vBdrB := 2
vX := -1, vY := 0
oRect := {}
Loop, Parse, vOutput, % ","
{
	vX += 1
	if (vX = vImgW)
		vX := 0, vY += 1
	vTemp := A_LoopField
	if (vTemp = "FFFFFF")
		continue

	oTemp := [vX-vBdrL, vY-vBdrT, vX+vBdrR, vY+vBdrB]
	;MsgBox, % Format("{}, {}, {}, {}", oTemp*)
	vLastMatch := 0
	Loop, % oRect.Length()
	{
		vIndex := oRect.Length() + 1 - A_Index
		if !JEE_RectObjOverlap(oTemp, oRect[vIndex])
			continue
		oRect[vIndex] := JEE_RectObjCombine(oTemp, oRect[vIndex])
		if vLastMatch
		{
			oRect[vIndex] := JEE_RectObjCombine(oRect[vIndex], oRect[vLastMatch])
			oRect.RemoveAt(vLastMatch)
		}
		vLastMatch := vIndex
	}
	if !vLastMatch
		oRect.Push(oTemp)
}
;MsgBox, % oRect.Length()

;safety check
;compare rectangles, combine any that overlap
Loop, % oRect.Length()
{
	vIndex2 := oRect.Length() + 1 - A_Index
	oTemp := oRect[vIndex2]
	vLastMatch := 0
	Loop, % oRect.Length()
	{
		vIndex := oRect.Length() + 1 - A_Index
		if (vIndex >= vIndex2)
			continue
		if !JEE_RectObjOverlap(oTemp, oRect[vIndex])
			continue
		oRect[vIndex] := JEE_RectObjCombine(oTemp, oRect[vIndex])
		if vLastMatch
		{
			oRect[vIndex] := JEE_RectObjCombine(oRect[vIndex], oRect[vLastMatch])
			oRect.RemoveAt(vLastMatch)
		}
		vLastMatch := vIndex
	}
}
;MsgBox, % oRect.Length()

;rectangles to images
vNow := A_Now
Loop, % oRect.Length()
{
	;MsgBox, % Format("{} {} {} {}", oRect[A_Index]*)
	vPosX := oRect[A_Index].1
	vPosY := oRect[A_Index].2
	vPosW := oRect[A_Index].3 - oRect[A_Index].1 + 1
	vPosH := oRect[A_Index].4 - oRect[A_Index].2 + 1

	pBitmapNew := Gdip_CreateBitmap(vPosW, vPosH)
	G := Gdip_GraphicsFromImage(pBitmapNew)
	;note: Gdip_DrawImage, destination coordinates, then source coordinates
	Gdip_DrawImage(G, pBitmap, 0, 0, vPosW, vPosH, vPosX, vPosY, vPosW, vPosH)
	hBitmapNew := Gdip_CreateHBITMAPFromBitmap(pBitmapNew)
	SplashImage, % "HBITMAP:" hBitmapNew, B
	Sleep, 2000
	SplashImage, Off

	vIndex := Format("{:04}", A_Index)
	vPath = %vDirOut%\z split image %vNow% %vIndex%.png
	if !FileExist(vPath)
		Gdip_SaveBitmapToFile(pBitmapNew, vPath)
	DeleteObject(hBitmapNew)
	Gdip_DisposeImage(pBitmapNew)
}

DeleteObject(hBitmap)
Gdip_DisposeImage(pBitmap)
Gdip_Shutdown(pToken)
MsgBox, % "done"
return

;==================================================

JEE_BinDataToHex(vAddr, vSize)
{
	;CRYPT_STRING_HEX := 0x4 ;to return space/CRLF-separated text
	;CRYPT_STRING_HEXRAW := 0xC ;to return raw hex (not supported by Windows XP)
	DllCall("crypt32\CryptBinaryToString", Ptr,vAddr, UInt,vSize, UInt,0x4, Ptr,0, UIntP,vChars)
	VarSetCapacity(vHex, vChars*2, 0)
	DllCall("crypt32\CryptBinaryToString", Ptr,vAddr, UInt,vSize, UInt,0x4, Str,vHex, UIntP,vChars)
	vHex := StrReplace(vHex, "`r`n")
	vHex := StrReplace(vHex, " ")
	return vHex
}

;==================================================

JEE_StrReverseLines(ByRef vText, vDelim:="`r`n", vSep:="`r`n")
{
	if (vText = "")
		return vText
	vOutput := ""
	VarSetCapacity(vOutput, StrLen(vText)*2)
	oArray := StrSplit(vText, vDelim)
	Loop, % oArray.Length() - 1
		vOutput .= oArray[oArray.Length()+1-A_Index] vSep
	vOutput .= oArray.1
	return vOutput
}

;==================================================

JEE_RectObjCombine(oRect1, oRect2)
{
	oRect := []
	oRect.1 := oRect1.1 < oRect2.1 ? oRect1.1 : oRect2.1 ;min
	oRect.2 := oRect1.2 < oRect2.2 ? oRect1.2 : oRect2.2 ;min
	oRect.3 := oRect1.3 > oRect2.3 ? oRect1.3 : oRect2.3 ;max
	oRect.4 := oRect1.4 > oRect2.4 ? oRect1.4 : oRect2.4 ;max
	return oRect
}

;==================================================

JEE_RectObjOverlap(oRect1, oRect2)
{
	;if (A is left of B) [rightmost A is left of leftmost B]
	;|| (A is right of B) [leftmost A is right of righttmost B]
	;|| (A is is above B) [bottom of A is is above top of B]
	;|| (A is is below B) [top of A is is below bottom of B]
	;then A and B do not overlap
	if (oRect1.3 < oRect2.1)
	|| (oRect1.1 > oRect2.3)
	|| (oRect1.4 < oRect2.2)
	|| (oRect1.2 > oRect2.4)
		return 0
	else
		return 1
}

;==================================================
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
Xtra
Posts: 1788
Joined: 02 Oct 2015, 12:15

Re: Use Gdip to split a single image into multiple images

12 Oct 2017, 21:17

Pass the original pBitmap to this function with the x,y,w,h you want from it and it will return pBitmap2 you can save.
If the images are precisely put on the scanner the same way every time then this could work without repeat checking of the positions.

Code: Select all

Gdip_CropImage(pBitmap, x, y, w, h)
{
   pBitmap2 := Gdip_CreateBitmap(w, h), G2 := Gdip_GraphicsFromImage(pBitmap2)
   Gdip_DrawImage(G2, pBitmap, 0, 0, w, h, x, y, w, h)
   Gdip_DeleteGraphics(G2)
   return pBitmap2
}
HTH

Note:
To put them on the scanner in the same positions every time i would make a template fixture with rectangle holes the images fit into.
This way you can scan them precisely as fast as possible.

edit: added additional note.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Use Gdip to split a single image into multiple images

12 Oct 2017, 21:24

@Xtra: It's possible that the Gdip_CloneBitmapArea function already does this, with similar or exactly the same results.

I was considering replacing Gdip_DrawImage with Gdip_CloneBitmapArea, I'm not sure if one is preferable to the other.

One thing that has to be considered is how functions handle transparency.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
Xtra
Posts: 1788
Joined: 02 Oct 2015, 12:15

Re: Use Gdip to split a single image into multiple images

12 Oct 2017, 21:36

@jeeswg Gdip_CloneBitmapArea looks like a better option its worth checking out. I will check it out when i get time.


Note:
@jeeswg Gdip_CloneBitmapArea works great for this and by default the image format is PixelFormat32bppARGB so transparency is not an issue.

Here is a list of all the image formats: Names, Dec, Hex and Description
Spoiler





edit: add note
User avatar
JoeWinograd
Posts: 1537
Joined: 10 Feb 2014, 20:00

Re: Use Gdip to split a single image into multiple images

14 Oct 2017, 12:36

Hi jeeswg,
A huge thanks for those two scripts! I tested both — they both work perfectly! I wrote a script for a client that needs to trim the whitespace on every page of a multi-page PDF. My script calls Xpdf's PDFtoPNG.exe utility to convert each page to a PNG, and then calls ImageMagick's magick.exe to trim the whitespace. Your code provides an alternative to ImageMagick for trimming the whitespace. Likewise, your script to split a single image into multiple images gives me a lot more flexibility than I have with the GIMP and Divide-Scanned-Images filter that I've been using. I can see incorporating it into an AHK script that processes all images in a folder, even recursing into subfolders. It can be compiled into a stand-alone program so the user won't have to download/install GIMP, the Divide-Scanned-Images filter, or, for that matter, anything. I very much appreciate your efforts!

Hi Xtra,
Thanks for your efforts to tweak jeeswg's code — much appreciated!

Regards, Joe

Return to “Ask For Help”

Who is online

Users browsing this forum: Bing [Bot], Clemens375, kevlea, majstang, stuarty27, vonsworld and 48 guests