User:Brian Schott/code/phototile
Photo Tiling
<"0 a.{~97+i. 2 4 +-+-+-+-+ |a|b|c|d| +-+-+-+-+ |e|f|g|h| +-+-+-+-+
The *output* boxed array above suggests the positions of 8 video frames placed as tiles on a single 4x6" photo print (the j input expression is a distraction). The purpose of this script pictfframes.ijs is to place multiple video frames taken from a video camera onto a single picture in a manner that tiles frames. (A digital camera with a fast enough "burst" mode could be used to generate the frames, also.)
Most consumer (ie, non-professional) video cameras produce 640x480 pixel frames when the camera is held in the normal, landscape, orientation. Serendipitously, if video pictures are taken with such a camera held in portrait orientation and if 8 frames are placed in 2 rows and 4 columns on a 4x6" photo paper, the tiled frames are automatically resized and fit perfectly on the paper when printed at drugstore photo kiosks. The required tiling is accomplished by the verb frame which employes the verb frames. frame requires as inputs a filename and an array of frame indices within that file. The scripts which extract the video avi files, and which create still jpg files, are those supplied by Cliff and Zach Reiter in the media/image3 addons package and documented in the associated labs and help files.
If the serendipitous tiling is not accomplished for your desired combination of paper size, pixel dimensions and frames array, then my experience suggests that the photo kiosks rescale the entire input file so that the smaller of the two dimensions exactly fills the corresponding paper dimension and the other dimension is cropped to fit evenly along the other paper dimension. For example if I wanted 1 row and 3 columns of frames like the following example (which would be restricted by its width in the horizontal dimension),
;:'abcdef ghijkl mnopqr' +------+------+------+ |abcdef|ghijkl|mnopqr| +------+------+------+
the kiosk would produce the following truncations, giving all of the middle frame, but only half of the other two,
;:'def ghijkl mno' +---+------+---+ |def|ghijkl|mno| +---+------+---+
so I elected to produce one of the following 3 simplifications, depending on the value of a "bias" parameter (the parameter is n in the conjunction resize: _1 <: n <: 1). Whereas there are only 3 examples here, the parameter produces many more gradations.
;:'abcd ghij mnop' +----+----+----+ |abcd|ghij|mnop| +----+----+----+ ;:'bcde hijk nopq' +----+----+----+ |bcde|hijk|nopq| +----+----+----+ ;:'cdef ijkl opqr' +----+----+----+ |cdef|ijkl|opqr| +----+----+----+
NB. pictfframes.ijs NB. 7/23/7 NB. based on the image3.ijs addon of Reiter and Reiter NB. many thanks for the extensive help they have provided load jpath 'addons/media/image3/prevare.ijs' load jpath 'addons/media/image3/view_m.ijs' NB. a valid filename `fn` must be supplied fsize fn NB. size of avi file avi_l fn NB. number of frames in video NB. These scripts assume the movie camera is rotated 90 degrees NB. ccw when the pictures are taken. ccw =: |.@ (1 0 2&|:) pixels =: 2&{.@$@(> @{.^:2) NB. compute shape of first frame columns =: {.@|.@$@,: NB. # of columns negcol =: -@columns arrayshape =: _2&({.!.1)@$ NB. "shape" of x, even for scalars frames =: 13 :'<"_2 (negcol x) ,"_3\ (,x) ccw@read_avi"0 1 y' frame =: 13 :',/ ,"2/@:>"1 x frames y' frame =: [: ,/ [: ,"2/@:>"1 frames stitch =: ,/@:(,"2/@:>"1) NB. stitch together resized frames resize =: conjunction define : pix =. pixels y aspect =. x:(%~/m)%%/"1 pix *"1 arrayshape x shift =. n if. aspect > 1 do. aspect =. 1,%aspect NB. Devon's approach else. aspect =. aspect,1 end. cut =. -. aspect trim =. cut * 1r2&+@-: shift (<.pix*cut * 1r2&+@-:shift),:pix-<.cut*pix ) Note 'Demo' fn =: '/Users/brian/Movies/DSCF0466.AVI' NB. verb frame works for 2x4 panes on 4x6 paper taking 3x4 aspect shots NB. notice that reflex (~) reverses the the right and left arguments, x & y NB. y is the file name NB. x is the frames array from the avi file NB. This first subsection really only works for 2x4 panes on 4x6" paper $B =: fn frame~ 9+_4]\}.10*i.9 view_image B B write_image '/Users/brian/Movies/DSCF0466.jpg' NB. conj resize works for other panes on 4x6 paper taking 3x4 aspect shots NB. conj resize *may* work on other paper and other aspect shots NB. notice that reflex (~) separates n from the right argument which is x NB. y is the file name NB. x is the frames array from the avi file NB. m is the paper size: width by height NB. _1 <: n <: 1 and can produce a biased crop of reshaped frames for n~:0 NB. This next subsection's *revise* attempts to be more efficient NB. by working with the indices of only one, archetypal bit array NB. and not with the exact content of the frames. NB. K is used only as the x of ;.0 and in my usage is not often NB. recomputed for a set of prints because C and F only supply NB. their structure to *resize*, not their content. The NB. resize conjunction's n parameter may need adjustment from NB. print to print. C =: fn frames~ F =: 19 20 21 ]K =: C 6 4 resize 0 ~ F view_image B=: stitch (<K)>;.0 each <"1 each C B write_image '/Users/brian/Movies/DSCF0466.jpg' )
In the resizing above, the image is cropped;
in the resizing below, the image is rescaled.
A Different Way to Resize
Not knowing what Brian was up to, I approached the photo kiosk problem independently and came up with a different solution. I seemed to have problems getting my prints from my local drugstore; I suspected that the non-standard size .JPGs I was giving them might be the reason. I usually work on my pictures - selecting, cropping, enhancing them - before I'm ready to show them. In fact, I usually don't print them; in fact, I prefer to display them on a screen because of the greater possible range of color and brightness.
Anyway, sometimes I still need a print to show someone, so I wrote the following code to resize arbitrary .JPGs to an arbitrary ratio, e.g. standard sizes like 4x6 or 8x10.
NB.* matPicFile: put pictures on file into "mat"->standard shape ratio. matPicFile=: 4 : 0 'dd flnm'=. pathFileBreak y pic=. read_image_ima3_ dd,flnm pic=. x matPic pic whsuf=. flnm i: '.' newsuf=. '_',;'mx',&.>":&.>x newnm=. (whsuf}.flnm),~newsuf,~whsuf{.flnm pic write_image_ima3_ dd,newnm NB.EG (<4 6) matPicFile&.>(<dd),&.>fls NB. Make 4x6 compatible NB.EG 4 6 matPicFile 'C:\amisc\pix\Art\miscSel\Stuy Town Palisade.jpg' ) NB.* matPic: fit picture y into ratio x by matting->borders to extend short dim. matPic=: 4 : 0 tsz=. x NB. Target "size" (use ratio) sz=. 2{.$y NB. Size of picture. np=. _1{$y NB. number of planes mult=. tsz sideResizeNum sz newsz=. (/:sz){<.0.5+mult*(<./,>./)sz NB. Mult short, long sides->orig alignctr=. -<.-:newsz-sz NB. order. 255-(alignctr,0)|.(newsz,np){.255-y NB. Overtake and center; double "not" NB. to fill with whitespace (255) instead of 0. ) NB.* sideResizeNum: set up short, long side multiplier vec. sideResizeNum=: 4 : 0 ratio=. ((<./%>./)x)%(<./%>./)y NB. Ratio of target to actual ratio if. ratio>1 do. NB. Increase short side by ratio,1 else. 1,%ratio NB. Increase long side by end. )
The comment "NB.EG" at the end of the top-level function matPicFile gives an example of how to use it. I also rely on Cliff Reiter's image addon.
Here's what a photo looks like if it's matted 6x4
versus 8x10:
As you can see, the white borders are on different pairs of sides depending on how the
ratio matches the original dimensions of the photo.
-- Devon McCormick
Contributed by Brian Schott