img=Import["https://web.mit.edu/puzzle/www/2019/assets/puzzles/picture_book/image.png"];

bytes=ImageData[img,"Byte"];

The distribution is a bit odd:

ListPlot[Tally[Flatten[bytes]]]

bytes[[1]][[1;;100]]

This is totally text. 0 is too overwhelmingly common to be an E, and it never occurs in doubles except at the very end, so it is padding of some sort.

First/@Position[Partition[Flatten[bytes],2,1],{0,0}]==Range[1450872,Length[Flatten[bytes]]-1]

SortBy[Tally[Flatten[bytes]],Last]//Reverse

It just takes a little inspiration to see “ULYSSES” at the beginning; then it’s obvious that 0 is a space.

knownMapping=Transpose[{{97,127,153,117,117,115,117},Characters@"ulysses"}]

rules:=First@First@#->Map[Last,#]&/@GatherBy[knownMapping,First]

errors:=Select[rules,DeleteDuplicates@#[[2]]{#[[2]][[1]]}&]

contracted:=Dispatch@MapAt[First,rules,{All,2}]

Flatten[bytes]/.contracted/.{0->" ",_?NumericQ->"_"}//StringJoin

Now just continue iteratively, it’s obviously “Ulysses by James Joyce”, etc.

knownMapping=With[{known="ulysses by james joyce"},Transpose[{Flatten[bytes][[;;StringLength[known]]],Characters@known}]]

And it’s clearly just the text of Ulysses.

knownMapping=Transpose[{{97,127,153,117,117,115,117,0,175,153,0,239,108,89,128,117,0,239,85,153,87,128,173,123,136,123,115,127,153,206,100,97,89,112,0,175,97,150,78,0,189,97,127,116,113,141,108,105,0,150,136,89,131,0,186,121,120,89,0,167,124,101,0,173,123,108,113,121,124,115,108,106},Characters@"ulysses by james joycestatelyplump buck mulligan came from the stairhead"}]

gutenberg=Import["https://www.gutenberg.org/files/4300/4300-0.txt"];

Text ends with “I will yes”:

Flatten[bytes][[1450872-20;;1450872]]

Get the text into a form where we can just automatically match it up with the bytes:

text=StringReplace[StringReplace[StringTake[ToUpperCase@gutenberg,First@First@StringPosition[gutenberg,"Stately"];;Last@Last@StringPosition[gutenberg,"I will Yes"]],{"\n"->" ","M.P."->"M P ","’"->" ","É"->"E","À" -> "A","È" -> "E","-"->" ","Î"->"I","C/O"->"C O","..."->" ",ToUpperCase@"œ"->"OE","Ü"->"U",RegularExpression["[^ A-Z]"]->""}],RegularExpression[" +"]->" "];

knownMapping=With[{n=140000},Transpose[{Flatten[bytes][[1;;n]],Characters[StringJoin["ULYSSES BY JAMES JOYCE I ",text]][[1;;n]]}]];

{Flatten[bytes][[130200;;130220]],Characters[StringJoin["ULYSSES BY JAMES JOYCE I ",text]][[130200;;130220]]}

errors

Range[255]/.(Rule@@@knownMapping)/._?NumericQ->"_"//Dynamic

That’s enough for “congratulations on your amazing steganography skills to complete puzzle find all pixels for the letter that begins the city where the author is buried, and form a new image in portrait orientation”. The city is Zurich, so Z.

zs=Join[Select[Rule@@@knownMapping//DeleteDuplicates,#[[2]]=="Z"&],{255->"Z"}]

zBytes=Cases[Flatten@bytes,_?(MemberQ[First/@zs,#]&)];

Length@zBytes//FactorInteger

ArrayPlot@Partition[zBytes,29]