首页 文章 精选 留言 我的

精选列表

搜索[图像理解],共10000篇文章
优秀的个人博客,低调大师

Python图像处理之图片文字识别(OCR)

OCR与Tesseract介绍 将图片翻译成文字一般被称为光学文字识别(Optical Character Recognition,OCR)。可以实现OCR 的底层库并不多,目前很多库都是使用共同的几个底层OCR 库,或者是在上面进行定制。 Tesseract 是一个OCR 库,目前由Google 赞助(Google 也是一家以OCR 和机器学习技术闻名于世的公司)。Tesseract 是目前公认最优秀、最精确的开源OCR 系统。 除了极高的精确度,Tesseract 也具有很高的灵活性。它可以通过训练识别出任何字体(只要这些字体的风格保持不变就可以),也可以识别出任何Unicode 字符。 Tesseract的安装与使用 Tesseract的Windows安装包下载地址为: http://digi.bib.uni-mannheim.de/tesseract/tesseract-ocr-setup-4.00.00dev.exe ,下载后双击直接安装即可。安装完后,需要将Tesseract添加到系统变量中。在CMD中输入tesseract -v, 如显示以下界面,则表示Tesseract安装完成且添加到系统变量中。 Linux 用户可以通过apt-get 安装: $sudo apt-get tesseract-ocr 用Tesseract可以识别格式规范的文字,主要具有以下特点: • 使用一个标准字体(不包含手写体、草书,或者十分“花哨的”字体) • 虽然被复印或拍照,字体还是很清晰,没有多余的痕迹或污点 • 排列整齐,没有歪歪斜斜的字 • 没有超出图片范围,也没有残缺不全,或紧紧贴在图片的边缘 下面将给出几个tesseract识别图片中文字的例子。 首先是E://figures/other/poems.jpg, 输入命令 tesseract E://figures/other/poems.jpg E://figures/other/poems.txt, 则会将poems.jpg中的识别文字写入到poems.txt中,如下图: 接着是稍微有点倾斜的文字图片th.jpg,识别情况如下: 可以看到识别的情况不如刚才规范字体的好,但是也能识别图片中的大部分字母。 最后是识别简体中文,需要事先安装简体中文语言包,下载地址为:https://github.com/tesseract-ocr/tessdata/find/master/chi_sim.traineddata ,再讲chi_sim.traineddata放在C:\Program Files (x86)\Tesseract-OCR\tessdata目录下。我们以图片timg.jpg为例: 输入命令: tesseract E://figures/other/timg.jpg E://figures/other/timg.txt -l chi_sim 识别结果如下: 只识别错了一个字,识别率还是不错的。 最后加一句,Tesseract对于彩色图片的识别效果没有黑白图片的效果好。 pytesseract pytesseract是Tesseract关于Python的接口,可以使用pip install pytesseract安装。安装完后,就可以使用Python调用Tesseract了,不过,你还需要一个Python的图片处理模块,可以安装pillow. 输入以下代码,可以实现同上述Tesseract命令一样的效果: import pytesseract from PIL import Image pytesseract.pytesseract.tesseract_cmd = 'C://Program Files (x86)/Tesseract-OCR/tesseract.exe' text = pytesseract.image_to_string(Image.open('E://figures/other/poems.jpg')) print(text) 运行结果如下: 参考文献 Python网络数据采集 【美】 Ryan Mitchell 人民邮电出版社 https://blog.csdn.net/dcrmg/article/details/78233459?locationNum=7&fps=1 http://www.inimei.cn/archives/297.html 注意:本人现已开通两个微信公众号: 因为Python(微信号为:python_math)以及轻松学会Python爬虫(微信号为:easy_web_scrape), 欢迎大家关注哦~~

优秀的个人博客,低调大师

android平台TextView使用ImageSpan画廊GIF图像

android-gif-drawable(https://github.com/koral--/android-gif-drawable/releases)开源项目---是一个蛮不错的android gif显示实现.本文在android-gif-drawable基础上介绍怎样实现TextView、EditText上展示Gif动态图。 网上有蛮多介绍这个框架使用的文章,比方http://www.open-open.com/lib/view/open1404888098200.html。 核心类GifDrawable间隔一定时间读取下一帧数据,然后运行invalidateSelf()----》CallBack::invalidateDrawable()---》View::verifyDrawable()和View::invalidate(),该帧数据刷新流程就运行结束。 而android-gif-drawable框架眼下已支持GifImageView、GifImageButton、GifTextView三个android widget,且 GifImageView、GifImageButton支持对src和backgroud设置Gif,而GifTextView对支持backgroud和CompoundDrawables设置Gif。 如今非常多app都支持Gif表情。但貌似还没有一个app对输入框(等)支持GIF。而基本全部的表情图片(包含Emoji)都是使用ImageSpan实现的。但默认的ImageSpan是无法支持GIF的。 參考android-gif-drawable框架中gif帧数据刷新流程,要支持GIF须要考虑并完毕以下三个操作: 1)对ImageSpan中的GifDrawable,何时设置其Callback,又何时清空该Callback,眼下TextView、ImageSpan和Spaned都没有设置Callback的地方。我们须要找一个合适的地方将TextView设置为GifDrawable的Callback; 2)在TextView::invalidateDrawable()中实现对GifDrawable的校验,即验证该GifDrawable是TextView的内容,须要刷新; 3)在TextView::invalidateDrawable()中实现怎样刷新TextView显示; 首先对于1)。我们參考下ImageView和TextView实现。ImageView的src drawable相应实现例如以下: /** * Sets a drawable as the content of this ImageView. * * @param drawable The drawable to set */ public void setImageDrawable(Drawable drawable) { if (mDrawable != drawable) { ... updateDrawable(drawable); ... } } private void updateDrawable(Drawable d) { if (mDrawable != null) { mDrawable.setCallback(null); unscheduleDrawable(mDrawable); } mDrawable = d; if (d != null) { d.setCallback(this); if (d.isStateful()) { d.setState(getDrawableState()); } d.setLevel(mLevel); d.setLayoutDirection(getLayoutDirection()); d.setVisible(getVisibility() == VISIBLE, true); mDrawableWidth = d.getIntrinsicWidth(); mDrawableHeight = d.getIntrinsicHeight(); applyColorMod(); configureBounds(); } else { mDrawableWidth = mDrawableHeight = -1; } }也就是说,ImageView在设置其src时。清空旧mDrawable的callback,然后将新设置的src drawable的callback设置为ImageView本身。 同理。TextView对于CompoundDrawables的callback处理也是在setCompoundDrawables()时。 而ImageSpan须要在什么时机设置GifDrawable的callback呢, public class GifImageSpan extends ImageSpan{ private Drawable mDrawable = null; public GifImageSpan(Drawable d) { super(d); mDrawable = d; } public GifImageSpan(Drawable d, int verticalAlignment) { super(d, verticalAlignment); mDrawable = d; } @Override public Drawable getDrawable() { return mDrawable; } } public class GifEditText extends EditText { private GifSpanChangeWatcher mGifSpanChangeWatcher; public GifEditText(Context context) { super(context); initGifSpanChangeWatcher(); } public GifEditText(Context context, AttributeSet attrs) { super(context, attrs); initGifSpanChangeWatcher(); } public GifEditText(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initGifSpanChangeWatcher(); } private void initGifSpanChangeWatcher() { mGifSpanChangeWatcher = new GifSpanChangeWatcher(this); addTextChangedListener(mGifSpanChangeWatcher); } @Override public void setText(CharSequence text, BufferType type) { CharSequence oldText = null; try { //EditText的默认mText为""。是一个String。但getText()强转为Editable,尼玛。仅仅能try/catch了 oldText = getText(); //首先清空全部旧GifImageSpan的callback和oldText上的GifSpanChangeWatcher if (!TextUtils.isEmpty(oldText) && oldText instanceof Spannable) { Spannable sp = (Spannable) oldText; final GifImageSpan[] spans = sp.getSpans(0, sp.length(), GifImageSpan.class); final int count = spans.length; for (int i = 0; i < count; i++) { spans[i].getDrawable().setCallback(null); } final GifSpanChangeWatcher[] watchers = sp.getSpans(0, sp.length(), GifSpanChangeWatcher.class); final int count1 = watchers.length; for (int i = 0; i < count1; i++) { sp.removeSpan(watchers[i]); } } } catch (Exception e) { } if (!TextUtils.isEmpty(text)) { if (!(text instanceof Editable)) { text = new SpannableStringBuilder(text); } } if (!TextUtils.isEmpty(text) && text instanceof Spannable) { Spannable sp = (Spannable) text; //设置新text中全部GifImageSpan的callback为当前EditText final GifImageSpan[] spans = sp.getSpans(0, sp.length(), GifImageSpan.class); final int count = spans.length; for (int i = 0; i < count; i++) { spans[i].getDrawable().setCallback(this); } //清空新text上的GifSpanChangeWatcher final GifSpanChangeWatcher[] watchers = sp.getSpans(0, sp.length(), GifSpanChangeWatcher.class); final int count1 = watchers.length; for (int i = 0; i < count1; i++) { sp.removeSpan(watchers[i]); } if (mGifSpanChangeWatcher == null) { mGifSpanChangeWatcher = new GifSpanChangeWatcher(this); } //设置新text上的GifSpanChangeWatcher sp.setSpan(mGifSpanChangeWatcher, 0, text.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE | (100 << Spanned.SPAN_PRIORITY_SHIFT)); } super.setText(text, type); } } public class GifSpanChangeWatcher implements SpanWatcher, TextWatcher{ private Drawable.Callback mCallback; public GifSpanChangeWatcher(Drawable.Callback callback) { mCallback = callback; } public void onSpanChanged(Spannable buf, Object what, int s, int e, int st, int en) { //do nothing } public void onSpanAdded(Spannable buf, Object what, int s, int e) { //设置callback if (what instanceof GifImageSpan) { ((GifImageSpan)what).getDrawable().setCallback(mCallback); } } public void onSpanRemoved(Spannable buf, Object what, int s, int e) { //清空callback if (what instanceof GifImageSpan) { ((GifImageSpan)what).getDrawable().setCallback(null); } } @Override public void afterTextChanged(Editable s) { if (s != null) { s.setSpan(this, 0, s.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE | (100 << Spanned.SPAN_PRIORITY_SHIFT)); } } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { // TODO Auto-generated method stub } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { // TODO Auto-generated method stub } } 也就是,在setText()和onSpanAdded()、onSpanRemoved()中运行操作(1) 然后,对于2),相同參考ImageView和TextView @Override protected boolean verifyDrawable(Drawable dr) { return mDrawable == dr || super.verifyDrawable(dr); } @Override protected boolean verifyDrawable(Drawable who) { final boolean verified = super.verifyDrawable(who); if (!verified && mDrawables != null) { return who == mDrawables.mDrawableLeft || who == mDrawables.mDrawableTop || who == mDrawables.mDrawableRight || who == mDrawables.mDrawableBottom || who == mDrawables.mDrawableStart || who == mDrawables.mDrawableEnd; } return verified; }直接上代码 public class GifEditText extends EditText { private GifImageSpan getImageSpan(Drawable drawable) { GifImageSpan imageSpan = null; CharSequence text = getText(); if (!TextUtils.isEmpty(text)) { if (text instanceof Spanned) { Spanned spanned = (Spanned) text; GifImageSpan[] spans = spanned.getSpans(0, text.length(), GifImageSpan.class); if (spans != null && spans.length > 0) { for (GifImageSpan span : spans) { if (drawable == span.getDrawable()) { imageSpan = span; } } } } } return imageSpan; } }getImageSpan()方法通过getSpans()获取全部的GifImageSpan。然后对照drawable,返回对应的GifImageSpan。 最后。操作3)更新View显示。相同參考下TextView @Override public void invalidateDrawable(Drawable drawable) { if (verifyDrawable(drawable)) { final Rect dirty = drawable.getBounds(); int scrollX = mScrollX; int scrollY = mScrollY; // IMPORTANT: The coordinates below are based on the coordinates computed // for each compound drawable in onDraw(). Make sure to update each section // accordingly. final TextView.Drawables drawables = mDrawables; if (drawables != null) { if (drawable == drawables.mDrawableLeft) { final int compoundPaddingTop = getCompoundPaddingTop(); final int compoundPaddingBottom = getCompoundPaddingBottom(); final int vspace = mBottom - mTop - compoundPaddingBottom - compoundPaddingTop; scrollX += mPaddingLeft; scrollY += compoundPaddingTop + (vspace - drawables.mDrawableHeightLeft) / 2; } else if (drawable == drawables.mDrawableRight) { final int compoundPaddingTop = getCompoundPaddingTop(); final int compoundPaddingBottom = getCompoundPaddingBottom(); final int vspace = mBottom - mTop - compoundPaddingBottom - compoundPaddingTop; scrollX += (mRight - mLeft - mPaddingRight - drawables.mDrawableSizeRight); scrollY += compoundPaddingTop + (vspace - drawables.mDrawableHeightRight) / 2; } else if (drawable == drawables.mDrawableTop) { final int compoundPaddingLeft = getCompoundPaddingLeft(); final int compoundPaddingRight = getCompoundPaddingRight(); final int hspace = mRight - mLeft - compoundPaddingRight - compoundPaddingLeft; scrollX += compoundPaddingLeft + (hspace - drawables.mDrawableWidthTop) / 2; scrollY += mPaddingTop; } else if (drawable == drawables.mDrawableBottom) { final int compoundPaddingLeft = getCompoundPaddingLeft(); final int compoundPaddingRight = getCompoundPaddingRight(); final int hspace = mRight - mLeft - compoundPaddingRight - compoundPaddingLeft; scrollX += compoundPaddingLeft + (hspace - drawables.mDrawableWidthBottom) / 2; scrollY += (mBottom - mTop - mPaddingBottom - drawables.mDrawableSizeBottom); } } invalidate(dirty.left + scrollX, dirty.top + scrollY, dirty.right + scrollX, dirty.bottom + scrollY); } }计算compoundDrawable位置栏,然后运行invalidate。 对于GifEditText貌似也能够类似操作,依据GifImageSpan的start、end计算其位置栏,然后运行invalidate()。只是计算过程太过复杂了。只是android4.4的TextView提供这种方法void invalidateRegion(int start, int end, boolean invalidateCursor)方法用于刷新start和end之间的区域,但还是蛮复杂的看的人眼花缭乱。研究了下这种方法终于是由谁调用的。 invalidateRegion()<<---invalidateCursor()<<---spanChange()<<---ChangeWatcher::onSpanChanged()、ChangeWatcher::onSpanAdded()、ChangeWatcher::onSpanRemoved() 也就是说,仅仅要TextView内容中span发生变化都会触发invalidateRegion()来刷新相应区域和cursor。 @Override public void invalidateDrawable(Drawable drawable) { GifImageSpan imageSpan = getImageSpan(drawable); Log.e("", "invalidateDrawable imageSpan:" + imageSpan); if (imageSpan != null) { CharSequence text = getText(); if (!TextUtils.isEmpty(text)) { if (text instanceof Editable) { Log.e("", "invalidateDrawable Editable:"); Editable editable = (Editable)text; int start = editable.getSpanStart(imageSpan); int end = editable.getSpanEnd(imageSpan); int flags = editable.getSpanFlags(imageSpan); editable.setSpan(imageSpan, start, end, flags); } } } else { super.invalidateDrawable(drawable); } }直接又一次设置该ImageSpan就可以触发ChangeWatcher::onSpanChanged()回调。也就会马上刷新其区域和cursor。 大功告成。执行ok。 上面是对EditText的实现,针对TextView实现略微有点差别 public class GifSpanTextView extends GifTextView { private GifSpanChangeWatcher mGifSpanChangeWatcher; public GifSpanTextView(Context context) { super(context); initGifSpanChangeWatcher(); } public GifSpanTextView(Context context, AttributeSet attrs) { super(context, attrs); initGifSpanChangeWatcher(); } public GifSpanTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initGifSpanChangeWatcher(); } private void initGifSpanChangeWatcher() { mGifSpanChangeWatcher = new GifSpanChangeWatcher(this); addTextChangedListener(mGifSpanChangeWatcher); } @Override public void setText(CharSequence text, BufferType type) { type = BufferType.EDITABLE; CharSequence oldText = getText(); if (!TextUtils.isEmpty(oldText) && oldText instanceof Spannable) { Spannable sp = (Spannable) oldText; final GifImageSpan[] spans = sp.getSpans(0, sp.length(), GifImageSpan.class); final int count = spans.length; for (int i = 0; i < count; i++) { spans[i].getDrawable().setCallback(null); } final GifSpanChangeWatcher[] watchers = sp.getSpans(0, sp.length(), GifSpanChangeWatcher.class); final int count1 = watchers.length; for (int i = 0; i < count1; i++) { sp.removeSpan(watchers[i]); } } if (!TextUtils.isEmpty(text) && text instanceof Spannable) { Spannable sp = (Spannable) text; final GifImageSpan[] spans = sp.getSpans(0, sp.length(), GifImageSpan.class); final int count = spans.length; for (int i = 0; i < count; i++) { spans[i].getDrawable().setCallback(this); } final GifSpanChangeWatcher[] watchers = sp.getSpans(0, sp.length(), GifSpanChangeWatcher.class); final int count1 = watchers.length; for (int i = 0; i < count1; i++) { sp.removeSpan(watchers[i]); } if (mGifSpanChangeWatcher == null) { mGifSpanChangeWatcher = new GifSpanChangeWatcher(this);; } sp.setSpan(mGifSpanChangeWatcher, 0, text.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE | (100 << Spanned.SPAN_PRIORITY_SHIFT)); } super.setText(text, type); } private GifImageSpan getImageSpan(Drawable drawable) { GifImageSpan imageSpan = null; CharSequence text = getText(); if (!TextUtils.isEmpty(text)) { if (text instanceof Spanned) { Spanned spanned = (Spanned) text; GifImageSpan[] spans = spanned.getSpans(0, text.length(), GifImageSpan.class); if (spans != null && spans.length > 0) { for (GifImageSpan span : spans) { if (drawable == span.getDrawable()) { imageSpan = span; } } } } } return imageSpan; } @Override public void invalidateDrawable(Drawable drawable) { GifImageSpan imageSpan = getImageSpan(drawable); if (imageSpan != null) { CharSequence text = getText(); if (!TextUtils.isEmpty(text)) { if (text instanceof Editable) { Editable editable = (Editable)text; int start = editable.getSpanStart(imageSpan); int end = editable.getSpanEnd(imageSpan); int flags = editable.getSpanFlags(imageSpan); editable.removeSpan(imageSpan); editable.setSpan(imageSpan, start, end, flags); } } } else { super.invalidateDrawable(drawable); } } } 设置其android:editable="true"或正上方setText(CharSequence text, BufferType type)将type设置BufferType.EDITABLE。 本文转自mfrbuaa博客园博客,原文链接:http://www.cnblogs.com/mfrbuaa/p/5045666.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

Facebook 图像识别平台 Lumos 是什么?

大多数人懒得给照片加标签。如果你属于这一类(大概率事件),那么你一定知道搜索某张照片有多辛苦。 但这很有可能即将成为过去。 本周,Facebook 披露了其机器学习平台 Lumos 的更多信息: Lumos 将使用户们利用相片内容进行搜索,而不是图片名称或是标签。 Facebook 应用机器学习负责人 Joaquin Quiñonero Candela 解释说: “换句话说,搜索‘黑衬衫照片‘时,系统能识别出每张照片里是否有黑衬衫,并据此搜索;即便照片并没有被添加标签也没有关系。” Lumos 利用了计算机视觉技术来识别照片中的人、动物和一系列其他物体。但它远不止于此,而是更进一大步,能识别出照片中发生的“事”。所以在识别某相片中有某个人之外,它还能够判断该人是在行走、骑马、弹琴还是在做其他的事情。雷锋网按,Facebook 并没有提

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册