从 Mathematica 上传图像到 Imgur
Posted
技术标签:
【中文标题】从 Mathematica 上传图像到 Imgur【英文标题】:Upload images to Imgur from Mathematica 【发布时间】:2011-12-23 17:01:17 【问题描述】:这是对所有mathematica
标签关注者的挑战。让我们通过创建一个imgur 上传器将图像插入到 Mathematica 的 SO 帖子中变得更加方便。
我们如何创建一个函数imgur[g_]
来光栅化它的参数(确保最终大小不大于 *** 帖子的宽度),将其转换为 PNG,上传到 imgur,并返回准备好的被粘贴MarkDown行如![Mathematica graphic](http://i.imgur.com/ZENa4.jpg)
?
有用的参考资料:
Imgur API Example of using POST request from Mathematica on WRI blog (posting to Twitter)ragfield Example of using POST requests from Mathematica on SO (uploading to ifile.it)我未能使后一种方法适应上传图像而不先将其导出到文件。
警告,小心使用! *** 使用separate imgur installation 无限期地保留图像。如果您使用主要的 imgur,the images will disappear after 6 months if no one views them。不幸的是,截至 2011 年 11 月,似乎有 no official way 以编程方式将图像上传到 ***。
更新:See below直接上传到 *** 的解决方案。
【问题讨论】:
请注意上传到http://i.stack.imgur.com/
比较困难(你必须“驱动” SO 接口)
@belisarius 哎呀,我没有意识到 *** 使用了一个单独的 imgur 站点......在主要的 imgur 站点上,图像可能不会永远保留,所以使用它可能不是一个好主意对于 SO imgur.com/faq#long(至少需要 1 个视图/6 个月才能保留它们)
手动发布图形并不难(在 V8 中)。鼠标右键一个图形,选择“将图像另存为...”。然后文件对话框在上次打开的位置打开,通常是我的桌面,其中一个名为 output.png 的文件已经在等待被其下一个化身覆盖。只需单击两次,再单击两次并将其发布在我的 SO 答案框中。最多需要 15 秒。获取 Markdown 行并粘贴大约需要相同的时间。
@Sjoerd 在 imgur[g]
,并复制和粘贴降价文本。在努力方面似乎真的没有太大区别。
【参考方案1】:
注意:这是使用匿名 imgur 上传器和我的匿名密钥。 imgur 站点将上传限制为每小时 50 次上传,这通常应该没问题,但是如果很多人同时尝试,这可能会导致问题。所以请在这里获取您自己的匿名密钥:
http://imgur.com/register/api_anon
然后将下面代码中的密钥替换为您自己的密钥(谢谢!)。
代码中最棘手的部分是从 Mathematica 表达式到 PNG 图像到 Base64 编码到 URL 编码的转换。大约有 1,000 种方法做错了,我想我设法全部尝试了。
代码分为几部分:
构造 POST url 建立 HTTP 连接 发送 POST 网址 读回结果,即 XML 从 XML 中提取 imgur url 将 imgur url 格式化为 markdown(或 MathematicaHyperlink
函数)。
代码如下:
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 = jUrl@openConnection[];
jConn@setDoOutput[True];
jWriter =
JLink`JavaNew["java.io.OutputStreamWriter",
jConn@getOutputStream[]];
jWriter@write[data];
jWriter@flush[];
jInput = jConn@getInputStream[];
buffer = ;
While[(byte = jInput@read[]; 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[Binarize@g, 10], g];
imgur[pic]
Out[]= ![Mathematica graphic](http://i.imgur.com/eGOlL.png)
还有实际的图片:
【讨论】:
有没有办法在 Mathematica 内部进行编码和上传,而无需求助于JLink
? +1,顺便说一句。
我收到错误:Java::excptn: A Java exception occurred: java.io.IOException: Server returned HTTP response code: 400 for URL: http://api.imgur.com/2/upload at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1290). XML`Parser`XMLGetString::string: String expected at position 1 in XML`Parser`XMLGetString[EndOfFile].
@Mr.Wizard -- 我现在在 V7 上也看到了这一点。 V7 使用 Base64 编码做了一些不同的事情:ExportString[ExportString[1, "PNG"], "Base64"]。我正在检查 V7 解决方案。
另外,像这样将查询字符串放在一起似乎有点笨拙。我可能会用queryString[enc_String, q:_String, _String..] := StringJoin@Riffle[ URLEncoder`encode[#1,enc]<> "="<> URLEncoder`encode[#2,enc]& @@@ q,"&"]
替换它。
@Mr.Wizard -- 在 V7 中,将设置图像的行替换为: image = Developer`EncodeBase64[ExportString[expr, "PNG"]];让我知道这是否解决了 V7 问题(对我有用)。【参考方案2】:
注意:获取具有此功能的现成调色板here。
Arnoud 的解决方案让我既兴奋又不耐烦,因此这里对其进行了改进。如果不研究他的代码,我不可能做到这一点。这个版本似乎更可靠一些,不太容易出现超时错误,但老实说,我根本不懂 Java,所以欢迎任何改进。
最重要的是:这个版本直接上传到stack.imgur.com
,所以在***这里使用是安全的,不用担心上传的图片过一段时间就会消失。
我提供三个功能:
stackImage
上传表达式,导出为PNG,并返回URL
stackMarkdown
返回markdown,可以复制了
stackCopyMarkdown
将 markdown 复制到剪贴板
下一步:创建一个调色板按钮,该按钮会自动为笔记本中的选定图形执行此操作。非常欢迎对代码进行改进。
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[
First@StringCases[First@Select[res, StringMatchQ[#, k ~~ ___] &],
k ~~ v___ ~~ ";" :> v],
"'"]
];
data = ExportString[g, "PNG"];
JavaBlock[
url = "https://***.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];
part@setContentType["image/png"];
entity = JavaNew["org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity", part, method@getParams[]];
method@setRequestEntity[entity];
code = client@executeMethod[method];
response = method@getResponseBodyAsString[];
]
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];
]
]
更新:
这是一个按钮,它将显示所选内容的预览并提供上传(或取消)功能。它需要定义前面的函数。
Button["Upload to SO",
Module[cell = NotebookRead@InputNotebook[], 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:
根据this question 的回答,这是一个仅适用于 Windows 的调色板按钮:
button = Button["Upload to SO",
Module[sel,
FrontEndExecute[
FrontEndToken[FrontEnd`SelectedNotebook[], "CopySpecial", "MGF"]];
sel = Cases[NotebookGet@ClipboardNotebook[],
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]
警告:即使您在预览框中单击取消,它也会破坏剪贴板内容。
【讨论】:
我认为光栅化调色板问题值得自己提出问题。 太棒了!我保留我的投票希望这一点。 +1 @Mr.Wizard 检查聊天室以获取打包解决方案【参考方案3】:一只小鸟刚刚告诉我这个问题的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"
未记录在案且处于试验阶段(因此可能会发生变化)。
【讨论】:
感谢您分享此 Arnoud!不幸的是,这在尝试上传到 *** 时似乎不起作用(使用我在对您的其他答案的评论中提到的方法)。我认为问题是图像必须以multiplart/form-data 提交,您知道该怎么做吗?另外,您对如何将其制作成将上传选择的调色板按钮(预览后)有任何想法吗?请参阅我的其他答案,了解我尝试了什么以及它如何不起作用(太窄的调色板宽度用于光栅化)。 +1:非常好!现在我正试图让它与 OAuth 一起使用,这样我就可以将图像直接上传到我的帐户相册等:)。另外,您是如何找到那些未记录的选项的?以上是关于从 Mathematica 上传图像到 Imgur的主要内容,如果未能解决你的问题,请参考以下文章
sh SH脚本截图并上传到Imgur(如果使用Snagit则从剪贴板获取)