User:Brian Schott/code/phototile

From J Wiki
Jump to navigation Jump to search

Photo Tiling


    <"0 a.{~97+i. 2 4

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'

the kiosk would produce the following truncations, giving all of the middle frame, but only half of the other two,

   ;:'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'

   ;:'bcde hijk nopq'

   ;:'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
   aspect =. aspect,1
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
       1,%ratio                         NB. Increase long side by

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
Vine blooms closeup m4x6 50.jpg
versus 8x10:
Vine blooms closeup m8x10 38.jpg
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