Подтвердить что ты не робот

Загрузить изображения в Imgur из Mathematica

Вот вызов всем подписчикам тегов mathematica. Пусть гораздо удобнее вставлять изображения в SO-сообщение из Mathematica, создавая imgur uploader.

Как мы можем создать функцию imgur[g_], которая растрирует свой аргумент (убедитесь, что конечный размер не шире, чем ширина сообщений StackOverflow), преобразуйте его в PNG, загрузите в imgur и вернете наклейте MarkDown, например ![Mathematica graphic](http://i.imgur.com/ZENa4.jpg)?

Полезные ссылки:

Мне не удалось адаптировать этот последний метод к загрузке изображения, не экспортируя его сначала в файл.


Предупреждение, используйте с осторожностью! StackOverflow использует отдельную установку imgur, которая сохраняет изображения на неопределенный срок. Если вы используете основной imgur, изображения исчезнут через 6 месяцев, если их никто не увидит. К сожалению, с ноября 2011 года, как представляется, нет официального пути для программной загрузки изображений в StackOverflow.


Обновление: См. ниже решение для загрузки в StackOverflow напрямую.

4b9b3361

Ответ 1

Маленькая птица только что сообщила мне о решении Mathematica по этому вопросу (основная реализация по-прежнему использует JLink, но этот ответ скрывает весь код, связанный с java):

imgur[expr_] := Module[
 {url, key, image, data, xml, imgurUrl},
 url = "http://api.imgur.com/2/upload";
 key = "c07bc3fb59ef878d5e23a0c4972fbb29";
 image = Fold[ExportString, expr, {"PNG", "Base64"}];
 xml = Import[url, 
  "XML", "RequestMethod" -> "POST", 
  "RequestParameters" -> {"key" -> key, "image" -> image}];
 imgurUrl = Cases[xml, XMLElement["original", {}, {string_}] :> string, 
  Infinity][[1]];
 "![Mathematica graphic](" <> imgurUrl <> ")"
]

Это только V8, а опции импорта XML "RequestMethod" и "RequestParameters" являются недокументированными и экспериментальными (и поэтому могут быть изменены).

Ответ 2

Примечание. Получите готовый палитру с этой функциональностью здесь.


Решение Arnoud вызвало у меня возбуждение и нетерпение, поэтому здесь улучшилось. Я не мог этого сделать, не изучая его код. Эта версия кажется несколько более надежной и менее подверженной ошибкам таймаута, но, честно говоря, я вообще не знаю Java, поэтому любые улучшения приветствуются.

Самое главное: эта версия напрямую загружается в stack.imgur.com, поэтому ее можно безопасно использовать в StackOverflow, не опасаясь, что загруженные изображения исчезнут через некоторое время.

Я предоставляю три функции:

  • stackImage загружает выражение, экспортируется как PNG, и возвращает URL
  • stackMarkdown возвращает уценку, готовую к копированию
  • stackCopyMarkdown копирует уценку в буфер обмена

Следующий шаг: создайте кнопку палитры, которая сделает это автоматически для выбранной графики в записной книжке. Усовершенствования кода очень приветствуются.


Needs["JLink`"]


stackImage::httperr = "Server returned respose code: `1`";
stackImage::err = "Server returner error: `1`";

stackImage[g_] :=
 Module[
  {getVal, url, client, method, data, partSource, part, entity, code, 
   response, error, result},

  (* this function attempts to parse the response fro the SO server *)
  getVal[res_, key_String] :=
   With[{k = "var " <> key <> " = "},
    StringTrim[
     [email protected][[email protected][res, StringMatchQ[#, k ~~ ___] &], 
       k ~~ v___ ~~ ";" :> v],
     "'"]
    ];

  data = ExportString[g, "PNG"];

  JavaBlock[
    url = "https://stackoverflow.com/upload/image";
    client = JavaNew["org.apache.commons.httpclient.HttpClient"];
    method = JavaNew["org.apache.commons.httpclient.methods.PostMethod", url];
    partSource = JavaNew["org.apache.commons.httpclient.methods.multipart.ByteArrayPartSource", "mmagraphics.png", MakeJavaObject[data]@toCharArray[]];
    part = JavaNew["org.apache.commons.httpclient.methods.multipart.FilePart", "name", partSource];
    [email protected]["image/png"];
    entity = JavaNew["org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity", {part}, [email protected][]];
    [email protected][entity];
    code = [email protected][method];
    response = [email protected][];
  ]

  If[code =!= 200, Message[stackImage::httperr, code]; Return[$Failed]];
  response = StringTrim /@ StringSplit[response, "\n"];

  error = getVal[response, "error"];
  result = getVal[response, "result"];
  If[StringMatchQ[result, "http*"],
   result,
   Message[stackImage::err, error]; $Failed]
  ]


stackMarkdown[g_] := "![Mathematica graphics](" <> stackImage[g] <> ")"


stackCopyMarkdown[g_] := Module[{nb, markdown},
  markdown = Check[stackMarkdown[g], $Failed];
  If[markdown =!= $Failed,
   nb = NotebookCreate[Visible -> False];
   NotebookWrite[nb, Cell[markdown, "Text"]];
   SelectionMove[nb, All, Notebook];
   FrontEndTokenExecute[nb, "Copy"];
   NotebookClose[nb];
   ]
  ]

Update:

Здесь кнопка, которая покажет предварительный просмотр выбора и предложит загрузку (или отмену). Это требует, чтобы предыдущие функции были определены.

Button["Upload to SO",
 Module[{cell = [email protected][], img},
  If[cell =!= {}, img = Rasterize[cell];
   MessageDialog[
    Column[{"Upload image to StackExchange sites?", 
      img}], {"Upload and copy MarkDown" :> stackCopyMarkdown[img], 
     "Cancel" :> Null}, WindowTitle -> "Upload to StackExchange"]]]]

К сожалению, я не могу поместить кнопку в палитру (CreatePalette), потому что размеры палитры повлияют на растеризацию. Решения этой проблемы приветствуются.

Обновление 2:

Основываясь на ответе на этот вопрос, здесь работает только кнопка палитры только для Windows:

button = Button["Upload to SO",
  Module[{sel},
   FrontEndExecute[
    FrontEndToken[FrontEnd`SelectedNotebook[], "CopySpecial", "MGF"]];
   sel = Cases[[email protected][], 
     RasterBox[data_, ___] :> 
      Image[data, "Byte", ColorSpace -> "RGB", Magnification -> 1], 
     Infinity];
   If[sel =!= {},
    With[{img = First[sel]},
     MessageDialog[
      Column[{"Upload image to StackExchange sites?", 
        img}], {"Upload and copy MarkDown" :> stackCopyMarkdown[img], 
       "Cancel" :> Null}, WindowTitle -> "Upload to StackExchange"]
     ]
    ]
   ]
  ]

CreatePalette[button]

Предупреждение: оно уничтожает содержимое буфера обмена, даже если вы нажмете отменить в окне предварительного просмотра.

Ответ 3

Примечание. Это использование анонимного загрузчика imgur с моим анонимным ключом. Сайт imgur ограничивает загрузку до 50 загрузок/час, что должно быть нормально, но это может вызвать проблему, если многие люди попытаются это сделать одновременно. Поэтому, пожалуйста, получите свой собственный анонимный ключ здесь:

http://imgur.com/register/api_anon

И затем замените ключ в коде ниже вашим собственным ключом (спасибо!).

Самой сложной частью для кода было преобразование из выражения Mathematica в PNG-изображение в кодировку Base64 для кодирования URL. Есть около 1000 способов сделать это неправильно, и я думаю, мне удалось попробовать их всех.

Код разбивается на несколько частей:

  • Построить URL POST
  • Сделать HTTP-соединение
  • Отправить URL-адрес POST
  • Считайте результат, который представляет собой XML
  • Извлечь URL-адрес imgur из XML
  • Отформатируйте imgur url как уценку (или как функцию Mathematica Hyperlink).

Вот код:

imgur[expr_] :=
 Module[{url, key, image, data, jUrl, jConn, jWriter, jInput, buffer,
   byte, xml, imgurUrl},
  Needs["JLink`"];
  JLink`JavaBlock[
   JLink`LoadJavaClass["java.net.URLEncoder"];
   url = "http://api.imgur.com/2/upload";
   key = "c07bc3fb59ef878d5e23a0c4972fbb29";
   image = ExportString[ExportString[expr, "PNG"], "Base64"];
   data =
    URLEncoder`encode["key"   , "UTF-8"] <> "=" <>
    URLEncoder`encode[ key    , "UTF-8"] <> "&" <>
    URLEncoder`encode["image" , "UTF-8"] <> "=" <>
    URLEncoder`encode[ image  , "UTF-8"] ;
   jUrl = JLink`JavaNew["java.net.URL", url];
   jConn = [email protected][];
   [email protected][True];
   jWriter =
    JLink`JavaNew["java.io.OutputStreamWriter",
     [email protected][]];
   [email protected][data];
   [email protected][];
   jInput = [email protected][];
   buffer = {};
   While[(byte = [email protected][]; byte >= 0), AppendTo[buffer, byte]];
   ];
  xml = ImportString[FromCharacterCode[buffer], "XML"];
  imgurUrl =
   Cases[xml,
     XMLElement["original", {}, {string_}] :>
      string, \[Infinity]][[1]];
  "![Mathematica graphic](" <> imgurUrl <> ")"
  ]

Тестирование:

In[]:= g = Graphics[{Blue, Disk[]}, PlotRange -> 1.2, ImageSize -> Small];
       pic = Overlay[{Blur[[email protected], 10], g}];
       imgur[pic]

Out[]= ![Mathematica graphic](http://i.imgur.com/eGOlL.png)

И фактическое изображение:

Mathematica graphic