[Closed] Maxscript is forgetting my variables?
Here’s an interesting problem; maxscript seems to be forgetting my variables. And stuttering. Take a look:
Here's the offending code in a "pixel counting" type script. It's a loop intended to grab a color from an HDRI image (procedural coordinates) and compare it to an array of already set pixels. If the incomming pixel is "similar" to an existing one, we can iterate a counter.
-- Grab a pixel from the bitmap at these procedural coordinates.
-- Convert the pixel into a color instead of keeping the array.
pixel = (getPixels thisBitmap [h_leg_length,w_leg_length] 1)[1]
thisLoc = 0
for k = 1 to Colors_and_Counts.count while thisLoc == 0 do
(
format "Subtracting % from %
" pixel (Colors_and_Counts[1][k] as color)
format "Loop iteration # %
" k
Compare = pixel - (Colors_and_Counts[1][k] as color)
if ( (Compare.r > 10 or Compare.r < -10) or \
(Compare.g > 10 or Compare.g < -10) or \
(Compare.b > 10 or Compare.b < -10) )
do thisLoc = k
)
Here's the kicker; have a look at the maxscript output (listener)
Subtracting (color 133.975 107.08 46.3184) from (color 0 0 0)
Loop iteration # 1
Subtracting undefined from (color 0 0 0)
Loop iteration # 1
It's listing [i]pixel [/i]as a valid color at first, but then suddenly lists it as undefined without going through a full iteration of the loop.
The program crashes after it tries to subtract undefined from the color array entry.
What on earth is going on here? :shrug:
Could you post actual working code?
your Colours_and_Counts array doesn’t get initialised, so it’s difficult to debug.
I’m sorry, I thought I had included all relevant code. The whole script is massive, so I won’t be able to post the whole thing. Here is the function in question, along with its declarations.
--THISBITMAP is already defined as a bitmaptex.bitmap
local Colors_and_Counts = #(#([0,0,0]),#(1))
local ThreeHighestValues = #(1,1,1)
local ImageAspectRatio = thisBitmap.width/thisbitmap.height
local w_leg_length = 50
local h_leg_length = 50
local thisLoc = 0
local pixel = (color 120 120 120)
for i = 1 to (thisBitmap.height - h_leg_length) by h_leg_length do
(
-- For each row.
for j = 1 to (thisBitmap.height - w_leg_length) by w_leg_length do
(
-- For each column.
-- Grab a pixel from the bitmap at these procedural coordinates.
pixel = (getPixels thisBitmap [i * h_leg_length, j * w_leg_length] 1)[1]
thisLoc = 0
for k = 1 to Colors_and_Counts.count while thisLoc == 0 do
(
format "Subtracting % from %
" pixel (Colors_and_Counts[1][k] as color)
format "Loop iteration # %
" k
Compare = pixel - (Colors_and_Counts[1][k] as color)
if ( (Compare.r > 10 or Compare.r < -10) or \
(Compare.g > 10 or Compare.g < -10) or \
(Compare.b > 10 or Compare.b < -10) )
do thisLoc = k
)
if thisLoc == 0
then
(
Colors_and_Counts[1] = append Colors_and_Counts[1] p[1] --Append the color
Colors_and_Counts[2] = append Colors_and_Counts[2] 1 --Append a counter
)
else Colors_and_Counts[2][thisLoc] += 1
)
)
-- ...
-- ...
)
The reason you’re getting errors is that you’re over reading the bitmap, you are trying to access pixel 50, 10050 in a 256×256 bitmap.
Your logic is a little goatrope somewhere, if you tell me exactly what you want to do I could help you with your code.
This code segment was set up to take samples of a large bitmap (2048 x 1024) at regular intervals. In this case, it’s every 50 pixels in both axes, stopping short of the edges of the image.
Each sample is then compared to all other samples that have been collected. If the recent sample compares within +/- 10 of any r,g, or b channel of another sample, then it should be tallied in Colors_and_Counts[2]. If it turns out that the sample is unique, it is added to the Colors_and_Counts[1] array so that it will be there for future comparisons.
Overall, the goal is to create a sampler that will arrive at an ordered list of sampled colors by frequency. In the end, it will return those colors (say, 50 most common hues) of a bitmap for later use.
Looks like kind of a case in point about commenting your code. Even maxscripts need comments in the code to explain and clarify what is going on.
just my two cents.
Chris J.
Here is my attempt, it’s pretty fast, approx 1700ms to compute every pixel in a 256×256 image, I’ve commented everything so I hope it helps:
(
-- variable used for code profiling
local ts = timestamp()
-- tiger bitmap that ships with max
thisBitmap = openBitmap "C:/3dsmax8/maps/tiger.bmp"
-- pixel sample intervals
local xInterval = 10
local yInterval = 10
-- maximum difference
local diff = 10
-- bitmap dimensions
local width = thisBitmap.width
local height = thisBitmap.height
-- number of samples
local w = floor (width / xInterval)-1
local h = floor (height / yInterval)-1
-- pixel array, current pixel, pixel index, reference pixel
local pixels, pixel, iRef, ref_pixel
-- colour array, a 1x2 array as opposed to a 2x1
-- this ensures that there is a 1-1 relationship between pixels and counts
local colours = #()
-- step through y
for y = 0 to h do
(
-- get the current pixel row
-- it is faster to get a whole row and sample pixels from there than it is to get individual pixels
pixels = getPixels thisBitmap [0,y*yInterval] width
-- step through x
for x = 0 to w do
(
-- get current pixel
pixel = pixels[x*xInterval+1]
iRef = 0
-- step through colours, stop once a reference has been found
for p = 1 to colours.count where iRef == 0 do
(
-- get reference colour
ref_pixel = colours[p][1]
-- subtract each colour element and check their absolute difference
if ((Abs (ref_pixel.r - pixel.r) < diff) or
(Abs (ref_pixel.g - pixel.g) < diff) or
(Abs (ref_pixel.b - pixel.b) < diff)) then
(
-- set the reference to the current index
iRef = p
)
)
-- if a reference has been found, increment it's count
if iRef > 0 then
colours[iRef][2] += 1
-- if not add it to the array
else
append colours #(pixel, 1)
)
)
-- a little gravy
-- the following sorts the array based on colour frequency
-- another advantage of rhe 1x2 array
function sortColours v1 v2 =
(
if v1[2] > v2[2] then -1
else if v1[2] < v2[2] then 1
else 0
)
-- do the actual sort
qsort colours sortColours
-- Show time taken
format "Time Taken: %
" (timestamp() - ts)
-- print the colours to the listener
for c in colours do format "> %
" c
)
Enjoy
Thanks for your help, Wahooney! This is just what I needed to do.
By the way, Alpine’s subtle comment has got me thinking about code commenting. What I posted earlier is how I comment code for my own use and revision. I see you have more experience in coding and Maxscripting. Do you comment your code like this usually? Would it be wise to do segment-by-segment commenting like you’ve demonstrated?
Thanks again!!
One important thing to remember about pixel access in bitmaps is that, other then most of MAXScript, it is ZERO BASED!
So getting a pixel with coordinates [bitmap_width,bitmap_height] is actually accessing one pixel outside the X and Y range. The loop should always read pixels from 0 to bitmat_height-1 , otherwise undefined colors will be returned.
Of course, once a pixel row is read into an array, the indexing becomes 1-based.
But from reading the initial code it looks like the script might have accessed pixels outside of the bitmap because the counters have not been adapted to 0-based access…
Usually I’m a little more spartan with my commenting, I was liberal now just so you, or someone else that might be reading, could understand exactly what’s going on.
I usually comment about half as much as I did in that post.
Bobo is right, it’s a bit of a mission sometimes to juggle the numbers to work correctly between pixel coords and arrays, but just make a mental note of it and you should be fine.