Overview
Given an image in plain PPM (P3) format as input, for each pixel p in the image, replace each of the following 4 pixels' red, green, and blue with the floored average value of the respective channels of all 4 pixels:
pitselfThe pixel located at
p's location when the image is flipped verticallyThe pixel located at
p's location when the image is flipped horizontallyThe pixel located at
p's location when the image is flipped both vertically and horizontally
Output the resulting image in plain PPM (P3) format.
For further explanation, consider this 8x8 image, magnified to 128x128:
step 2 example
Let p be the red pixel. To calculate the new value for p (and the 3 blue pixels), the values of p and the 3 blue pixels will be averaged together:
p1 = (255, 0, 0)
p2 = (0, 0, 255)
p3 = (0, 0, 255)
p4 = (0, 0, 255)
p_result = (63, 0, 191)
Examples
Reference Implementation
#!/usr/bin/python
import sys
from itertools import *
def grouper(iterable, n, fillvalue=None):
args = [iter(iterable)] * n
return list(izip_longest(*args, fillvalue=fillvalue))
def flatten(lst):
return sum(([x] if not isinstance(x, list) else flatten(x) for x in lst), [])
def pnm_to_bin(p):
w,h = map(int,p[1].split(' '))
data = map(int, ' '.join(p[3:]).replace('\n', ' ').split())
bin = []
lines = grouper(data, w*3)
for line in lines:
data = []
for rgb in grouper(line, 3):
data.append(list(rgb))
bin.append(data)
return bin
def bin_to_pnm(b):
pnm = 'P3 {} {} 255 '.format(len(b[0]), len(b))
b = flatten(b)
pnm += ' '.join(map(str, b))
return pnm
def imageblender(img):
h = len(img)
w = len(img[0])
for y in range(w):
for x in range(h):
for i in range(3):
val = (img[x][y][i] + img[x][~y][i] + img[~x][y][i] + img[~x][~y][i])//4
img[x][y][i],img[x][~y][i],img[~x][y][i],img[~x][~y][i] = (val,)*4
return img
def main(fname):
bin = pnm_to_bin(open(fname).read().split('\n'))
bin = imageblender(bin)
return bin_to_pnm(bin)
if __name__ == '__main__':
print main(sys.argv[1])
This program takes a single filename as input, formatted like the output of pngtopnm <pngfile> -plain, and outputs a single line of PPM data separated by spaces.
A Brief Description of the P3 Format
A PPM plaintext file generated from pngtopnm <pngfile> -plain will look like this:
P3
<width in pixels> <height in pixels>
<maximum value as defined by the bit depth, always 255 for our purposes>
<leftmost 24 pixels of row 1, in RGB triples, space-separated; like (0 0 0 1 1 1 ...)>
<next 24 pixels of row 1>
<...>
<rightmost (up to) 24 pixels of row 1>
<leftmost 24 pixels of row 2>
<next 24 pixels of row 2>
<...>
<rightmost (up to) 24 pixels of row 2>
<...>
This is the format that the example input and output files use. However, PNM is very loose about its formatting - any whitespace may separate values. You could replace all newlines in the above file with a single space each, and still have a valid file. For example, this file and this file are both valid, and represent the same image. The only other requirements are that the file must end with a trailing newline, and there must be width*height RGB triplets following the 255.
Rules
- This is code-golf, so the shortest valid solution wins.
- You may input and output PPM data formatted in any convenient and consistent manner, so long as it is valid according to the PPM format described above. The only exception is that you must use the plain (P3) format, and not the binary (P6) format.
- You must provide verification that your solution outputs the correct images for the above test images.
- All images will have a bit-depth of 8 bits.
Extra reading: Netpbm format wikipedia page
Testing Snippet (thanks to Calvin's Hobbies for this)
function loadImage(t){if(t.files&&t.files[0]){var i=new FileReader;i.onload=function(t){function i(t){t.attr("width",img.width),t.width=img.width,t.attr("height",img.height),t.height=img.height;var i=t[0].getContext("2d");return i}img=$("<img>").attr("src",t.target.result)[0],ctxIn=i($("#input")),ctxIn.drawImage(img,0,0),ctxOut=i($("#output")),go()},i.readAsDataURL(t.files[0])}}function getPixel(t,i){return ctxIn.getImageData(t,i,1,1).data}function setPixel(t,i,e){ctxOut.fillStyle="rgb("+e[0]+","+e[1]+","+e[2]+")",ctxOut.fillRect(t,i,1,1)}function go(){if(void 0!==ctxOut)for(var t=0;t<img.width;t++)for(var i=0;i<img.height;i++){for(var e=new Array(3),g=getPixel(t,i),a=getPixel(img.width-t-1,i),r=getPixel(t,img.height-i-1),n=getPixel(img.width-t-1,img.height-i-1),h=0;h<e.length;h++)e[h]=Math.floor((g[h]+a[h]+r[h]+n[h])/4);setPixel(t,i,e)}}var img,ctxIn,ctxOut;
* {
font-size: 100%;
font-family: Arial, sans-serif;
}
<script src="http://code.jquery.com/jquery-2.2.0.min.js"></script>
Warning - runs very slowly on large images! 300x300 pixel maximum advised.<br><br>
<canvas id='input'>Your browser doesn't support the HTML5 canvas tag.</canvas><br><br>
Load an image: <input type='file' onchange='loadImage(this)'><br><br>
<canvas id='output'>Your browser doesn't support the HTML5 canvas tag.</canvas>
-
\$\begingroup\$ Are image libraries that open/save ppm files allowed? \$\endgroup\$Calvin's Hobbies– Calvin's Hobbies2016年01月10日 08:38:58 +00:00Commented Jan 10, 2016 at 8:38
-
\$\begingroup\$ @Calvin'sHobbies Yes \$\endgroup\$user45941– user459412016年01月10日 08:40:13 +00:00Commented Jan 10, 2016 at 8:40
-
3\$\begingroup\$ "Flip it, flop it, average it" youtube.com/watch?v=D8K90hX4PrE \$\endgroup\$Luis Mendo– Luis Mendo2016年01月10日 13:29:44 +00:00Commented Jan 10, 2016 at 13:29
-
3\$\begingroup\$ Maybe "Flip it, flop it, mean it"? \$\endgroup\$Conor O'Brien– Conor O'Brien2016年01月10日 17:31:50 +00:00Commented Jan 10, 2016 at 17:31
-
2\$\begingroup\$ @CᴏɴᴏʀO'Bʀɪᴇɴ That sounds like some party anth- oh, wait, what Luis posted. \$\endgroup\$Addison Crump– Addison Crump2016年01月11日 07:43:01 +00:00Commented Jan 11, 2016 at 7:43
6 Answers 6
Bash (+ImageMagick), 64+1 = 65 bytes
C=convert;$C a -flip b;$C a -flop c;$C c -flip d;$C * -average e
Right tool for the job.
Must be run in a directory containing a single file a that contains the PPM data to transform. Since this filename is significant, I've added one byte to the byte count.
PNG thumbnail outputs (not sure why this is necessary because they're all the same anyway, but the question says so, so...):
penguin quintopia peter minibits
Thanks to nneonneo for saving 2 bytes!
-
3\$\begingroup\$ I require the outputs because people have a bad habit of posting solutions without testing them. +1 for
-flop, I really want to be surprised that it is a flag. \$\endgroup\$user45941– user459412016年01月10日 20:37:43 +00:00Commented Jan 10, 2016 at 20:37 -
1\$\begingroup\$ Shave off 2 bytes by using
C=convertand$Cinstead ofalias. \$\endgroup\$nneonneo– nneonneo2016年01月11日 18:05:17 +00:00Commented Jan 11, 2016 at 18:05
Matlab, (削除) 106 (削除ここまで) (削除) 82 (削除ここまで) 80 bytes
i=imread(input(''))/4;for k=1:2;i=i+flipdim(i,k);end;imwrite(i,'p3.pnm','e','A')
The image is loaded as n*m*3 matrix. Then we flip the matrix and added to itself for both axis, and write it again to a file.
I couldn't find a place to upload text files so big, so here are the PNG versions:
-
\$\begingroup\$ Omg, I didn't even know you could use
<imgtags! \$\endgroup\$flawr– flawr2016年01月10日 20:46:21 +00:00Commented Jan 10, 2016 at 20:46 -
1\$\begingroup\$ In MATLAB R2013b and newer it is possible to use flip instead of flipdim. That should save you 3 more bytes. flipdim's help actually says: "flipdim will be removed in a future release. Use FLIP instead." \$\endgroup\$slvrbld– slvrbld2016年01月11日 12:25:33 +00:00Commented Jan 11, 2016 at 12:25
Mathematica, (削除) 86 (削除ここまで) 84 bytes
Thanks to DavidC for the advice. (saves 2 bytes)
Export[#2,⌊Mean@Join[#,(r=Reverse)/@#]&@{#,r/@#}&@Import[#,"Data"]⌋~Image~"Byte"]&
The first and second parameters are the paths to the input and output images, respectively.
Test cases
f=%; (assign the function to symbol f)
f["penguin.pnm","penguin2.pnm"]
f["quintopia.pnm","quintopia2.pnm"]
f["peter.pnm","peter2.pnm"]
Result
(PNG versions of the images are uploaded below)
Import["penguin2.pnm"]
Import["quintopia2.pnm"]
Import["peter2.pnm"]
-
\$\begingroup\$
Join[#,(r=Reverse)/@#]\$\endgroup\$DavidC– DavidC2016年01月10日 10:21:40 +00:00Commented Jan 10, 2016 at 10:21
Pyth, (削除) 30 (削除ここまで) 29 bytes
zjms.OdC.nM[JrR7.zKm_cd3J_J_K
My program expects all metadata on the first line, and the image data row by row on the lines after on stdin. To help, this is a small Python program to convert any valid PPM file into a PPM file my program can understand:
import sys
p3, w, h, d, *data = sys.stdin.read().split()
print(p3, w, h, d)
for i in range(0, int(w) * int(h), int(w)):
print(" ".join(data[i:i+int(w)]))
Once you have the image data row by row the operations are really simple. First I read the image data into a list of lists of integers (JrR7.z), then I create the horizontally mirrored version by grouping every 3 integers and reversing them for every row (Km_cd3J). Then the vertically mirrored versions are simply _J_K, since we can just reverse rows.
I take all those matrices, flatten each of them into an 1d array with .nM, transpose with C to get a list of lists of each of the pixel components, average and truncate to int each of those lists (ms.Od), and finally print joined by newlines j.
Note that my program generates output in a different format (but still valid PPM). The demo images can be viewed in this imgur album.
Julia, 157 bytes
using FileIO
s->(a=load(s);b=deepcopy(a);d=a.data;(n,m)=size(d);for i=1:n,j=1:m b.data[i,j]=mean([d[i,j];d[n-i+1,j];d[i,m-j+1];d[n-i+1,m-j+1]])end;save(s,b))
This is a lambda function that accepts a string containing the full path to a PPM file and overwrites it with the transformed image. To call it, assign it to a variable.
Ungolfed:
using FileIO
function f(s::AbstractString)
# Load the input image
a = load(s)
# Create a copy (overwriting does bad things)
b = deepcopy(a)
# Extract the matrix of RGB triples from the input
d = a.data
# Store the size of the matrix
n, m = size(d)
# Apply the transformation
# Note that we don't floor the mean; this is because the RGB values
# aren't stored as integers, they're fixed point values in [0,1].
# Simply taking the mean produces the desired output.
for i = 1:n, j = 1:m
b.data[i,j] = mean([d[i,j]; d[n-i+1,j]; d[i,m-j+1]; d[n-i+1,m-j+1]])
end
# Overwrite the input
save(s, b)
end
Example outputs:
python 2+PIL, 268
Now I massively use PIL, using image flipping and alpha blending
from PIL import Image
I=Image
B,T=I.blend,I.FLIP_TOP_BOTTOM
a=I.open(raw_input()).convert('RGB')
exec'[email protected]_LEFT_RIGHT);c=a@T);d=b@T)'.replace('@','.transpose(')
x,y=a.size
print'P3',x,y,255
for r,g,b in list(B(B(B(a,b,0.5),c,0.25),d,0.25).getdata()):print r,g,b
Resulting images are available here
-
1\$\begingroup\$ Please include the outputs for the test cases, as required by the rules. \$\endgroup\$user45941– user459412016年01月11日 19:39:02 +00:00Commented Jan 11, 2016 at 19:39