2014年6月28日 星期六

關於java,android,lucene開發的一些事....

以下是個人學習java的一些記錄,隨時會增減修改。

使用grouplayout,JSplitPane的比例亂掉

JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP);
splitPane_2.setRightComponent(tabbedPane);

paneT1 = new JScrollPane();
paneT2 = new JPanel();


tabbedPane.addTab("簡要", paneT1);
tabbedPane.addTab("全文", paneT2);
GridBagLayout gbl_paneT2 = new GridBagLayout();
gbl_paneT2.columnWidths = new int[]{0, 0, 0, 0, 0, 0, 0};
gbl_paneT2.rowHeights = new int[]{0, 0, 0};
gbl_paneT2.columnWeights = new double[]{1.0, 0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE};
gbl_paneT2.rowWeights = new double[]{0.0, 1.0, Double.MIN_VALUE};
paneT2.setLayout(gbl_paneT2);

在JTabbedPane 中有兩個tab paneT1及pantT2, paneT1內只包含一個JScrollPane,內含JList, paneT2包含兩個JButton及一個含JScrollPane的JTextArea。paneT1因為只有一個JList所以沒有特別使用佈局(layout),而paneT2一開始先放一個JPanel,再考慮用grouplayout作佈局,可是不知怎麼的,一放上去以後,原來整個JSplitPane的比例就變的很tricky,整個比例都亂掉了,後來改用GridBagLayout才正常,原因...目前不知。。。


JScrollPane 為何用setViewportView,而不用add來加入物件

scrollPane_1.setViewportView(textArea_1);

Q:JscrollPane.setviewportview與JscrollPane.add之間的差異
當我們想要把展示的東西放在panel內,我們只需用add方法將其添加到panel,但我們為什麼不能添加一個table或textArea到ScrollPane中,而必須調用(呼叫)setviewportview的方法? 而不用add()方法呢?

A:基本上,你不應該使用JScrollPane#add 。

JScrollPane連接到一個JViewport ,JScrollPane用這個來顯示添加到view視圖中的任何組件(元件)(如textarea, table, list等)。
setViewportView是一個方便的方法,用來顯示滾動窗格(scrollpane)內容的一部分,它就像一個窗戶一樣,只能看到窗外世界的一部分,或像是照像機的觀景窗,所以add的方法,實際上在JScrollPane中並不代表任何意義。add只是單純將物件加入容器內。

rgZsh.gif

關於valueChanged執行兩次的情況


model = new DefaultTableModel(){
public boolean isCellEditable(int row, int column) {
return false;
}
};

lstModel = new DefaultListModel();
list = new JList(lstModel);

list.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent arg0) {
if (arg0.getValueIsAdjusting()) {
// The mouse button has not yet been released
return;
}
int idx=list.getSelectedIndex();
ss.moveCaret(2,idx,textArea_1);
//arg0.getLastIndex();
}
});


getValueIsAdjusting在按下滑鼠時,即使未放開滑鼠就已經執行,當放開滑鼠時,還會再執行一次,所以會造成執行兩次的情況,增加下面這一段是為了避免執行兩次的狀況發生。
if (arg0.getValueIsAdjusting()) {
// The mouse button has not yet been released
return;
}


利用ArrayList來處理java不固定長度的陣列

如果一開始不能確定陣列要給多少長度,而是要跑完一段長長的作業,才能知道陣列長度。可用ArrayList來代替一般的陣列,如下用add的方法,隨時加入元素到ArrayList中,不必事先決定陣列的長度。非常方便。但是要注意加入的元素最好是int, long 等單純的型別,如果在ArrayList中放入陣列,要很小心。因為放入陣列到arraylist事實上是放入該陣列的位址。它真實的值是指向別的地方。有可能發生意想不到的情況。我曾經發生一次誤用的情況,但因當時未記錄,現在也無法回想當時的情況,只能提醒自己小心。


ArrayList bl = new ArrayList();
ArrayList el = new ArrayList();

int b,e;
Pattern pattern = Pattern.compile(str);
Matcher matcher = pattern.matcher(textString);

int count = 0;
while(matcher.find()) {
b=matcher.start();
e=matcher.end();
String s=textString.substring(b-50,100+e);
lst.addElement(s);
count++;
System.out.println("found: " + count + " : "
+ matcher.start() + " - " + matcher.end());
bl.add(b);
el.add(e);
}

/*for (ScoreDoc scoreDoc : hits.scoreDocs) {
Document doc = searcher.doc(scoreDoc.doc);
*/

hitCollects=new int[bl.size()][2];
maxHit=bl.size();
for(int i=0;i<bl.size();i++){
hitCollects[i][0]=(int)bl.get(i);
hitCollects[i][1]=(int)el.get(i);
}

curHit=0;
return hitCollects;



正則表達式 -group 用法


Pattern p = Pattern.compile("(.+)\\*$");
Matcher m = p.matcher(str);
if (m.find()) {
str=m.group(1)+"file:///D|/w*";
}

以上的效果,如果是 abc* 會換成 abc\\*
group是針對()來說的,group(0)就是指的整個串,group(1) 指的是第一個括號裡的東西,group(2)指的第二個括號裡的東西。 所以上例中group(1) => (.+) => 一個以上的任意字元


str=str.replace("*","file:///D|/w*");
str=str.replace("*","[\\w']*");
以上兩句的區別,第二句包含縮寫符號,匹配ca*t,可以找出can't,第一句不行

str=str.replace("?","file:///D|/w");
將通配字元,轉成正則表示式

正規表示式,如何不區分大小寫

Pattern pattern = Pattern.compile("file:///D|/b%22%2Bstr%2B%22/b");
Pattern pattern = Pattern.compile("file:///D|/b%22%2Bstr%2B%22/b%22%2CPattern.CASE_INSENSITIVE);

正規表示式-\b\B的意義

\b 指的是英文字與空格(不一定是空格,如換行,不是字母的符號等...)的間隙的那個位置,比對英文字的邊界,例如空格 例如 /\bn\w/ 可以比對 "noonday" 中的 'no' ;
/\wy\b/ 可比對 "possibly yesterday." 中的 'ly'

\B 比對非「英文字的邊界」 例如, /\w\Bn/ 可以比對 "noonday" 中的 'on' ,
另外 /y\B\w/ 可以比對 "possibly yesterday." 中的 'ye'

有無加"file:///D|/b"區別
加上的話,b*nd 會比對出 behind, blind,即使是句子的第一個英文字前無空白,仍能正確比對
若不加,b*nd 會比對出behind,blind,hidebrand


如何使用正則表示去除雙引號

Matcher matcher = pattern.matcher(textString);
Pattern p = Pattern.compile("^\\\"(.+)\\\"$");
Matcher m = p.matcher(str);

if (m.find()) {
str = m.group(1);
}



lucene 分詞器的選擇-StandardAnalyzer

關於stop word的問題
使用StandardAnalyzer分詞,會去掉stop word,如on the in a an等,使得像"on record"這樣的phrase search失敗,而只找到record的文件,造成搜尋結果比預期多,且不準確。
改用WhitespaceAnalyzer,也無法改善,且像"alan,"字尾緊跟著","號會找不到,用StandardAnalyze則無問題
SimpleAnalyzer "alan,"可找到,但"on record"還是找不到

解決方法:阻止StandardAnalyzer去除stop word
1)在建立索引時,使用如下命令建立StandardAnalyzer
Analyzer analyzer =
new StandardAnalyzer(Version.LUCENE_48, CharArraySet.EMPTY_SET);
IndexWriterConfig conf = new IndexWriterConfig(Version.LUCENE_48, analyzer);

2)在搜尋建立QueryParser時,使用如下命令建立StandardAnalyzer
Analyzer analyzer =
new StandardAnalyzer(Version.LUCENE_48, CharArraySet.EMPTY_SET);
QueryParser parser =
new QueryParser(Version.LUCENE_48, where, analyzer);

關於縮寫字及連字符
StandardAnalyzer 找不到縮寫字 如can't , "single-handedly"加雙引號可以找到,不加則當成single handedly 2個字去找,找出很多.


移除java Jtextarea內所有的highlights

public static void removeHighlights(JTextArea textComp) {
Highlighter hilite = textComp.getHighlighter();
Highlighter.Highlight[] hilites = hilite.getHighlights();

for (int i = 0; i < hilites.length; i++) {
//if (hilites[i].getPainter() instanceof MyHighlightPainter) {
hilite.removeHighlight(hilites[i]);
//}
}
}


改變highlight的顏色

Highlighter.HighlightPainter greenPainter =
new DefaultHighlighter.DefaultHighlightPainter(Color.GREEN);
Highlighter h = ta.getHighlighter();

h.addHighlight(hitCollects[curHit][0], hitCollects[curHit][1],
greenPainter);


正則表示式範例

abc\W+([A-Za-z]+\W+)*def
取abc至def間所有英文字的token

\b(\w+\W+){0,3}take(\W+\w+){0,3}\b
以take為中心向前或後最多取10組文數字的token,即使遇到換行符號,仍會往前或往後取,因此不限於只取一行文字
\W是分隔token的符號(除了文數字以外的字元,如空白,標點,換行等...)
\w是文數字及底線
以下為例:
abc def 7678
sdkd dir take much
cat doff place
會取出 7678 sdkd dir take much cat doff

如果用在一個長的文件中,有二個以上的結果,取過的部分,不會再取一次:

bb take cc dd ee take ff gg
\b(\w+\W+){0,2}take(\W+\w+){0,2}\b
第一次取出 [bb take cc dd]
第二次依規則前後最多取二組,應取出 [dd ee take ff gg]
但dd已經在第一次選取時取過了,因此第二次選取只會取出
[ee take ff gg]



coming with you...No.
00350 I need you here to take care of Arendelle.
00351 On my honor.
00352 I leave

表示次數的regex後面,不加或再加上?或+之不同:

光看這樣的說明是無法分出三者不同,以下舉例說明。
Greedy quantifiers
字串 "xfooxxxxxxfoo"
(不加) pattern ".*foo"
結果 xfooxxxxxxfoo
Greedy字面翻譯是貪婪,也就是盡可能的取字串,其實最貪婪的是第三種方法,因為Greedy還會把之後相符的資料留下來,Possessive吃的連骨頭都不剩。

Reluctant quantifiers
字串 "xfooxxxxxxfoo"
(加上?)pattern ".*?foo"
結果 xfoo 和 xxxxxxfoo
Reluctant字面翻譯是勉強,也就是抓最小可能,像這個例子,第一次抓一個x之後發現後面和foo相符,就得第一個結果,然後一直到最後又得到第二個結果。

Possessive quantifiers
字串 "xfooxxxxxxfoo"
(加上+)pattern ".*+foo"
結果 沒有相符合資料,因為所有的資料都與"."比較相符,最後沒有剩下的字串可以和foo做比較,所以沒有符合資料。


java 注意contructor 不必宣告傳回值

public class aaa{....
public aaa(){
}
}

上例中如果寫成public void aaa()會被視為一般函數


宣告建立ArrayList時,先指定型別

ArrayList
ArrayList<Integer> bl = new ArrayList<Integer>();

bl.add(1);
bl.add(2);
bl.add(3);

使用新的for語法
for (int a:bl){
System.out.println(a) //印出123
}

因前面宣告建立ArrayList時,先指定型別為int,則在取出資料時,就不用做型別轉換.如下例1),否刖就要先轉成int,如例2)
1) a = bl.get(i);
2) a = (int) bl.get(i);


netbeans 比對兩個檔案的技巧

先同時開啟兩個要比對的檔案,拖動其中一個檔案的頁籤,往右或往下,待出現方框時放開,則會將兩個檔案分別並排開啟。

getContentPane().invalidate(),getContentPane().validate()的使用

table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent event) {

....程式碼.....

getContentPane().invalidate();
getContentPane().validate();
}

getContentPane().invalidate();=>出錯,只能放在valueChanged的範圍內
getContentPane().validate();
});


繼承關係下的Constructor執行順序

先將所有變數設為內定值。對數值型態來說,其值為0; 對reference來說,其值為null; 對boolean來說,其值為false。
呼叫父類別的constructor。如果子類別Constructor裡沒有指定父類別的Constructor, 則使用父類別沒有參數的Constructor。
執行變數宣告的初始化動作。
執行自己的constructor。

netbeans好用的命令 右鍵->find usages 可跨檔案尋找,用find只能在一個檔案內找

ArrayList<String> strList = new ArrayList<String>();
strList.add("abc");
strList.add("def");
strList.add("ghi");
s="Def";

這種情況下用 int index=strList.indexOf(s) 無法找出正確資料,因無法忽略大小寫

必須改用手工去比對
int index = -1;
for (int i=0;i<strList.size();i++){
if (strList.get(i).equalsIgnoreCase(s)) {
index=i;
break;
}
}

String a="abc";
String b="abc";
a==b 不一定為真,因判斷的是a,b所參考的物件是否相同,並不是值是否相同
應該用a.equals(b)才是判斷值是否相同


使用字串相加 string a="a"+b";效率較差

----
最近看了一個面試題是這樣的:

char 是否能存儲一個中文字符,為什麼?

char類型一般佔用兩個字節,所以能存儲中文字符(一個中文字符佔用兩個字節)。

char a = '中' ;(合法) char a = 'ab' ;(非法的)

所以char類型在內存中佔用兩個字節空間,但是只能表示一個字符。若是只要表示一個字節的字符,可以考慮byte。

byte表示字節,佔用內存一個字節的空間。

byte a = 'a' ;(合法) byte a = '中' ;(非法的,不能存放中文字符)

另外:1 byte = 8 bit (bit就是0和1的一個位數,8bit表示一個字節)

String str = "中" ;

byte[] a = str.getBytes();

System.out.println(a.length); //打印出多長?

result:可能2,3,4.

原因是getBytes()方法會根據當前默認的字符編碼格式獲取字節數組,gbk/gb2312佔2位,utf-8佔3位,unicode佔4位(很多地方看見別人說是佔兩位,但是親測出來的結果是4,求解!)

可以str.getBytes("GBK"),str.getBytes("UTF-8")使用,指定了編碼格式,就不根據默認的取得了。
----
eclipse
'Launching android' has encountered a problem.

An internal error occurred during : "Launching android".

Clicking "details >>" produces an extra line:

Path for project must have only one segment

因為configuration未給名,run->run configurations,選擇一個configuration在project的地方給名字即可
--------
eclipse ctrl+h 可多檔搜尋文字
在logcat的某一項目的第一行,按ctrl+c可複製,如果在第二行就不行
--------

06-18 16:44:42.982: E/AndroidRuntime(3437): java.lang.RuntimeException:
Unable to start activity ComponentInfo{com.example.engandroid/com.example.engandroid.ItemListActivity}:
android.view.InflateException: Binary XML file line #24: Error inflating class fragment


错误信息:

android.view.InflateException: Binary XML file line #8: Error inflating class fragment

http://blog.csdn.net/duguang77/article/details/17579847

下面我总结下此错误出现的原因:

1.XML文件中引入的Fragment路径不对(如下图)

2.在Activity继承Fragment时引入的包名不对

关于什么时候引入android.app.Fragment和
android.support.v4.app.Fragment(向下相容3.0以下)

public class MainActivity extends Activity { // above 3.0
public class MainActivity extends FragmentActivity { // below 3.0


请参考我的另外一篇文章:

点击了解:【android fragment android.support.v4.app.Fragment与android.app.Fragment区别】

06-18 17:46:26.981: E/AndroidRuntime(4193): Caused by: java.lang.IllegalArgumentException:
Binary XML file line #24: Duplicate id 0x7f05003d, tag null, or parent id 0xffffffff with another fragment
for com.example.engandroid.ItemListFragment

The problem is that what you are trying to do shouldn't be done. You shouldn't be inflating fragments inside other fragments. From Android's documentation:

Note: You cannot inflate a layout into a fragment when that layout includes a <fragment>. Nested fragments are only supported when added to a fragment dynamically.

While you may be able to accomplish the task with the hacks presented here, I highly suggest you don't do it. Its impossible to be sure that these hacks will handle what each new Android OS does when you try to inflate a layout for a fragment containing another fragment.

The only Android-supported way to add a fragment to another fragment is via a transaction from the child fragment manager.

---------

<fragment
android:id="@+id/eng_list"
android:name="com.example.engsearcha.EngListFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
tools:layout="@android:layout/list_content" />

<FrameLayout
android:id="@+id/eng_detail_container"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3" />

以上佈局,如果 android:layout_width="0dp"不設為 0dp,會使得比例變得很不正常

----------
eclipse 使用ctrl+H作search時,如果是選擇?java search,file search,有時因從外部copy檔案進來,未sync會搜尋不到,此時會提示錯誤,從detail中可看到訊息
先在package explorer中refresh即可


setContentView(R.layout.activity_eng_list);中的R.layout.[activity_eng_list]和檔名[activity_eng_list].xml是同步的,用refactor->rename改其中一個地方,另一個也會跟著改
而 activity_eng_list.xml內有一段:
<fragment
android:id="@+id/eng_list"
android:name="com.example.engsearcha.EngListFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
tools:layout="@android:layout/list_content" />
會建立啟動com.example.engsearcha.EngListFragment這一個物件. 這是屬於靜態建立fragment.
----------

setListAdapter(new ArrayAdapter<DummyContent.DummyItem>(getActivity(),
android.R.layout.simple_list_item_activated_1,
android.R.id.text1, DummyContent.ITEMS));


android.R.layout.simple_list_item_1:一行text
android.R.layout.simple_list_item_2:一行text較大,一行text較小
android.R.layout.simple_list_item_single_choice:單選
android.R.layout.simple_list_item_multiple_choice:多選按鈕
android.R.layout.simple_list_item_checked:勾選盒
------------

static後面加大括號的含義
static { }
In the Java programming language, a block is a group of lines of code enclosed in curly braces. Blocks serve many purposes in Java--
for instance, control Java keywords such as "while" take a block as an argument. In particular, Java supports "static blocks."
A static block gets executed exactly once per class--rather than once per object created in the class, as would be the case for code
included in a constructor method. You can use static Java blocks to perform one-time tasks when the class in question gets loaded into
memory by the Java Virtual Machine.

static blocks只有在類別被載入時,執行一次,物件被建立時,不會執行
----------

Few days back I came to know about a different way of initializing collections and objects in Java. Although this method of initialization has been there in Java since quite a few years now, very few people actually knows about it or have used it in their code. The technique is called Double Brace Initialization technique in Java.

Let us see first how we will initialize an ArrayList in Java by "normal" way:
List<String> countries = new ArrayList<String>();
countries.add("Switzerland");
countries.add("France");
countries.add("Germany");
countries.add("Italy");
countries.add("India");

Above code snippet first creates an object of ArrayList type String and then add one by one countries in it.

Now check the Double Brace Initialization of the same code:
List<String> countries = new ArrayList<String>() {{
add("India");
add("Switzerland");
add("Italy");
add("France");
add("Germany");
}};


How does this works?

Well, the double brace ( {{ .. }} ) initialization in Java works this way. The first brace creates a new AnonymousInnerClass,
第一個大括號,宣告是一個內部的匿名類別
the second declares an instance initializer block that is run when the anonymous inner class is instantiated.
This type of initializer block is formally called an "instance initializer",
because it is declared withing the instance scope of the class
第二個大括號是一個"instance initializer",當物件被建立時執行(類似constructor)

- "static initializers" are a related concept where the keyword static is placed before the brace that starts the block,
and which is executed at the class level as soon as the classloader completes loading the class .
The initializer block can use any methods, fields and final variables available in the containing scope,
but one has to be wary of the fact that initializers are run before constructors.

If you have not understood the concept, try executing following code and it will make things clear for you.
public class Test {
public Test() {
System.out.println("Constructor called");
}
static {
System.out.println("Static block called");
}

{
System.out.println("Instance initializer called");
}

public static void main(String[] args) {
new Test();
new Test();
}
}
Output:
第一個new Text()執行時:
Static block called
Instance initializer called
Constructor called
第二個new Text()執行時:
Instance initializer called
Constructor called

執行的優先次序:static block->instance initializer->constructor
static block:類別載入時,只執行一次
Instance initializer:物件建立時執行.
Constructor:物件建立時執行

Instance initializer比Constructor好用之處
1)可以處理catch exceptions.
2)如果一個類別有多個constructor,每次可能執行不同的constructor,但是你希望每次都要執行一段程式碼,則你必需每一個constructo重覆寫一段相同的程式碼,執行同一件事,如果寫在Instance initializer裡,就只要寫一次就好.
3)用在內部匿名類別,因為內部匿名類別不能宣告constructor

----------

This seems to explain it well:

"Instance initializers are a useful alternative to instance variable initializers whenever:

initializer code must catch exceptions, or

perform fancy calculations that can't be expressed with an instance variable initializer. You could, of course, always write such code in constructors.

But in a class that had multiple constructors, you would have to repeat the code in each constructor. With an instance initializer,
you can just write the code once, and it will be executed no matter what constructor is used to create the object.
Instance initializers are also useful in anonymous inner classes, which can't declare any constructors at all."

Read more...

四種免費中文ocr文字辨識的測試與使用

因為工作需要,必須做一些中文文字的辨識(OCR),找了許多資料,找到了一些免費軟體,我測試了四種均是免費OCR軟體(除了附加在ms office那個,當然Office是要錢的,不過我想大部分人都有吧,所以也把它的附加功能列入免費軟體)。
這些辨識工具,在不同情況下,有不同的使用方式,有些工具如果操作錯誤,還會影響辨識效果,有些安裝時沒注意到細節,還會一直出現錯誤,因為花了很多功夫,試了很多軟體,所以乾脆作成記錄,方便以後有同樣需求時參考,也提供有相同需求的朋友一些方向,不用像我走了許多冤枉路,浪費了許多時間。

這裡所提供的文字辨識功能均支援中文辨識(當然也支援英文),我所測試的均以繁體中文為主,唯一例外的是Capture2Text,因找到資料說它對簡體字的辨識效果不錯,因此也特別測試了一下,如果您不想看下面冗長的文章,以下的注意事項及使用心得,可以先給您一個快速的參考:

注意事項:

*要使用JOCR或Office來辨識:
1)必須在安裝office時,選擇安裝 Microsoft Office Document Imaging,(最好選全部安裝,因為裡面的Microsoft Office Document Image Writer在不是使用Jocr的情況,而是用Microsoft Office Document Imaging 時會用到)

2)據找到的資料指出要在Office 2003以上的版本才可使用。(我實測office xp不能使用,我沒有Office 2003,直接使用Office 2007)

3)office 2007要安裝service pack 3,才能正常使用。

*中文原始文件的文字方向很重要,會大大的影響辨識結果,我所測試的文字均是橫向,Google Doc直接在其說明資料,表明不支援直行中文字辨識,Capture2Text有選項可以調整橫向或直向中文字。


使用心得:

*在幾個免費的ocr軟體中,操作上最沒有問題的是Capture2Text,在操作過程中,沒有出現異常錯誤,

*在我測試的條件下,辨識率最好的是JOCR及Capture2Text(只限於辨識簡體中文時),辨識效果最不佳的是Google 雲端硬碟,特別強調我的測試條件,是因為這只是我工作過程中的附帶記錄,所以測試OCR的原始樣本並不多,只是我工作上使用到的情況,大都是直接在螢幕上擷取文字來辨識,並未用掃描器或數位相機從紙本文件中辨識,這也可能是Google雲端及Office Document Imaging的辨識效果較差的原因(文末有同一原始文件,各個軟體的辨識結果供參考,請參看辨識結果比一比)。

*而辨識速度,除了Capture2Text可以感覺到慢了一些,其他的看不出明顯的差異。

*操作最煩瑣的是使用Office的 Microsoft Office Document Imaging來作辨識,它必須先把原始圖檔用一個虛擬印表機轉成tif檔,再到Microsoft Office Document Imaging進行辨識。

綜合以上,我認為用Jocr最優秀,它檔案極小,操作簡單,辨識率也不錯,速度也還好,但像Jocr或Capture2Text是直接在螢幕上選取一個區域作辨識,如果是多個文件(圖檔),或是原始圖檔超過一個畫面大小,就會比較麻煩。
而Google或Office是直接讀取圖檔,可以一次讀進一個或多個影像檔進行辨識,不必在螢幕上選取操作,在多檔或批次作業上佔優勢。

反之Jocr及Capture2Text雖然不利於多檔或大檔案辨識,但其優點就是,在螢幕上看到的影像文字,只要一選取,立即辨識,方便又快速,像有時候遇到電腦顯示的錯誤訊息,或視窗上的文字,想在搜尋引擎上尋找相關資料,如果萬一不能用複製,貼上來處理,就必須親自打字,如果用Jocr或Capture2Text就可以即時辨識成文字來運用。


以下逐一說明操作步驟:

JOcr

下載:JOCR 1.0 Download (Free) - JOCR.exe

JOCR是一個可以辨識圖片裡的文字並存成文字檔的免費軟體,不過其實它只是借用Microsoft Office的殼,實際的功能是Microsoft Office提供的, 因此它的檔案極小,不到100K。

使用前你的office 必須安裝Microsoft Office Document Imaging,如果安裝office時,沒有安裝的話,請把您的office光碟拿出來,增加安裝此一項目, 如下圖所示。
2014-06-05 08 49 50.png

至於那一版的Microsoft Office才有提供呢,我目前使用的Office XP找不到此功能,應該是Microsoft Office 2003以上的版本才有,(我另外找到Microsoft Office 2007有提供),JOCR的操作非常簡單,它不用安裝,只要在下載回來的檔案上按兩下滑鼠左鍵即可執行;還有它的檔案非常小,大約只有80K左右。

使用JOcr前先到Ms Office 工具中作語言項目的設定,從程式集進入「Microsoft Office->Microsoft Office工具->Microsoft Office Document Imaging 」

2014-06-05 06 33 46.png


進入後從工具->選項->OCR 選擇您要的語言,這裡當然選擇中文(繁體)如下圖:
2014-06-05 08 21 56.png


上述設定完成後,接著啟動JOCR,先按下「Capture Region」,然後選取圖片中你想辨識的區域,接著選擇辨識的語言,並按下「Recognize」就會開始辨識,辨識完成的文字會出現在記事本內, 如果您出現如下圖的錯誤訊息,是要你到Ms Office 工具中作語言項目的設定,可是我已照著設定,仍不能正常工作, 後來我安裝了Office 2007 Service Pack 3,就可正常使用了。

2014-06-05 08 23 32.png




直接使用Office內 Microsoft Office Document Imaging的OCR功能


如同前文所講,JOcr只是借用Office的功能,提供一個較方便的方式來作辨識,那當然也可以直接使用Office內的功能來完成,雖然Office不是免費的軟體,但是應該大部分的人都有安裝。使用方法如下:

先安裝 Microsoft Office Document Image Writer, 如上面第一個圖中所示,如果您有全部安裝Microsoft Office Document Imaging的話,也會安裝Microsoft Office Document Image Writer,安裝完成後會在系統的印表機中多出一個Microsoft Office Document Image Writer的虛擬印表機。


使用步驟
1) 先在檔案總管找出要辨識的圖片(不同於JOCR可以在螢幕上直接指定一塊區域來辨識,它辨識的是整個圖檔),請在圖片上按滑鼠右鍵,選擇[列印],出現相片列印精靈後,點擊[下一步],選擇印表機時,請選擇Microsoft Office Document Image Writer的印表機。

2) 點擊[列印喜好設定],定好要印的紙張大小(建議用A3紙),如果圖片文字方向不對,請選好直印或橫印加以調整,選擇好之後點擊[確定],點擊[下一步]。

3)點選[下一步],存成tif格式檔案, (這個虛擬印表機事實上是一個轉檔的功能,將你的圖片轉成tif檔,以方便下一步的辨識 )

4)在剛剛存好檔的 tif 檔,使用 Microsoft Office Document Imaging 來開啟(操作方式上面JOCR時已提到過,程式集進入「Microsoft Office->Microsoft Office工具->Microsoft Office Document Imaging 」)。


5)進入後選檔案->開啟,打開上述轉好的tif檔
打開後可看到如下圖的縮圖,再縮圖上按右鍵,還有一些功能選項,如果在上文列印(轉tif檔)原圖時,沒有選好紙張方向,在此處也可以旋轉頁面來調整。

2014-06-16_105532.png

再進入「工具->使用OCR 辨識文字」,辨識完成後,並不會出現任何完成的訊息,而是在左邊的預覽圖右下角就會出現已辨識的圖樣,如下圖紅框內的圖示。

2014-06-16_105652.png

再點「工具 -> 傳送文字到 Word」。


Capture2Text


Capture2Text個人認為是一款還算不錯的免費文字辨識軟體,它可以像JOcr一樣直接在螢幕上,選定一個區域作辨認,又不必像JOcr必須背著Office這個大包袱,另一方面,它也不像直接在Office內作辨識那麼繁瑣,但有一個問題,就是它的辨識速度較慢,辨識簡體中文較準確,繁體中文差了一些,不過目前很多pdf或圖片檔的免費電子書,幾乎都是簡體中文,拿它來辨識,應該是非常適合,所以這裡也一並介紹。


Capture2Text下載點/

各種語言的tessdata pack(包含中文繁簡體)

簡體中文的nhocr pack (如果是辨識簡體,建議下載這個,辨識率較高)

這裡也提供個人已壓縮好的Capture2Text,含繁簡體(87M),當然主程式本來就含有日英德法西班牙意大利文,如果想省事的人可直接下這套解壓即可使用, 下載點:
http://pan.baidu.com/s/1hqFZWMC

如果不是下載我自製的懶人包,這裡提供使用繁簡體nhocr的使用方式:
下載下來主程式以後,直接解壓縮,簡體中文的nhocr pack也下載下來,一樣解壓後,將nhocr pack內的檔案全部copy到Capture2Text主程式內的\Utils\nhocr\Dic的資料夾內。
然後直接執行Capture2Text內的Capture2Text,exe執行檔,在右下工作列中會出現Capture2Text的圖示,在圖示上點右鍵,選擇ocr language,再挑選欲辨識的語言。

2014-06-05 19 00 56.png

(如果要辨識繁體中文,只能下載tessdata pack,因為nhocr沒有繁體中文, 請copy 到\Utils\tesseract\tessdata資料夾內,其他步驟與上相同)



要開始辨識時,請先將鼠標移到您要辨識螢幕區域的左上角,按下Windows鍵+Q,然後直接拖動滑鼠(不用按滑鼠鍵),圈出一個藍色的矩形範圍(如下圖)。

2014-06-05 19 07 04.png


這個範圍就是您要作辨識的區域,此時你可以用空白鍵切換左上,右下角,再拖動滑鼠改變選取區大小,也可以按下滑鼠右鍵,拖動以移動整個選取區,還可以用方向鍵調整選取大小,區域選定好以後,再次按下Windows鍵+Q(或滑鼠左鍵),Capture2Text就開始辨識作業,辨識速度取決您的電腦速度,辨識完成後,會彈出一個視窗,成功辨識的文字會在視窗內,也會放在剪貼薄內,要運用直接貼上即可。

下面兩個圖分別是簡體繁體中文的辨識效果,在popup result的結果視窗中,可以看到簡體(選用Chinese-NHocr)比繁體效果要好很多,
以一個免費的ocr軟體來說,乍看之下似乎有許多字未能正確辨識,但這裡的例子是直接選取螢幕的文字作測試,本來解析度就不太高,如果原始圖片或pdf上的圖片文字解析度比螢幕高,或字體大小比較大,也許效果會好很多。


簡體中文字的辨識效果:
2014-06-05 18 47 22.png

繁體中文字的辨識效果:
2014-06-05 18 50 19.png

另外在設定的地方(工作列圖示->右鍵->perferences)。可進行一些設定,其中ocr頁面有幾個值得注意的地方:
1)Text Direction選取文字的方向,如果方向錯的話,辨識結果會非常差,要特別注意。
另外ocr-pre-processing如果勾選會增加辨識時間,但辨識度會提高許多。

2014-06-05 18 57 11.png



Google 文件的OCR功能

以上介紹的幾種OCR功能均是借用軟體來完成,但臨時找不到適合的軟體,也可以利用Google雲端硬碟上的OCR功能,它一樣也支持中文文字辨識。
使用方法如下:
前往Google Doc網站:
https://drive.google.com/

按下上傳文件圖示,再選取->檔案

2014-06-15_140028.png


在上傳設定中選擇->「將PDF檔案或圖片,...轉為Google文件」,勾選此一選項將使Google Doc會進行OCR辨識作業。
2014-06-15 11 11 40.png


因為這些圖是我直接從螢幕擷取下來的,可能是解析度太差了,第一次除了原圖檔外,未辨識出出任何文字
2014-06-15 11 28 02.png

第二次我把原來的文字放大一些再進行擷取,結果嚇了一大跳,這是什麼情況,是有辨識出一些文字,但Google文件把字放的超大的,所以也看不出OCR的效果。
2014-06-15 11 28 40.png
我手動把字縮小一些,才看出原貌,這次辨識的很不錯,但不知為何只辨識出一部分,不知是否Google Doc把字放大了,導致辨識不全,因為Google Doc強調它除了能辨識出文字之外,還能辨識出字體,如斜體,粗體等,可能功能太多反而出現不能預期的錯誤。
(除了以上的測試,我還使用不同擷圖軟體,picpick及faststone分別擷取png檔,將dpi品質調高到500,又試過jpeg的品質調為最佳,擷出來的圖檔,都不能辨認,只有把原始文字放大,才認得出來)

2014-06-15 11 29 08.png


免費中文OCR軟體-辨識結果比一比

以下是同一段中文字,使用不同免費ocr軟件的辨識結果,可以見到JOCR真的是最優的,同時也很遺憾Google Doc又未識別出任何文字.可能是這種直接擷取螢幕文字的方式,真的對Google Doc較不能適應吧! (Google Doc我是使用擷圖軟體,分別擷取png檔,將dpi品質調高到500,又試過jpeg的品質調為最佳,擷出來的圖檔,都不能辨認,只有把原始文字放大,才認得出來)
2014-06-15_214021.png

以上就是針對幾個免費的中文ocr軟體的比較與測試報告,希望對大家有所助益。


參考:
The 3 Best Free OCR Tools To Convert Your Files Back Into Editable Documents

Convert screenshots to text with Capture2Text, a free optical character recognition (OCR) app

Read more...

  © Blogger templates Psi by Ourblogtemplates.com 2008

Back to TOP