Home Technical Talk

Autodesk 3ds Maxscript: How To Get Pixels From Opened Bitmap?

polycounter lvl 6
Offline / Send Message
MrQuetch polycounter lvl 6
Hi, there. I have been looking at the Autodesk 3ds Maxscript documentation lately. I believe that this piece of code I have should work, but for some reason it does not. So, "theBitmap" code appears to work - I don't receive errors. But, "thePixels" code appears to throw errors. I don't know why it would when "theBitmap" is already opened for reading. I must be missing something small. Basically, I want to read all of the pixels from the bitmap, and print them to hex in an array as: 0xRRGGBBAA. The texture is 32x32 in size, so I only need to loop through 1024 times.
texel_count = 0
format "unsigned int %[] = { \n" (getFilenameFile(selection[1].material.diffusemap.filename)) to:fStream
theBitmap = openbitmap(getFilenameFile(selection[1].material.diffusemap.filename))
thePixels = getPixels theBitmap [0,0] 1 linear:false
for i = 1 to 1024 do
(
format "%%%%%" (bit.intashex(thePixels.r)) (bit.intashex(thePixels.g)) (bit.intashex(thePixels.b)) (bit.intashex(255)) "," to:fStream
texel_count += 1
		
if (texel_count >= 32) do
	(
	format " \n" to:fStream
	texel_count = 0
	)
)
format "}; \n" to:fStream
format " \n" to:fStream
I am aware that "thePixels" line of code is only looking at the top corner: [0,0]. But, in order to at least check the other pixels in the array, I must figure out how to read the first one. I will keep working to see if I can figure this out. I appreciate any help and advice that I can receive. Thanks.

Replies

  • poopipe
    Offline / Send Message
    poopipe grand marshal polycounter
    aBitmap = selectBitMap caption:"Select a Bitmap"
    --Print(aBitmap.height)
    --Print(aBitmap.width)
    aLength = aBitmap.height
    aWidth = aBitmap.width
    
    for i = 0 to (aLength-1) do
    (
    for j = 0 to (aWidth - 1) do
    (
       -- Print(i)
       -- Print(j)
        aPixel = getPixels aBitmap [j,i] 1
        aPixelColor = [ aPixel[1].red, aPixel[1].green, aPixel[1].blue]
    	
    	if aPixelColor != [255,0,255] do
    	(
        aBox = instance $Box001
    	aBox.position = [i,j,0]
    	aBox.wireColor = aPixelColor
    	)
    	--: width:0.1 length:0.1 height:aBrightness
    )
    )
    

    I've not tried this code since 2015 but it worked then.

    I think you're misunderstanding how the bitmap data is structured.  It's been ages so I'm sure there's a more elegant way to collect the data etc. But it should get you going. 
  • MrQuetch
    Offline / Send Message
    MrQuetch polycounter lvl 6
    poopipe said:
    aBitmap = selectBitMap caption:"Select a Bitmap"
    --Print(aBitmap.height)
    --Print(aBitmap.width)
    aLength = aBitmap.height
    aWidth = aBitmap.width
    
    for i = 0 to (aLength-1) do
    (
    for j = 0 to (aWidth - 1) do
    (
       -- Print(i)
       -- Print(j)
        aPixel = getPixels aBitmap [j,i] 1
        aPixelColor = [ aPixel[1].red, aPixel[1].green, aPixel[1].blue]
    	
    	if aPixelColor != [255,0,255] do
    	(
        aBox = instance $Box001
    	aBox.position = [i,j,0]
    	aBox.wireColor = aPixelColor
    	)
    	--: width:0.1 length:0.1 height:aBrightness
    )
    )
    

    I've not tried this code since 2015 but it worked then.

    I think you're misunderstanding how the bitmap data is structured.  It's been ages so I'm sure there's a more elegant way to collect the data etc. But it should get you going. 
    Hi there, poopipe.

    Thank you for taking the time to help me out. I agree, I'm definitely misunderstanding the structure. This explains a lot of things actually. I don't want to have to select the bitmap, although I may have to in this regard. I've been working on a batch exporter for textured models, and wanted to be able to get the textured bitmap from the objects they're applied to, just so I wouldn't have to go and load each one at a time. The objects already have materials and textures applied to those materials - if that makes sense. Although, I guess I could load the textures before hand, and just save them all in the right format.

    Thank you very much, I will see what I can do.

    Edit: It seems to have trouble trying to get the colors. Either 'r', 'red', or even 'x' return as undefined.
  • Noors
    Offline / Send Message
    Noors greentooth
    You may just use
    selection[1].material.diffusemap<br><br>--or for batching, stuff like :<br><br>sel = selection as array<br>for obj in sel do<br>(
    <code> theBitmap = obj.material.diffusemap
    --
    )
    </code><code>theBitmap =

    as your bitmap file is already loaded.

    Maxscript help says to avoid getpixels for every pixel and use it per row, but else code is working for me. Not an issue fot a 32*32 bitmap. So i don't know how comes you don't have red ?

    For the record, getpixels per row :
    aBitmap = selectBitMap caption:"Select a Bitmap"<br><br>ts= timestamp()<br><br>aHeight = aBitmap.height<br>aWidth = aBitmap.width<br> <br>for i = 0 to (aHeight-1) do<br>(<br>	aRow = getPixels aBitmap [0,i] aWidth<br>	<br>	for j = 1 to aWidth do<br>	(<br>		aPixelColor = [ aRow[j].red, aRow[j].green, aRow[j].blue]<br>		--format "pixel color : % \n" aPixelColor<br>	)<br><br>)<br><br>format "took : % ms \n" (timestamp()-ts)


  • MrQuetch
    Offline / Send Message
    MrQuetch polycounter lvl 6
    Noors said:
    You may just use
    selection[1].material.diffusemap<br><br>--or for batching, stuff like :<br><br>sel = selection as array<br>for obj in sel do<br>(
    <code> theBitmap = obj.material.diffusemap
    --
    )
    </code><code>theBitmap =

    as your bitmap file is already loaded.

    Maxscript help says to avoid getpixels for every pixel and use it per row, but else code is working for me. Not an issue fot a 32*32 bitmap. So i don't know how comes you don't have red ?

    For the record, getpixels per row :
    aBitmap = selectBitMap caption:"Select a Bitmap"<br><br>ts= timestamp()<br><br>aHeight = aBitmap.height<br>aWidth = aBitmap.width<br> <br>for i = 0 to (aHeight-1) do<br>(<br>	aRow = getPixels aBitmap [0,i] aWidth<br>	<br>	for j = 1 to aWidth do<br>	(<br>		aPixelColor = [ aRow[j].red, aRow[j].green, aRow[j].blue]<br>		--format "pixel color : % \n" aPixelColor<br>	)<br><br>)<br><br>format "took : % ms \n" (timestamp()-ts)


    Hi, Noors.

    Thank you so much for your examples! Honestly, I don't know why Maxscript has been acting this way recently. Perhaps my loops are off by a single number. I will try these out and see what I can do. I really appreciate this.
  • MrQuetch
    Offline / Send Message
    MrQuetch polycounter lvl 6
    Alright, my loops were off by one number and I was't accessing the colors as arrays. I've definitely learned. Thank you so much guys, it works perfectly now. If there is one thing I could change though, it would be to print the hex in capitalized letters - right now they're lower cased.

    Here is my final code:

    fStream = newScript()
    
    aBitmap = selectBitMap caption:"Select a Bitmap"
    
    aHeight = aBitmap.height
    aWidth = aBitmap.width
    
    count = 0
    
    format "// %x% \n" (aHeight) (aWidth) to:fStream
    format "unsigned int %[] = { \n" (getFilenameFile(aBitmap.filename)) to:fStream
    
    for i = 0 to (aHeight-1) do
    	(
    	aRow = getPixels aBitmap [0, i] aWidth
    	
    	for j = 1 to aWidth do
    		(
    		aPixelColor = [ aRow[j].red, aRow[j].green, aRow[j].blue]
    		
    		format "0x" to:fStream
    		
    		if (aPixelColor[1] < 16) then
    			(
    			format "0%" (bit.intAsHex(aPixelColor[1])) to:fStream
    			)
    		else
    			(
    			format "%" (bit.intAsHex(aPixelColor[1])) to:fStream
    			)
    		if (aPixelColor[2] < 16) then
    			(
    			format "0%" (bit.intAsHex(aPixelColor[2])) to:fStream
    			)
    		else
    			(
    			format "%" (bit.intAsHex(aPixelColor[2])) to:fStream
    			)
    		if (aPixelColor[3] < 16) then
    			(
    			format "0%" (bit.intAsHex(aPixelColor[3])) to:fStream
    			)
    		else
    			(
    			format "%" (bit.intAsHex(aPixelColor[3])) to:fStream
    			)
    		
    		format "ff," to:fStream
    		
    		count += 1
    		
    		if (count >= 32) do
    			(
    			format "\n" to:fStream
    			count = 0
    			)
    		)
    	)
    
    format "}; \n" to:fStream

  • Noors
    Offline / Send Message
    Noors greentooth
    Use the toUpper built-in function
    toUpper (bit.intAsHex(aPixelColor[1]))) to:fStream <br>--small tips to make code clearer (or shorter to navigate into), if you just have one line of code in a loop, you can write on one line:
    if (aPixelColor[1] < 16) then format "0%" (toUpper(bit.intAsHex(aPixelColor[1]))) to:fStream
    else format "%" (<code>toUpper(bit.intAsHex(aPixelColor[1]))) to:fStream

    format "0%" (
    for i = 0 to (aHeight-1) do<br>(<br>	aRow = getPixels aBitmap [0, i] aWidth<br><br>	for j = 1 to aWidth do<br>	(<br><br>		r = aRow[j].red<br>		rHex = toUpper (bit.intAsHex(r))<br>			<br>		g = aRow[j].green<br>		gHex = toUpper (bit.intAsHex(g))<br>			<br>		b = aRow[j].blue<br>		bHex = toUpper (bit.intAsHex(b))<br>		<br>		format "0x" to:fStream<br>		<br>		if r < 16 then format "0%" rHex to:fStream<br>		else format "%" rHex to:fStream<br>			<br>		if g < 16 then format "0%" gHex to:fStream<br>		else format "%" gHex to:fStream<br>			<br>		if b < 16 then format "0%" bHex to:fStream<br>		else format "%" bHex to:fStream<br><br>		format "ff," to:fStream<br>		<br>		count += 1<br>		<br>		if count >= 32 do<br>		(<br>			format "\n" to:fStream<br>			count = 0<br>		)<br>	)<br>)<br>	<br><br>
    Beeing picky, that's how i'd put it tho, storing the values in variables so you don't check your array several times per loop.
    I have to say editing code and text is horrible on Polycount.

  • MrQuetch
    Offline / Send Message
    MrQuetch polycounter lvl 6
    Noors said:
    Use the toUpper built-in function
    toUpper (bit.intAsHex(aPixelColor[1]))) to:fStream <br>--small tips to make code clearer (or shorter to navigate into), if you just have one line of code in a loop, you can write on one line:
    if (aPixelColor[1] < 16) then format "0%" (toUpper(bit.intAsHex(aPixelColor[1]))) to:fStream
    else format "%" (<code>toUpper(bit.intAsHex(aPixelColor[1]))) to:fStream

    format "0%" (
    for i = 0 to (aHeight-1) do<br>(<br>	aRow = getPixels aBitmap [0, i] aWidth<br><br>	for j = 1 to aWidth do<br>	(<br><br>		r = aRow[j].red<br>		rHex = toUpper (bit.intAsHex(r))<br>			<br>		g = aRow[j].green<br>		gHex = toUpper (bit.intAsHex(g))<br>			<br>		b = aRow[j].blue<br>		bHex = toUpper (bit.intAsHex(b))<br>		<br>		format "0x" to:fStream<br>		<br>		if r < 16 then format "0%" rHex to:fStream<br>		else format "%" rHex to:fStream<br>			<br>		if g < 16 then format "0%" gHex to:fStream<br>		else format "%" gHex to:fStream<br>			<br>		if b < 16 then format "0%" bHex to:fStream<br>		else format "%" bHex to:fStream<br><br>		format "ff," to:fStream<br>		<br>		count += 1<br>		<br>		if count >= 32 do<br>		(<br>			format "\n" to:fStream<br>			count = 0<br>		)<br>	)<br>)<br>	<br><br>
    that's how i'd put it tho, storing the values in variables so you don't check your array several times per loop.
    I have to say editing code and text is horrible on Polycount.

    Hey again, Noors.

    Those functions will definitely come in handy. I just thought of it now, but I should also check if the "count" is greater than or equal to the width of the image, then go to the next line. As it is, the code is best for 32x32 sizes. You're awesome! Thanks again for your work; it really means a lot to me. 
  • Noors
    Offline / Send Message
    Noors greentooth
    You're welcome.
    Yeah i wasn't sure what you were doing with the counter.
    But i guess you don't need it if you put your last format outside the j loop.
    Then there will be a line break at the end of each row, no matter the width of the bitmap.


    <code> format "\n" to:fStream<br>)</code><code>for i = 0 to (aHeight-1) do<br>(<br> aRow = getPixels aBitmap [0, i] aWidth<br><br> for j = 1 to aWidth do<br> (<br><br> r = aRow[j].red<br> rHex = toUpper (bit.intAsHex(r))<br> <br> g = aRow[j].green<br> gHex = toUpper (bit.intAsHex(g))<br> <br> b = aRow[j].blue<br> bHex = toUpper (bit.intAsHex(b))<br> <br> format "0x" to:fStream<br> <br> if r < 16 then format "0%" rHex to:fStream<br> else format "%" rHex to:fStream<br> <br> if g < 16 then format "0%" gHex to:fStream<br> else format "%" gHex to:fStream<br> <br> if b < 16 then format "0%" bHex to:fStream<br> else format "%" bHex to:fStream<br><br> format "ff," to:fStream<br> )<br>




Sign In or Register to comment.