5
\$\begingroup\$

Input into your program a black and white image. (png, jpg, etc)

Convert the image to a 2D matrix of pixels, where each index is the grayscale value of the image.

Take each index of the matrix and average the 8 pixels around it. Then replace that index with the average.

For example if you had:

120 80  60
20  10  30
40  100 05

If you were finding the average for 10, the result would be 56.875, which is (120,80,60,30,05,100,40,20)/8.

Round to up to the nearest integer (57) and replace the index. The result of this iteration would be

120 80  60
20  57  30
40  100 05

If you are finding the average for a pixel on an edge then only take values that are valid. In the example above if you were finding the average for 20 You would only consider 120, 80, 57, 100, and 40 which is 79.4 rounded to 80.

You may start at any index of the graph but it must be in order. For example you could do

[0][0], [0][1], ... [0][n], then [1][0], [1][1],... [1][n]

Once the averaging is complete, convert the matrix back into a new image.

Display the new image as the output of the program. (Or save it and add it in your solution). The result of your code should be a blurry looking image but much smoother than the original.

Example Input
enter image description here
Example Output
enter image description here

Example Input two
enter image description here
Example Output two
enter image description here

Please provide your code and start and end image results.

Q/A
1Q. If one of the 8 neighbors has already been changed, should the average be calculated based on this new value, or based on the original values? That is, does the order of calculation affect the end result?

1A Yes, You should change each index with new values from any potential indices done before. That is why you need to go left-right, top-bottom etc. In order.

2Q. Could you clarify what is meant by "must be in order"? Does this mean that only raster order/reading order is acceptable? Or does it mean that the order must be the same each time the code is run? Would a spiral be valid? Or a shuffled order that is always the same?

2A. You should go left to right, right to left, top to bottom, or bottom to top. I.E. Do all of the 0th row, do all of the 1st row,... do all of the nth row

\$\endgroup\$
9
  • 1
    \$\begingroup\$ If one of the 8 neighbours has already been changed, should the average be calculated based on this new value, or based on the original values? That is, does the order of calculation affect the end result? \$\endgroup\$ Oct 17, 2016 at 13:29
  • \$\begingroup\$ Yes each value is based on the previous ones. That is why you need to change them going from one direction to another. and not hop around. \$\endgroup\$ Oct 17, 2016 at 13:34
  • \$\begingroup\$ Could you clarify what is meant by "must be in order"? Does this mean that only raster order/reading order is acceptable? Or does it mean that the order must be the same each time the code is run? Would a spiral be valid? Or a shuffled order that is always the same? \$\endgroup\$ Oct 17, 2016 at 13:35
  • \$\begingroup\$ You should go left to right, right to left, top to bottom, or bottom to top. I.E. Do all of the 0th row, do all of the 1st row,... do all of the nth row \$\endgroup\$ Oct 17, 2016 at 13:36
  • \$\begingroup\$ Are we allowed to take the image as an array of integers? \$\endgroup\$
    – miles
    Oct 17, 2016 at 16:00

5 Answers 5

5
\$\begingroup\$

J, 90 82 bytes

[:((=i.@$){"0 1],"+3 3((+/>.@%#)@(_-.~(<<<4){,);._3)_,.~_,._,~_,])&.>/(#<@-#\)@,,<

This iterates over pixels from left-to-right, then top-to-bottom.

You can see the progression of values here.

   |. ((=i.@$){"0 1],"+3 3((+/>.@%#)@(_-.~(<<<4){,);._3)_,.~_,._,~_,])&.>/\. ((#<@-#\)@,,<) 120 80 60 , 20 10 30 ,: 40 100 5
┌──────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬────────┬────────┐
│120  80 60│37  80 60│37  32 60│37  32 24│37  32 24│37  32 24│37  32 24│37  32 24│37 32 24│37 32 24│
│ 20  10 30│20  10 30│20  10 30│20  10 30│44  10 30│44  39 30│44  39 40│44  39 40│44 39 40│44 39 40│
│ 40 100  5│40 100  5│40 100  5│40 100  5│40 100  5│40 100  5│40 100  5│61 100  5│61 38  5│61 38 39│
└──────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴────────┴────────┘

The output for the first sample image is (converting from an array of pixels to a bmp)

sample

\$\endgroup\$
4
  • \$\begingroup\$ Here's code in groovy to change the image into a 2D array (85 bytes, 1 more than you already, jesus.......), {i->(0..i.getWidth()-1).collect{x->(0..i.getHeight()-1).collect{y->i.getRGB(x,y)}}}​...... Ha. I concede, you win before I even start. \$\endgroup\$ Oct 17, 2016 at 17:08
  • \$\begingroup\$ Can you provide the image before and after? \$\endgroup\$ Oct 17, 2016 at 17:22
  • \$\begingroup\$ @jacksonecac Sure, also could you clarify what order (directions to iterate over the pixels) you used for the sample images? \$\endgroup\$
    – miles
    Oct 17, 2016 at 20:07
  • 1
    \$\begingroup\$ @JonathanAllan Thanks, I've added a simple fix using a permutation that shifts the center to the front so it can be dropped \$\endgroup\$
    – miles
    Oct 18, 2016 at 13:28
4
\$\begingroup\$

PHP, 321 bytes

$i=imagecreatefromjpeg($f="96512.jpg");list($w,$h)=getimagesize($f);for($y=$h;$y--;)for($x=$w;$x--;$s=$n=0){for($a=-2;$a++<1;)for($b=-2;$b++<1;)if(false!==($d=imagecolorat($i,$x+$a,$y+$b))){$n++;$s+=imagecolorsforindex($i,$d)[red];}imagesetpixel($i,$x,$y,imagecolorallocate($i,$r=ceil($s/$n),$r,$r));}imagejpeg($i,"b$f");

does the blurring from bottom to top, from right to left.

-2 bytes for a gif or png; could save even more with a shorter file name
and -6 if rounding down was allowed. pity that image processing functions have so long names.
but I´m fine. pretty byte count.

blurred example image

\$\endgroup\$
1
  • \$\begingroup\$ Nice job looks great \$\endgroup\$ Oct 18, 2016 at 11:08
2
\$\begingroup\$

PHP, 70 bytes

imagefilter($i=imagecreatefromjpeg($f="i.jpg"),10,0);imagejpeg($i,$f);

Overwrites the original image file. Run with -r.
-2 bytes if you use a gif or png.


I thought PHP might have a built-in for this, and indeed:
10 stands for IMG_FILTER_SMOOTH; and 0 is the weight parameter.

See the GD library source code: This calls gdImageSmooth which calls gdImageConvolution with the requested matrix. I am not sure what it does at the edges; but overall it does the smoothing from top to bottom, from left to right.

enter image description here

\$\endgroup\$
1
\$\begingroup\$

Ruby 2.3.1, 338 bytes

->w{d,k=16843008,ChunkyPNG::Canvas.from_file(w);(0..k.height-1).each do |y|;(0..k.width-1).each do |x|;t,i,a=0,0,[[x-1,y-1],[x,y-1],[x+1,y-1],[x-1,y],[x+1,y],[x,y+1],[x,y+1],[x+1,y+1]];a.each do |c|; next unless k.include_xy?(c[0],c[1]);t=t+k.[](c[0],c[1]);i=i+1;end;n=t/i;while(n%d)>0 do;n=n+1;end;k.[]=(x,y,n-1);end;end;k.save("w")}
  • Uses oily_png/chunky_png for image handling.
  • Goes left to right, top to bottom doing the averaging.
  • Since chunky uses integer color values, I'm doing a really, really slow up rounding method to get averaged color values to an actual grayscale value.

Example output(that took a very, very long time):

enter image description here

Ungolfed, slightly modded, and commented

require 'oily_png'
l = -> img {
  time_begin = Time.new
  d = 16843008
  k = ChunkyPNG::Canvas.from_file(img)
  # go line by line, pixel by pixel
  (0..k.height-1).each do |y|
    (0..k.width-1).each do |x|
      total, i = 0, 0
      p "Starting color is #{curr} or #{curr.to_s(16)}"
      # set the coords around the pixel we want
      around = [[x-1,y-1], [x, y-1], [x+1, y-1], [x-1,y], [x+1, y], [x,y+1], [x, y+1], [x+1, y+1]]
      # grab the 8 colors around the current pixel
      around.each do |coords|
        # ignore values that are not in bounds
        next unless k.include_xy?(coords[0], coords[1])
        # get a total to average later
        total = total + k.[](coords[0], coords[1])
        # increment
        i = i+1
      end
      # get the average value for the new color
      new_color = total/i
      # since new color will likely not be a grayscale value,
      # round up to the nearest grayscale value.
      # This is unfortunately really slow.
      while (new_color%d) > 0 do
        new_color = new_color+1
      end
      p "New color is now #{new_color-1} or #{(new_color-1).to_s(16)}"
      # set the color and
      # remove one from the color value to get full alpha instead of none.
      k.[]=(x, y, new_color-1)
    end
  end
  # save the complete image
  k.save("#{Time.new}.png")
}
l.($*[0])

Could certainly do things a bit more efficiently, unfortunately I'm not quite how to. There's got to be a more clever way of handling the coordinate management for the 8 surrounding locations.

\$\endgroup\$
1
\$\begingroup\$

Racket 346 bytes

(let((s1 sub1)(a1 add1)(lr list-ref)(ln length))(define(g r c)(if(or(> r(s1(ln l)))(> c(s1(ln(lr l 0))))(< r 0)(< c 0))
#f(lr(lr l r)c)))(define(ng r c)(define h'(-1 0 1))(define k(filter(λ(x)x)(for*/list((i h)(j h)#:unless(= 0 i j))
(g (+ r i)(+ c j)))))(round(/(apply + k)(ln k))))(for/list((i(ln l)))(for/list((j(length (lr l 0))))(ng i j))))

Ungolfed (comments follow ';'):

(define (f l)
  (define (get r c)                       ; to get value at a row,col            
    (if (or (> r (sub1 (length l))) 
            (> c (sub1 (length (list-ref l 0))))
            (< r 0)
            (< c 0))                      ; if row/col number is invalid
        #f (list-ref (list-ref l r) c)))  ; return false else return value
  (define (neighbors r c)                 ; finds average of neighboring values
    (define h '(-1 0 1))
    (define k (filter                     ; filter out false from list
               (lambda(x) x)
               (for*/list ((i h)(j h) #:unless (= 0 i j))
                 (get (+ r i) (+ c j)))))
    (round(/(apply + k)(length k))))      ; find average of values (sum/length of list)
  (for/list ((i (length l)))              ; create a list of lists of average values
    (for/list ((j (length (list-ref l 0))))
      (neighbors i j))
    ))

Testing:

(f (list
    '(120 80 60)
    '(20 10 30)
    '(40 100 5)))

Output:

'((37 48 40) (70 57 51) (43 21 47))
\$\endgroup\$
1
  • \$\begingroup\$ Nice Job! Please provide the image result as well. \$\endgroup\$ Oct 18, 2016 at 17:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.