#πŸ”’ nested loop alternative

469 messages Β· Page 1 of 1 (latest)

mild kayak
#
step: int = ...

for x in range(0, 3267, step):
  for y in range(0, 4969, step):
    ...

Can I somehow not make this a nested loop?

patent flameBOT
#

@mild kayak

Python help channel opened

Remember to:

  • Ask your Python question, not if you can ask or if there's an expert who can help.
  • Show a code sample as text (rather than a screenshot) and the error message, if you've got one.
  • Explain what you expect to happen and what actually happens.

:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.

mild kayak
#
x_s, y_s = img.size # (3267, 4969)

step: int = ...

for x in range(0, x_s, step):
  for y in range(0, y_s, step):
    ...

Can I somehow not make this a nested loop?

#

my first thought was itertools.product but that wouldn't necessarily work.

polar meteor
#

Why do you want to?

mild kayak
#

?

#

for speed

polar meteor
#

Why do you want to not make it nested?

mild kayak
#

you can imagine how slow it is

polar meteor
#

For speed? Why on earth would a nested loop be slower and an equivalent construct?

mild kayak
#

well u wouldn't be looping twice if u can do it in one step?

polar meteor
#

It has to do x*y amount of work. Anything you replace it with also has to do that.

mild kayak
#

uhm

#

oh?

junior thicket
polar meteor
#

If you needed nested loops, you need that many steps. Doing it with 1 loop or 2 doesn't change that.

mild kayak
#

so uhm then how do I speed this up

polar meteor
#

Purplys' query is the relevant one. What's the loop for?

mild kayak
#
R, G, B = 0, 0, 0

            surrounding_pixels: list[tuple[int, int]] = [
                (-1,-1), (0,-1), (+1,-1),
                (-1, 0), (0, 0), (+1, 0),
                (-1,+1), (0,+1), (+1,+1)
            ]

            depth: int = 3

            for d in range(depth):
                for x_o, y_o in surrounding_pixels:
                    x_o = (x_o - d) if (x_o < 0) else (x_o + d)
                    y_o = (y_o - d) if (y_o < 0) else (y_o + d)

                    x_o += x
                    y_o += y

                    x_o = 0 if x_o < 0 else x_o
                    x_o = x_s if x_o > x_s else x_o

                    y_o = 0 if y_o < 0 else y_o
                    y_o = y_s if y_o > y_s else y_o

                    try:
                        r, g, b = img.getpixel((x_o,y_o))
                    except IndexError:
                        pass
                    except Exception as e:
                        raise(e)
                    
                    D: int = d + 1

                    R += r * (1 / (D * 9))
                    G += g * (1 / (D * 9))
                    B += b * (1 / (D * 9))

            pixels[x, y] = (int(R), int(G), int(B))

this is what I'm doing inside the nested loop.

#

it basically blurs an image

#

by combining the 3x3 pixels into 1 big pixel

#

I gotta figure out a better way to deal with the depth

#

which is just a way to make 3x3 become like 6x6 9x9

junior thicket
#

so a box filter?

mild kayak
#

for bigger images

#

ig?

#

probably a variable box would be best to constant blur

#

look?

#

like make sure no matter what size an image is the blur boxes would be same sizd

#

size*

junior thicket
mild kayak
#

says page not found

#

ah nvm

#

u had 0

#

in the end

junior thicket
mild kayak
#

oh but I'm not doing it because I want to blur an image its because I want to implement it myself

#

I thought u linked an algorithm of some sort but its just a library that does what Im looking todo

junior thicket
mild kayak
#

hm

#

numpy is a big library that I have never really tried to learn

#

in this context what features would numpy have that'd help me?

junior thicket
mild kayak
# elfin basalt it should i think
for y, x in product(range(y_s), repeat=2):
  print(y, x)

>>> (0, 0)
>>> (0, 1)
>>> ...
>>> (0, 4969)
>>> ...
>>> (4949, 4949)

however x in this context maxes out at 3267

junior thicket
mild kayak
#

oh?

#

I thought the whole product thing was the repeat arg

junior thicket
#

you're still doing something max_x * max_y times though
doesn't matter if you have a flat loop instead of a nested one

mild kayak
#

yeah

#

it gives me a bit more screen room tho so I think Im gonna keep product

#
for d in range(depth):
                for x_o, y_o in surrounding_pixels:
                    x_o = (x_o - d) if (x_o < 0) else (x_o + d)
                    y_o = (y_o - d) if (y_o < 0) else (y_o + d)

                    x_o += x
                    y_o += y

                    x_o = 0 if x_o < 0 else x_o
                    x_o = x_s if x_o > x_s else x_o

                    y_o = 0 if y_o < 0 else y_o
                    y_o = y_s if y_o > y_s else y_o

                    try:
                        r, g, b = img.getpixel((x_o,y_o))
                    except IndexError:
                        pass
                    except Exception as e:
                        raise(e)
                    
                    D: int = d + 1

                    R += r * (1 / (D * 9))
                    G += g * (1 / (D * 9))
                    B += b * (1 / (D * 9))

this is really what I have to fix to make it faster

polar meteor
#

But Felix can be quicker, even in Python. Think about the blur step - you're taking surrounding pixels and adding them. The cost of picking them up, per-pixel, is O(dx*dy) where dx and dy are the size of the blur rectangle. You might make some headway fetching them just once and adding to the target array in several places.

mild kayak
#

the first loop is basically for x in width for y in height

#

which ig can't be made faster

polar meteor
#

Your img - is it a Pillow Image? Or just some grid of pixels of your own?

mild kayak
#

pillow

polar meteor
#

Well a Pillow image is an array underneath. Packed binary data.

mild kayak
#
        new_image = Image.new(mode="RGB", size=img.size)
        pixels = new_image.load()

        """
            (x,y)
            [-1,-1] [0,-1] [+1,-1]
            [-1, 0] [0 ,0] [+1, 0]
            [-1,+1] [0,+1] [+1,+1]
        """

        x_s, y_s = img.size
polar meteor
#

You can use numpy to do bulk adds at machine speed.

junior thicket
mild kayak
#

oh?

polar meteor
#

It isn't "less" work in terms of compute steps, but the steps are faster.

mild kayak
#

how do I do that?

polar meteor
#

Numpy works with the same arrays as Python. You can take the array from the Image and make a numpy array from it directly (not even copying the data - just sharing the array).

#

You can compute your blurred image by adding these arrays together, each slightly offset.

mild kayak
#

could I utilize numpy's matrix multiplication stuff to do the blurring step?

junior thicket
polar meteor
#

Sorry, I wrote "Python" above - i meant Pillow.

mild kayak
polar meteor
mild kayak
#

so multiply the surrounding pixels matrix by a matrix of depth * 9

polar meteor
#

Well, i thought you were effectively adding the image to itself, offset slightly (this adds the adjacent pixels).

#

Then divide at the end.

junior thicket
mild kayak
#

aha

#

I actually remember that

#

it is a convolution

#

because this whole idea came after I watched an episode of 3 blue 1 brown

#

and the video was about convolution

#

πŸ˜…

#

so numpy probably has convolution stuff then ha

#

numpy.convole

polar meteor
junior thicket
mild kayak
#

why would I need a 2d solution?

junior thicket
junior thicket
mild kayak
#

couldn't I just use the 1d convolution for R then G then B

junior thicket
mild kayak
#

isn't every image a 2d image πŸ˜…

#

not sure I understand what 2d image means

junior thicket
mild kayak
#

oh yes

#

my idea was

#

hmm

#

wait...

#

😭

#

my head

#

so my idea was

#

oh it is

#

since the list contains tuples

#

so it isnt really a 1d array

#

hm.

#

so right now I am going thru the surrounding pixels one by one and adding their RGB values

#

what if I do R, then G, then B and do all the surrounding pixels at once

#

wouldn't that be faster?

junior thicket
mild kayak
#

I mean this

#

nvm

#

my logic doesn't work

junior thicket
mild kayak
#

that name scares me

#

what is that 😭

junior thicket
mild kayak
#

not really tbh

#

I mean I know what a sliding window is

#

in real life

#

not in code

#

πŸ˜…

#

so it makes a group of values that can be gotten and placed back?

junior thicket
# mild kayak not really tbh

for ease of explanation, let's work on 1d

[ 1 2 3 4 5 6 7 ]
```then you can make 3-lengthed windows(not an actual term) by 'sliding' a window across the array like this
```py
[|1 2 3|4 5 6 7 ]
[ 1|2 3 4|5 6 7 ]
[ 1 2|3 4 5|6 7 ]
...
[ 1 2 3 4|5 6 7|]
#  to obtain
[1 2 3]
[2 3 4]
[3 4 5]
...
[5 6 7]
mild kayak
#

oh so u can grab a chunk of data from the array

#

but then can u place it back where it was with modified values?

#

I mean ig I can just create a new image

junior thicket
#

for 2d, say you had an 3x5 image and a 3x3 box filter. then you can make sliding windows of 3x3 by

|. . .|. .
|. . .|. .
|. . .|. .
 
 .|. . .|.
 .|. . .|.
 .|. . .|.
 
 . .|. . .|
 . .|. . .|
 . .|. . .|
```and you can element-wise multiply the box filter with these windows to get the blurred image
mild kayak
#

aha...

#

ok so this makes sense

#

I see why it'd be faster

#

wait but

#

oh yeah

#

wait no

#

waht...

#

so I got this 3x3 chunk of data

#

which is the rgb values of the pixels or is it the x, y positions of the pixels?

#

wait...

#

instead of looping over the x, y of the image size

#

why dont I create an array of rgb of the whole image

#

and loop over that

#

so [(rgb), (rgb), (rgb), ...]

junior thicket
# mild kayak and loop over that

because you're still looping in python
the whole point of numpy & friends' bjillion operations is so that you don't loop in python

mild kayak
#

aha

#

python loops that bad ha

#

so using numpy I wouldn't need a loop at all then

#

right?

junior thicket
mild kayak
#

okay so the million dollar question

#

how do I do this 😭

junior thicket
# mild kayak how do I do this 😭

if you just want to use the blahblah.sliding_window_view, it's not that difficult

>>> import numpy as np
>>> from numpy.lib.stride_tricks import sliding_window_view
>>> a = np.arange(16).reshape(4, 4)
>>> a
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])
>>> sliding_window_view(a, (3, 3))
array([[[[ 0,  1,  2],
         [ 4,  5,  6],
         [ 8,  9, 10]],

        [[ 1,  2,  3],
         [ 5,  6,  7],
         [ 9, 10, 11]]],


       [[[ 4,  5,  6],
         [ 8,  9, 10],
         [12, 13, 14]],

        [[ 5,  6,  7],
         [ 9, 10, 11],
         [13, 14, 15]]]])
mild kayak
#

and that'd help me how?....

#

oh

#

wait

#

shit

#

...

#
img_np_array = ...

sliding_window_view(img_np_array, (3,3))
#

so that gives me the pixel with its 8 surrounding pixels

#

correct?

junior thicket
#

you get 3x3 chunks of the original, correct

mild kayak
#

then I need to do a convolution of that chunk with the offsets to blur the pixel

#

right?

#

and then just reshape it to match what it first was and put it back into the image

junior thicket
mild kayak
#

hm

#

stupid real python keeps asking for an account

junior thicket
#

you should be able to just a = np.array(image)

mild kayak
junior thicket
# mild kayak elaborate please
>>> kernel = np.ones((3, 3)) / 9
>>> kernel   # your 3x3 box filter
array([[0.11111111, 0.11111111, 0.11111111],
       [0.11111111, 0.11111111, 0.11111111],
       [0.11111111, 0.11111111, 0.11111111]])
>>> swv = sliding_window_view(a, (3, 3))
>>> np.multiply(swv, kernel)  # element-wise multiply
array([[[[0.        , 0.11111111, 0.22222222],
         [0.44444444, 0.55555556, 0.66666667],
         [0.88888889, 1.        , 1.11111111]],

        [[0.11111111, 0.22222222, 0.33333333],
         [0.55555556, 0.66666667, 0.77777778],
         [1.        , 1.11111111, 1.22222222]]],


       [[[0.44444444, 0.55555556, 0.66666667],
         [0.88888889, 1.        , 1.11111111],
         [1.33333333, 1.44444444, 1.55555556]],

        [[0.55555556, 0.66666667, 0.77777778],
         [1.        , 1.11111111, 1.22222222],
         [1.44444444, 1.55555556, 1.66666667]]]])
>>> b = np.multiply(swv, kernel)
>>> b.sum(axis=(-1, -2))  # sum each chunk together to get the averaged value; remeber we already divided by 9 because of the elem-wise kernel multiplication earlier
array([[ 5.,  6.],
       [ 9., 10.]])
mild kayak
#

ok..

#

why is the axis -1, -2 here?

junior thicket
# mild kayak why is the axis `-1, -2` here?

if you read the brackets carefully, the sliding_window_view gave us an array of array of 3x3 chunks
so the last 2 axes (-1, -2) combined are the 3x3 chunks we have (that we want to sum)

mild kayak
#

oh ic

#

ok and now with the b array I just create a new image right?

#

well not b

#

but the combination of all b arrays

#

new_image.extend(b)

#

wait

#

that'd require a loop nvm

#

im stupid

#

its alr done

#

b is the new pixels

#

lmao

#
with Image.open("1.jpg") as img:
    pixels = array(img)

    pixel_chunks = sliding_window_view(pixels, (3,3))

    box_filter = ones((3,3)) / 9

    blurred_pixel_chunks = multiply(pixels, box_filter)

    new_image = Image.new(mode="RGB", size=img.size)

    new_pixels = new_image.load()

    new_pixels = blurred_pixel_chunks.sum(axis=(-1,-2))

    new_image.show()
#

so this should be it correct?

#

is the new image gonna be the same size?

#

no right?

#

this code is not complete yet

junior thicket
# mild kayak so this should be it correct?

probably
the example I showed is like a grayscale image, while you have an RGB image, meaning you'll have 3 2d matrices indicating the r g b values
if it doesn't work and you can't be bothered, you can technically just loop over the r g b (it's only 3 so it's not that bad)

junior thicket
mild kayak
#
    pixel_chunks = sliding_window_view(pixels, (3,3))
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
    raise ValueError(f'Since axis is `None`, must provide '
ValueError: Since axis is `None`, must provide window_shape for all dimensions of `x`; got 2 window_shape elements and `x.ndim` is 3.
junior thicket
#

(image width, image length, and channel=3 for rgb)

mild kayak
#
[[[177 187 189]
  [179 189 191]
  [182 192 194]
  ...
  [226 229 234]
  [229 232 237]
  [227 230 235]]

 [[173 183 185]
  [176 186 188]
  [176 186 188]
  ...
  [229 232 237]
  [228 231 236]
  [228 231 236]]

 [[177 187 189]
  [175 185 187]
  [172 182 184]
  ...
  [229 232 237]
  [226 229 234]
  [228 231 236]]

 ...

 [[ 75  52  46]
  [ 73  49  45]
  [ 86  62  58]
  ...
  [  8   0   0]
  [  9   1   0]
  [  9   1   0]]

 [[ 85  60  56]
  [ 81  56  52]
  [ 84  60  56]
  ...
  [  9   0   1]
  [ 10   2   0]
  [ 10   2   0]]

 [[ 88  64  60]
  [ 80  59  56]
  [ 81  60  57]
  ...
  [ 13   3   2]
  [  4   0   0]
  [  2   0   1]]]
#

yeah thats pixels

#

its a 3d image

#

I mean array

#

so ur saying loop thru it

junior thicket
#

well loop through the channels, then you're working with 'normal' 2d arrays again
then at the end you should have 3 2d arrays representing the blurred r, g, b channels, then merge them back together

mild kayak
#

icic

#

ugh

#
with Image.open("1.jpg") as img:
    pixels = array(img)

    new_image = Image.new(mode="RGB", size=img.size)
    new_pixels = new_image.load()

    blurred_pixels = []

    for kernel in pixels:
        kernel_chunks = sliding_window_view(kernel, (3,3))

        box_filter = ones((3,3)) / 9

        blurred_kernel_chunks = multiply(kernel, box_filter)

        blurred_pixels.append(blurred_kernel_chunks.sum(axis=(-1,-2)))

    new_pixels = blurred_pixels

    new_image.show()
#

am I in the right track?

#
    blurred_kernel_chunks = multiply(kernel, box_filter)
                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: operands could not be broadcast together with shapes (3267,3) (3,3) 

uhmmm.....

mild kayak
junior thicket
mild kayak
#
[
  [ # <-- row
    [0, 0, 0], # <-- column
  ],
  [ # <-- x
    [0, 0, 0], # <-- x, y
  ]
]
#

it doesnt make sense

#

whys it like that/

#

?

#

OH

#

its row by row

junior thicket
mild kayak
mild kayak
#

its y, x, somethingf

#

oh rgb

#

lol

junior thicket
mild kayak
#

3 values per pixel

#

yeah its basically a list of rows

junior thicket
#

you can np.moveaxis the 3 to the front, then you can loop and it should work as intended

mild kayak
#

that contains lists of pixels in that row

#

that contain a 3 valued list with the rgb

#

why its not a tuple idk

#

ig so its immutable?

mild kayak
#
    blurred_kernel_chunks = multiply(kernel, box_filter)
                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: operands could not be broadcast together with shapes (4969,3267) (3,3) 
#
with Image.open("1.jpg") as img:
    stupid_pixels = array(img)

    pixels = moveaxis(stupid_pixels, -1, 0)

    new_image = Image.new(mode="RGB", size=img.size)
    new_pixels = new_image.load()

    blurred_pixels = []

    for kernel in pixels:
        kernel_chunks = sliding_window_view(kernel, (3,3))

        box_filter = ones((3,3)) / 9

        blurred_kernel_chunks = multiply(kernel, box_filter)

        blurred_pixels.append(blurred_kernel_chunks.sum(axis=(-1,-2)))

    new_pixels = blurred_pixels

    new_image.show()
#

(4969, 3267, 3)
(3, 4969, 3267)
junior thicket
mild kayak
#

thats one beautiful image πŸ˜…

junior thicket
#

tho assigning to new_pixels would definitely not affect new_image

mild kayak
#
pixels[x, y] = (int(R), int(G), int(B))
#

thats all I did before

#

pixels is not just a list

#

its this PyAccess thing

polar meteor
mild kayak
#

I was wondering if such thing existed

#

yeah this is better

polar meteor
#

to go back to a Pillow Image from your numpy image array

junior thicket
#

like whatever you're doing, I'm 99% certain that new_pixels = blurred_pixels definitely won't change anything about new_image
maybe try making an image from the array instead? yeah like cameron pointed to

mild kayak
#
    new_image = Image.fromarray(blurred_pixels, "RGB")
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/PIL/Image.py", line 3266, in fromarray
    arr = obj.__array_interface__
          ^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'list' object has no attribute '__array_interface__'
#

do I reshape it?

#

I mean moveaxis

#

back

polar meteor
#

Note this caveat paragraph there: "In the case of NumPy, be aware that Pillow modes do not always correspond to NumPy dtypes. Pillow modes only offer 1-bit pixels, 8-bit pixels, 32-bit signed integer pixels, and 32-bit floating point pixels."

#

I think your blurred_pixels isn't a numpy array but a plain list?

mild kayak
#

it is.

#

very interesting

polar meteor
#

Both Image and a numpy array manipulate a packed binary array of values, which is how they get to shovel their data back and forth so easily.

mild kayak
#

so I need blurred_pixels to be a numpy array

polar meteor
#

I thought you were doing the blurring using numpy anyway. I've been elsewhere.

mild kayak
#
blurred_pixels.append(blurred_kernel_chunk.sum(axis=(-1,-2)))

whats the numpy equivelant to this?

mild kayak
# polar meteor I thought you were doing the blurring using numpy anyway. I've been elsewhere.
with Image.open("1.jpg") as img:
    stupid_pixels = array(img)

    pixels = moveaxis(stupid_pixels, -1, 0)

    blurred_pixels = empty(pixels.shape)

    for kernel in pixels:
        kernel_chunk = sliding_window_view(kernel, (3,3))

        box_filter = ones((3,3)) / 9

        blurred_kernel_chunk = multiply(kernel_chunk, box_filter)

        blurred_pixels.append(blurred_kernel_chunk.sum(axis=(-1,-2)))

    blurred_pixels = moveaxis(blurred_pixels, -1, 0)

    new_image = Image.fromarray(blurred_pixels, "RGB")

    print("Time:", (time() - tick), "s")

    new_image.show()

this is where we're at rn.

#

+= ?

junior thicket
junior thicket
mild kayak
#

I did

#

blurred_pixels = empty(pixels.shape)

#

thats numpy.empty

#

it should match the size

#

or do I do zeros

#

since I need something to refil the empty corners anyways

polar meteor
#

Somehow blurred_pixels is becoming not a numpy array. Put in some prints and see if you can see what it's type is print(type(blurred_pixels))

#

You can make a bigger image by padding the borders with zeroes. There are numpy methods for that kind of thing.

mild kayak
#

wdym

#

it is

#

still a ndarray

#

I just need to figure out how to build it again

polar meteor
#

Then Image.fromarray ought to work.
Check its type just before calling that to be sure.

mild kayak
#

what?

#

im confused

#

so blurred_pixels doesn't need to be an ndarray ?

junior thicket
mild kayak
#

oh im so stupid

junior thicket
#

and remember to move the color channel axis to where it should be

mild kayak
mild kayak
#

nvm

#

wit

#

nvm

#

same result

junior thicket
mild kayak
#

not right

#
(4967, 3265, 3)
Time: 6.728874921798706 s
junior thicket
#

what's the code now?

mild kayak
#
with Image.open("1.jpg") as img:
    stupid_pixels = array(img)

    pixels = moveaxis(stupid_pixels, -1, 0)

    blurred_kernels = []

    for kernel in pixels:
        kernel_chunk = sliding_window_view(kernel, (3,3))

        box_filter = ones((3,3)) / 9

        blurred_kernel_chunk = multiply(kernel_chunk, box_filter)

        blurred_kernels.append(blurred_kernel_chunk.sum(axis=(-1,-2)))

    blurred_pixels = array(blurred_kernels)
    blurred_pixels = moveaxis(blurred_pixels, 0, -1)
    
    print(blurred_pixels.shape)

    new_image = Image.fromarray(blurred_pixels, "RGB")

    print("Time:", (time() - tick), "s")

    new_image.show()
#
(4969, 3267, 3)
(3, 4969, 3267)
(4967, 3265, 3)
Time: 6.69256329536438 s
#

im somehow losing 2 pixels at the end...?

#
[[[1.76333333e+02 1.86333333e+02 1.88333333e+02]
  [1.76666667e+02 1.86666667e+02 1.88666667e+02]
  [1.76444444e+02 1.86444444e+02 1.88444444e+02]
  ...
  [2.27222222e+02 2.30888889e+02 2.34555556e+02]
  [2.27444444e+02 2.30777778e+02 2.35111111e+02]
  [2.27777778e+02 2.30777778e+02 2.35777778e+02]]

 [[1.75333333e+02 1.85666667e+02 1.87666667e+02]
  [1.75777778e+02 1.86111111e+02 1.88111111e+02]
  [1.76111111e+02 1.86444444e+02 1.88444444e+02]
  ...
  [2.27555556e+02 2.31333333e+02 2.34777778e+02]
  [2.26888889e+02 2.30444444e+02 2.34333333e+02]
  [2.26777778e+02 2.30111111e+02 2.34444444e+02]]

 [[1.75333333e+02 1.86000000e+02 1.88000000e+02]
  [1.75777778e+02 1.86444444e+02 1.88444444e+02]
  [1.76666667e+02 1.87333333e+02 1.89333333e+02]
  ...
  [2.28000000e+02 2.31888889e+02 2.35111111e+02]
  [2.26444444e+02 2.30222222e+02 2.33666667e+02]
  [2.25555556e+02 2.29222222e+02 2.32888889e+02]]

 ...

 [[7.51111111e+01 5.16666667e+01 4.57777778e+01]
  [7.93333333e+01 5.53333333e+01 5.02222222e+01]
  [8.30000000e+01 5.86666667e+01 5.42222222e+01]
  ...
  [9.77777778e+00 8.88888889e-01 2.22222222e-01]
  [9.66666667e+00 1.22222222e+00 0.00000000e+00]
  [9.44444444e+00 1.44444444e+00 0.00000000e+00]]

 [[7.86666667e+01 5.46666667e+01 4.98888889e+01]
  [8.18888889e+01 5.76666667e+01 5.33333333e+01]
  [8.50000000e+01 6.10000000e+01 5.67777778e+01]
  ...
  [9.66666667e+00 6.66666667e-01 3.33333333e-01]
  [9.55555556e+00 1.00000000e+00 1.11111111e-01]
  [9.33333333e+00 1.22222222e+00 1.11111111e-01]]
...
#

this is what the image ends up becoming btw

#

I feel like this is wrong...?

#
[[[177 187 189]
  [179 189 191]
  [182 192 194]
  ...
  [226 229 234]
  [229 232 237]
  [227 230 235]]

 [[173 183 185]
  [176 186 188]
  [176 186 188]
  ...
  [229 232 237]
  [228 231 236]
  [228 231 236]]

 [[177 187 189]
  [175 185 187]
  [172 182 184]
  ...
  [229 232 237]
  [226 229 234]
  [228 231 236]]

 ...

 [[ 75  52  46]
  [ 73  49  45]
  [ 86  62  58]
  ...
  [  8   0   0]
  [  9   1   0]
  [  9   1   0]]

 [[ 85  60  56]
  [ 81  56  52]
  [ 84  60  56]
  ...
  [  9   0   1]
  [ 10   2   0]
  [ 10   2   0]]

 [[ 88  64  60]
  [ 80  59  56]
  [ 81  60  57]
  ...
  [ 13   3   2]
  [  4   0   0]
  [  2   0   1]]]
#

I mean compared to this

#

wait what if it needs to be integers not floats

junior thicket
#

it do be looking jank
probably bugs

mild kayak
#

I was talking abt how low the values are btw

#

it doesn't make sense

mild kayak
#

I'd be lying if I said I understand how half of this works

junior thicket
#
blurred_kernels.append(blurred_kernel_chunk.sum(axis=(-1,-2), dtype=np.uint8))
#                                                             ^^^^^^^^^^^^^^
mild kayak
#

I mean it worked....

#

not really blurred tho πŸ˜…

#

I thought this would happen

#

3x3 blur filter is too small

#
with Image.open("1.jpg") as img:
    stupid_pixels = array(img)

    print(stupid_pixels.shape)

    pixels = moveaxis(stupid_pixels, -1, 0)

    print(pixels.shape)

    blurred_kernels = []

    box_filter_size = (3,3)

    for kernel in pixels:
        kernel_chunk = sliding_window_view(kernel, window_shape=box_filter_size)

        s1, s2 = box_filter_size

        box_filter = ones(shape=box_filter_size) / (s1 * s2)

        blurred_kernel_chunk = multiply(kernel_chunk, box_filter)

        blurred_kernels.append(blurred_kernel_chunk.sum(axis=(-1,-2), dtype=uint8))

    blurred_pixels = array(blurred_kernels)
    blurred_pixels = moveaxis(blurred_pixels, 0, -1)
    
    print(blurred_pixels.shape)

    new_image = Image.fromarray(blurred_pixels, "RGB")

    print("Time:", (time() - tick), "s")

    new_image.show()
#

I alr implemented this

junior thicket
mild kayak
#

imma try

#

before we fixed the data type

#

zsh would kill it cuz it ran too long

#

6 was max I could do

junior thicket
#

also some stuff I didn't think about cause I'm dumb:

  • you can use np.mean instead of np.sum then you don't need to divide by 9 on the kernel
  • np.multiply is just * (I thought it was matmul but then that's @ lol)
mild kayak
#

btw I captured that picture myself

#

what do u think?

junior thicket
mild kayak
#

5x5

junior thicket
mild kayak
#

side by side

#

its not rly blurring it rather than kinda messing with the colors

#

or am I not seeing it right

junior thicket
mild kayak
#

this is using mean

#

instead of sum

#

also I took out the diving by filter size square

#

lmao what did I just create

junior thicket
# mild kayak this is using mean

I mean it's definitely blurring for me

with Image.open("...") as img:
    stupid_pixels = np.array(img)
    pixels = np.moveaxis(stupid_pixels, -1, 0)
    blurred = []

    for mat in pixels:
        chunks = sliding_window_view(mat, (3, 3))
        blurred.append(chunks.mean(axis=(-1, -2)))

    blurred = np.array(blurred).astype(np.uint8)
    blurred = np.moveaxis(blurred, 0, -1)
    new_image = Image.fromarray(blurred, "RGB")

    print("Time:", (time() - tick), "s")

    new_image.show()
mild kayak
#

thats the * dunder right?

junior thicket
mild kayak
#

is @ a math term or programming term?

#

like is it the math symbol for matmul?

junior thicket
mild kayak
#

wth

junior thicket
junior thicket
junior thicket
mild kayak
#

3x3 vs 9x9

#

barely any diff

#

shit needs like 200

#

imma do 69

#

prob gonna crash

#

lol

junior thicket
# mild kayak

how big is your image lol
you might run into some performance issues if you increase the kernel size again

mild kayak
#

that was my error

#

when it didnt work

mild kayak
#

its 3567 by 4969

#

I think

junior thicket
#

beeg parrot

mild kayak
#

its a 4k image 😭

#

yeah...

#

well 69x69 is still running

#

hasnt crashed or slowed down my pc

#

soooo

#

im hopeful

#

πŸ˜…

junior thicket
#

cause you technically don't need all of those windows to do convolution

mild kayak
#

aha...

#

so how do we implement that?

#

πŸ’€

#

there we go

#

69

#

the magic number

#

took 240 seconds

mild kayak
junior thicket
mild kayak
#

what do u mean /handjob?

junior thicket
mild kayak
#

lmfa

#

well the mission rn is

#
with Image.open("1.jpg") as img:
    stupid_pixels = array(img)
    pixels = moveaxis(stupid_pixels, -1, 0)
    blurred = []
    
    filter_size = (69, 69)

    for mat in pixels:
        chunks = sliding_window_view(mat, filter_size)
        blurred.append(chunks.mean(axis=(-1, -2)))

    blurred = array(blurred).astype(uint8)
    blurred = moveaxis(blurred, 0, -1)
    new_image = Image.fromarray(blurred, "RGB")

    print("Time:", (time() - tick), "s")

    new_image.show()
#

how we take this

#

from 240s to like 5s

#

5s prob too extreme

#

30s

junior thicket
#

honestly just slap that convolve2d and you should get there
if you want to pure numpy then well... good luck cause I don't know either

#

ig I can explain the idea

mild kayak
#

afterall its just a math lib

#

its not blurring

#

where do I slap that bitch?

junior thicket
mild kayak
#

how do people do that thing where ur terminal shows like a loading thing

#

or like something that shows u that its still running or like progress updates

#

for the script

#

without slowing shit down

#

cuz its boring just looking at a still terminal hoping something will pop up

#

lol

#

im guessing the logging lib?

junior thicket
# junior thicket ig I can explain the idea

as for why it can run so much faster, imagine you have a 1d array again and a 10-lengthed window

[|1 2 3 ... 10|11 12 ... ]
[ 1|2 3 ... 10 11|12 ... ]
[ 1 2|3 ... 10 11 12|... ]
```notice that the the window only differs from each other by the head & tail element, i.e the first window is `1~10`, the second is `2~11`; the two differs by `-1+11`
so instead of re-calculating each window every time, you can just change the current window by 2 values
specialized algos will do this so they run faster than stride tricks
mild kayak
#

yeah

#

makes sense

#

I noticed that alr b4

#

I mean so far its not looking like its gonna do better than 240s 😭

junior thicket
mild kayak
#

ugh

#

still runnning?

#

wth

#

its been like 5m

#

is this ever gonna end?

#

imma stop it and do 3x3 to see just how bad it is

#

ok 3s

#

wth?

#

ok

#

fftconvolve

#

25,25 in 2 seconds

#

looking good

#

there we go

#

69x69 in 3s

#

but now getting this weird black border

#

@junior thicket

#

vinegtte

junior thicket
mild kayak
#

1000x1000 in 4.5s

#

damnn

junior thicket
#

scipy.signal also says there's a oaconvolve which might be even faster

#

but honestly just try stuff out and see what works

mild kayak
#

mode same

#

mode valid

#

mode full

#

its litearlly just cropping in

mild kayak
# junior thicket [`scipy.signal`](https://docs.scipy.org/doc/scipy/reference/signal.html) also sa...
methodstr {β€˜auto’, β€˜direct’, β€˜fft’}, optional
A string indicating which method to use to calculate the convolution.

direct
The convolution is determined directly from sums, the definition of convolution.

fft
The Fourier Transform is used to perform the convolution by calling fftconvolve.

auto
Automatically chooses direct or Fourier method based on an estimate of which is faster (default). See Notes for more detail.
#

their regular convolve

#

just chooses which is gonna be fastest

#

oaconvolve 4.3

#

concolve auto 4.7

#

fftconvolve 4.5

#

if I wanted a variable filter size where it is determined by the size of an image how do I do that

#

like so each image has the same amount of blurring

#

not rly sure what the math would be for that

#

@junior thicket

patent flameBOT
#
Python help channel closed

This help channel has been closed and it's no longer possible to send messages here. If your question wasn't answered, feel free to create a new post in #1035199133436354600. To maximize your chances of getting a response, check out this guide on asking good questions.