thejeffersonkettle
thejeffersonkettle

Reputation: 11

How to create averaged rgb vectors from image pixel array?

I have an image that I'd like to break up into 30px by 30px blocks. Then, I'd like to average each of the r,g, and b values of the pixels. So for example, a 900px by 900px would breaken up into 900 blocks (each 30px by 30px). Then I'd like to take the average of the r,g, and b values in each block. In the end, I'd like an array of 900 three-dimensional vectors, each representing the average r,g,b value of their respective block.

I've tried using numpy and pillow to break up the blocks, but I don't seem to be splicing my pixel array correctly.

import numpy as np
from PIL import Image

item_image = Image.open("1.jpg")
pixel_array = np.asarray(item_image)
width, height = item_image.size
blocks = []

BLOCK_WIDTH = 30
BLOCK_HEIGHT = 30

row_start = 0
row_end = BLOCK_HEIGHT
column_start = 0
column_end = BLOCK_WIDTH
print(len(pixel_array))
while row_end < height:
  while column_end < width:
    row = slice(row_start, row_end)
    col = slice(column_start, column_end)
    blocks.append(pixel_array[ row , col ])
    column_start += BLOCK_WIDTH
    column_end += BLOCK_WIDTH
  row_start += BLOCK_HEIGHT
  row_end += BLOCK_HEIGHT

averaged_blocks = []

for i in range(BLOCK_HEIGHT):
  for j in range(BLOCK_WIDTH):
    averaged_blocks.append(np.mean(blocks[i][j], axis = 0))


I'm pretty new at working with images, so if anyone has any recommendations or suggestions I'd greatly appreciate it!

Upvotes: 1

Views: 316

Answers (4)

fmw42
fmw42

Reputation: 53164

This is pretty trivial in Imagemagick. All you need to do is scale the image down to 30x30 pixels. Scale averages the blocks of 30x30 non-overlapping pixels. (Or use -filter box -resize in place of -scale). Then just list the resulting image's pixel values using txt: output format.

Create 900x900 pixel horizontal gradient:

convert -size 900x900 gradient: -rotate 90 grad.png


enter image description here

Scale the image and list each pixel:

 convert grad.png -scale 30x30 txt:

# ImageMagick pixel enumeration: 30,30,65535,gray

coords:  (16-bit rgb values) hex values   percent

0,0: (1057,0,64478)  #04210000FBDE  srgb(2%,0%,98%)
1,0: (3244,0,62291)  #0CAC0000F353  srgb(5%,0%,95%)
2,0: (5431,0,60104)  #15370000EAC8  srgb(8%,0%,92%)
3,0: (7618,0,57917)  #1DC20000E23D  srgb(12%,0%,88%)
4,0: (9805,0,55730)  #264D0000D9B2  srgb(15%,0%,85%)
5,0: (11992,0,53543)  #2ED80000D127  srgb(18%,0%,82%)
6,0: (14179,0,51356)  #37630000C89C  srgb(22%,0%,78%)
7,0: (16366,0,49169)  #3FEE0000C011  srgb(25%,0%,75%)
8,0: (18552,0,46983)  #48780000B787  srgb(28%,0%,72%)
9,0: (20739,0,44796)  #51030000AEFC  srgb(32%,0%,68%)
10,0: (22926,0,42609)  #598E0000A671  srgb(35%,0%,65%)
11,0: (25113,0,40422)  #621900009DE6  srgb(38%,0%,62%)
12,0: (27300,0,38235)  #6AA40000955B  srgb(42%,0%,58%)
13,0: (29487,0,36048)  #732F00008CD0  srgb(45%,0%,55%)
14,0: (31674,0,33861)  #7BBA00008445  srgb(48%,0%,52%)
15,0: (33861,0,31674)  #844500007BBA  srgb(52%,0%,48%)
16,0: (36048,0,29487)  #8CD00000732F  srgb(55%,0%,45%)
17,0: (38235,0,27300)  #955B00006AA4  srgb(58%,0%,42%)
18,0: (40422,0,25113)  #9DE600006219  srgb(62%,0%,38%)
19,0: (42609,0,22926)  #A6710000598E  srgb(65%,0%,35%)
20,0: (44796,0,20739)  #AEFC00005103  srgb(68%,0%,32%)
21,0: (46983,0,18552)  #B78700004878  srgb(72%,0%,28%)
22,0: (49169,0,16366)  #C01100003FEE  srgb(75%,0%,25%)
23,0: (51356,0,14179)  #C89C00003763  srgb(78%,0%,22%)
24,0: (53543,0,11992)  #D12700002ED8  srgb(82%,0%,18%)
25,0: (55730,0,9805)  #D9B20000264D  srgb(85%,0%,15%)
26,0: (57917,0,7618)  #E23D00001DC2  srgb(88%,0%,12%)
27,0: (60104,0,5431)  #EAC800001537  srgb(92%,0%,8%)
28,0: (62291,0,3244)  #F35300000CAC  srgb(95%,0%,5%)
29,0: (64478,0,1057)  #FBDE00000421  srgb(98%,0%,2%)
0,1: (1057,0,64478)  #04210000FBDE  srgb(2%,0%,98%)
1,1: (3244,0,62291)  #0CAC0000F353  srgb(5%,0%,95%)
2,1: (5431,0,60104)  #15370000EAC8  srgb(8%,0%,92%)
3,1: (7618,0,57917)  #1DC20000E23D  srgb(12%,0%,88%)
4,1: (9805,0,55730)  #264D0000D9B2  srgb(15%,0%,85%)
5,1: (11992,0,53543)  #2ED80000D127  srgb(18%,0%,82%)
6,1: (14179,0,51356)  #37630000C89C  srgb(22%,0%,78%)
7,1: (16366,0,49169)  #3FEE0000C011  srgb(25%,0%,75%)
8,1: (18552,0,46983)  #48780000B787  srgb(28%,0%,72%)
9,1: (20739,0,44796)  #51030000AEFC  srgb(32%,0%,68%)
10,1: (22926,0,42609)  #598E0000A671  srgb(35%,0%,65%)
11,1: (25113,0,40422)  #621900009DE6  srgb(38%,0%,62%)
12,1: (27300,0,38235)  #6AA40000955B  srgb(42%,0%,58%)
13,1: (29487,0,36048)  #732F00008CD0  srgb(45%,0%,55%)
14,1: (31674,0,33861)  #7BBA00008445  srgb(48%,0%,52%)
15,1: (33861,0,31674)  #844500007BBA  srgb(52%,0%,48%)
16,1: (36048,0,29487)  #8CD00000732F  srgb(55%,0%,45%)
17,1: (38235,0,27300)  #955B00006AA4  srgb(58%,0%,42%)
18,1: (40422,0,25113)  #9DE600006219  srgb(62%,0%,38%)
19,1: (42609,0,22926)  #A6710000598E  srgb(65%,0%,35%)
20,1: (44796,0,20739)  #AEFC00005103  srgb(68%,0%,32%)
21,1: (46983,0,18552)  #B78700004878  srgb(72%,0%,28%)
22,1: (49169,0,16366)  #C01100003FEE  srgb(75%,0%,25%)
23,1: (51356,0,14179)  #C89C00003763  srgb(78%,0%,22%)
24,1: (53543,0,11992)  #D12700002ED8  srgb(82%,0%,18%)
25,1: (55730,0,9805)  #D9B20000264D  srgb(85%,0%,15%)
26,1: (57917,0,7618)  #E23D00001DC2  srgb(88%,0%,12%)
27,1: (60104,0,5431)  #EAC800001537  srgb(92%,0%,8%)
28,1: (62291,0,3244)  #F35300000CAC  srgb(95%,0%,5%)
29,1: (64478,0,1057)  #FBDE00000421  srgb(98%,0%,2%)
.
.
.
0,28: (1057,0,64478)  #04210000FBDE  srgb(2%,0%,98%)
1,28: (3244,0,62291)  #0CAC0000F353  srgb(5%,0%,95%)
2,28: (5431,0,60104)  #15370000EAC8  srgb(8%,0%,92%)
3,28: (7618,0,57917)  #1DC20000E23D  srgb(12%,0%,88%)
4,28: (9805,0,55730)  #264D0000D9B2  srgb(15%,0%,85%)
5,28: (11992,0,53543)  #2ED80000D127  srgb(18%,0%,82%)
6,28: (14179,0,51356)  #37630000C89C  srgb(22%,0%,78%)
7,28: (16366,0,49169)  #3FEE0000C011  srgb(25%,0%,75%)
8,28: (18552,0,46983)  #48780000B787  srgb(28%,0%,72%)
9,28: (20739,0,44796)  #51030000AEFC  srgb(32%,0%,68%)
10,28: (22926,0,42609)  #598E0000A671  srgb(35%,0%,65%)
11,28: (25113,0,40422)  #621900009DE6  srgb(38%,0%,62%)
12,28: (27300,0,38235)  #6AA40000955B  srgb(42%,0%,58%)
13,28: (29487,0,36048)  #732F00008CD0  srgb(45%,0%,55%)
14,28: (31674,0,33861)  #7BBA00008445  srgb(48%,0%,52%)
15,28: (33861,0,31674)  #844500007BBA  srgb(52%,0%,48%)
16,28: (36048,0,29487)  #8CD00000732F  srgb(55%,0%,45%)
17,28: (38235,0,27300)  #955B00006AA4  srgb(58%,0%,42%)
18,28: (40422,0,25113)  #9DE600006219  srgb(62%,0%,38%)
19,28: (42609,0,22926)  #A6710000598E  srgb(65%,0%,35%)
20,28: (44796,0,20739)  #AEFC00005103  srgb(68%,0%,32%)
21,28: (46983,0,18552)  #B78700004878  srgb(72%,0%,28%)
22,28: (49169,0,16366)  #C01100003FEE  srgb(75%,0%,25%)
23,28: (51356,0,14179)  #C89C00003763  srgb(78%,0%,22%)
24,28: (53543,0,11992)  #D12700002ED8  srgb(82%,0%,18%)
25,28: (55730,0,9805)  #D9B20000264D  srgb(85%,0%,15%)
26,28: (57917,0,7618)  #E23D00001DC2  srgb(88%,0%,12%)
27,28: (60104,0,5431)  #EAC800001537  srgb(92%,0%,8%)
28,28: (62291,0,3244)  #F35300000CAC  srgb(95%,0%,5%)
29,28: (64478,0,1057)  #FBDE00000421  srgb(98%,0%,2%)
0,29: (1057,0,64478)  #04210000FBDE  srgb(2%,0%,98%)
1,29: (3244,0,62291)  #0CAC0000F353  srgb(5%,0%,95%)
2,29: (5431,0,60104)  #15370000EAC8  srgb(8%,0%,92%)
3,29: (7618,0,57917)  #1DC20000E23D  srgb(12%,0%,88%)
4,29: (9805,0,55730)  #264D0000D9B2  srgb(15%,0%,85%)
5,29: (11992,0,53543)  #2ED80000D127  srgb(18%,0%,82%)
6,29: (14179,0,51356)  #37630000C89C  srgb(22%,0%,78%)
7,29: (16366,0,49169)  #3FEE0000C011  srgb(25%,0%,75%)
8,29: (18552,0,46983)  #48780000B787  srgb(28%,0%,72%)
9,29: (20739,0,44796)  #51030000AEFC  srgb(32%,0%,68%)
10,29: (22926,0,42609)  #598E0000A671  srgb(35%,0%,65%)
11,29: (25113,0,40422)  #621900009DE6  srgb(38%,0%,62%)
12,29: (27300,0,38235)  #6AA40000955B  srgb(42%,0%,58%)
13,29: (29487,0,36048)  #732F00008CD0  srgb(45%,0%,55%)
14,29: (31674,0,33861)  #7BBA00008445  srgb(48%,0%,52%)
15,29: (33861,0,31674)  #844500007BBA  srgb(52%,0%,48%)
16,29: (36048,0,29487)  #8CD00000732F  srgb(55%,0%,45%)
17,29: (38235,0,27300)  #955B00006AA4  srgb(58%,0%,42%)
18,29: (40422,0,25113)  #9DE600006219  srgb(62%,0%,38%)
19,29: (42609,0,22926)  #A6710000598E  srgb(65%,0%,35%)
20,29: (44796,0,20739)  #AEFC00005103  srgb(68%,0%,32%)
21,29: (46983,0,18552)  #B78700004878  srgb(72%,0%,28%)
22,29: (49169,0,16366)  #C01100003FEE  srgb(75%,0%,25%)
23,29: (51356,0,14179)  #C89C00003763  srgb(78%,0%,22%)
24,29: (53543,0,11992)  #D12700002ED8  srgb(82%,0%,18%)
25,29: (55730,0,9805)  #D9B20000264D  srgb(85%,0%,15%)
26,29: (57917,0,7618)  #E23D00001DC2  srgb(88%,0%,12%)
27,29: (60104,0,5431)  #EAC800001537  srgb(92%,0%,8%)
28,29: (62291,0,3244)  #F35300000CAC  srgb(95%,0%,5%)
29,29: (64478,0,1057)  #FBDE00000421  srgb(98%,0%,2%)

Upvotes: 1

Mark Setchell
Mark Setchell

Reputation: 207818

In general you want to avoid copying large images around, or chunks of images because it is slow. It is often preferable to process as you go, rather than build a new data structure to process later.

So, with that in mind:

#!/usr/bin/env python3

from PIL import Image
import numpy as np

BLKSIZE=30

# Load image into Numpy array
im = np.array(Image.open('image.png').convert('RGB'))

for y in range(0, 900, BLKSIZE):
    for x in range(0, 900, BLKSIZE):
        view = im[y:y+BLKSIZE, x:x+BLKSIZE]
        Rmean = np.mean(view[...,0]) 
        Gmean = np.mean(view[...,1]) 
        Bmean = np.mean(view[...,2]) 
        print(f'Block {y},{x}, R:{Rmean:.2f}, G:{Gmean:.2f}, B:{Bmean:.2f}') 

Sample Output - using Fred's image (@fmw42)

Block 0,0, R:4.13, G:4.13, B:4.13
Block 0,30, R:12.60, G:12.60, B:12.60
Block 0,60, R:21.13, G:21.13, B:21.13
Block 0,90, R:29.63, G:29.63, B:29.63
Block 0,120, R:38.13, G:38.13, B:38.13
Block 0,150, R:46.70, G:46.70, B:46.70
Block 0,180, R:55.13, G:55.13, B:55.13
Block 0,210, R:63.73, G:63.73, B:63.73
Block 0,240, R:72.13, G:72.13, B:72.13
Block 0,270, R:80.73, G:80.73, B:80.73
Block 0,300, R:89.17, G:89.17, B:89.17
Block 0,330, R:97.73, G:97.73, B:97.73
Block 0,360, R:106.23, G:106.23, B:106.23
Block 0,390, R:114.73, G:114.73, B:114.73
Block 0,420, R:123.27, G:123.27, B:123.27
Block 0,450, R:131.73, G:131.73, B:131.73
Block 0,480, R:140.27, G:140.27, B:140.27
Block 0,510, R:148.77, G:148.77, B:148.77
Block 0,540, R:157.27, G:157.27, B:157.27
Block 0,570, R:165.83, G:165.83, B:165.83
Block 0,600, R:174.27, G:174.27, B:174.27
Block 0,630, R:182.87, G:182.87, B:182.87
Block 0,660, R:191.27, G:191.27, B:191.27
Block 0,690, R:199.87, G:199.87, B:199.87
Block 0,720, R:208.30, G:208.30, B:208.30
Block 0,750, R:216.87, G:216.87, B:216.87
Block 0,780, R:225.37, G:225.37, B:225.37
Block 0,810, R:233.87, G:233.87, B:233.87
Block 0,840, R:242.40, G:242.40, B:242.40
Block 0,870, R:250.87, G:250.87, B:250.87
Block 30,0, R:4.13, G:4.13, B:4.13
Block 30,30, R:12.60, G:12.60, B:12.60
Block 30,60, R:21.13, G:21.13, B:21.13
...
...
Block 870,750, R:216.87, G:216.87, B:216.87
Block 870,780, R:225.37, G:225.37, B:225.37
Block 870,810, R:233.87, G:233.87, B:233.87
Block 870,840, R:242.40, G:242.40, B:242.40
Block 870,870, R:250.87, G:250.87, B:250.87

Upvotes: 1

Mark Setchell
Mark Setchell

Reputation: 207818

As you say you are open to suggestions, you could achieve what you ask in the shell with one line of ImageMagick which is installed on most Linux distros and is available for macOS and Windows.

So, in shell or Command Prompt, take an image, split it into 30x30 tiles, average contents of each tile by resizing to 1x1, append all 1x1 means to a new single row image and print each pixel of that image when represented as 8-bit in text format:

magick image.png -crop 30x30 -resize 1x1\! +append -depth 8 txt:

Sample Output

# ImageMagick pixel enumeration: 900,1,65535,rgb
0,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
1,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
2,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
3,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
4,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
5,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
6,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
7,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
8,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
9,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
10,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
11,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
12,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
13,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
14,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
15,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
16,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
17,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
18,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
19,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
20,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
21,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
22,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
23,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
24,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
25,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
26,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
27,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
28,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
29,0: (82.2696,63142.9,82.2696)  #00F600  rgb(0,246,0)
30,0: (254.322,58412.4,254.322)  #01E301  rgb(1,227,1)
31,0: (254.322,58412.4,254.322)  #01E301  rgb(1,227,1)
32,0: (254.322,58412.4,254.322)  #01E301  rgb(1,227,1)

Upvotes: 1

venkata krishnan
venkata krishnan

Reputation: 2046

There is some logical fault in the row_start and row_end variables. You dont need to use 2 variables for start and end.

Please make use of the below code and replace it in place of the while loop.

blocks=[]
while(row_start < height):
col_start = 0
    while(col_start < width):
        patch = pixel_array[row_start:row_start+30,col_start:col_start+30]
        #print(patch.shape)
        blocks.append(patch)
        col_start+=30
        #print(row_start,col_start)
row_start+=30

Now the blocks array will have all the 900 patches of size 30,30,3. I have hard coded the value 30, to extract the patch. Change the value to make it dynamic according to the use case.

Upvotes: 0

Related Questions