Загрузить изображения в Imgur из Mathematica
Вот вызов всем подписчикам тегов mathematica
. Пусть гораздо удобнее вставлять изображения в SO-сообщение из Mathematica, создавая imgur uploader.
Как мы можем создать функцию imgur[g_]
, которая растрирует свой аргумент (убедитесь, что конечный размер не шире, чем ширина сообщений StackOverflow), преобразуйте его в PNG, загрузите в imgur и вернете наклейте MarkDown, например 
?
Полезные ссылки:
Мне не удалось адаптировать этот последний метод к загрузке изображения, не экспортируя его сначала в файл.
Предупреждение, используйте с осторожностью! StackOverflow использует отдельную установку imgur, которая сохраняет изображения на неопределенный срок. Если вы используете основной imgur, изображения исчезнут через 6 месяцев, если их никто не увидит. К сожалению, с ноября 2011 года, как представляется, нет официального пути для программной загрузки изображений в StackOverflow.
Обновление: См. ниже решение для загрузки в StackOverflow напрямую.
Ответы
Ответ 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]];
""
]
Это только 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_] := ""
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]];
""
]
Тестирование:
In[]:= g = Graphics[{Blue, Disk[]}, PlotRange -> 1.2, ImageSize -> Small];
pic = Overlay[{Blur[[email protected], 10], g}];
imgur[pic]
Out[]= 
И фактическое изображение:
![Mathematica graphic]()