blob: aa16e27c84d320d6c69a4d2d1022837fdad0767a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008 IBM Corporation and Others
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Junji MAEDA - initial API and implementation
*******************************************************************************/
package org.eclipse.actf.visualization.engines.lowvision.checker;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Stack;
import java.util.Vector;
import org.eclipse.actf.visualization.engines.lowvision.LowVisionCommon;
import org.eclipse.actf.visualization.engines.lowvision.LowVisionType;
import org.eclipse.actf.visualization.engines.lowvision.character.CharacterMS;
import org.eclipse.actf.visualization.engines.lowvision.character.CharacterSM;
import org.eclipse.actf.visualization.engines.lowvision.character.CharacterSS;
import org.eclipse.actf.visualization.engines.lowvision.color.ColorIRGB;
import org.eclipse.actf.visualization.engines.lowvision.image.BinaryImage;
import org.eclipse.actf.visualization.engines.lowvision.image.ConnectedComponent;
import org.eclipse.actf.visualization.engines.lowvision.image.Container;
import org.eclipse.actf.visualization.engines.lowvision.image.ImageException;
import org.eclipse.actf.visualization.engines.lowvision.image.Int2D;
import org.eclipse.actf.visualization.engines.lowvision.image.PageImage;
import org.eclipse.actf.visualization.engines.lowvision.image.Topology;
import org.eclipse.actf.visualization.engines.lowvision.operator.LowVisionFilter;
import org.eclipse.actf.visualization.engines.lowvision.problem.BlurProblem;
import org.eclipse.actf.visualization.engines.lowvision.problem.ColorProblem;
import org.eclipse.actf.visualization.engines.lowvision.problem.LowVisionProblem;
import org.eclipse.actf.visualization.engines.lowvision.problem.LowVisionProblemException;
import org.eclipse.actf.visualization.engines.lowvision.problem.LowVisionProblemGroup;
import org.eclipse.actf.visualization.engines.lowvision.util.DecisionMaker;
public class CharacterChecker implements LowVisionCommon {
// separated from PageImage
private PageImage pageImage;
public CharacterChecker(PageImage pageImage) {
this.pageImage = pageImage;
}
private class CompareByPriority implements Comparator<LowVisionProblem> {
public int compare(LowVisionProblem _o1, LowVisionProblem _o2) {
return (_o2.getPriority() - _o1.getPriority());
}
}
// handle PageImage as an image (without considering HTML char/image char)
public LowVisionProblemGroup[] checkAllCharacters(LowVisionType _lvType)
throws ImageException, LowVisionProblemException {
if (_lvType.countTypes() == 0) {
return (new LowVisionProblemGroup[0]);
}
Vector<LowVisionProblem> problemVec = new Vector<LowVisionProblem>();
// container
for (int k = 0; k < pageImage.getNumContainers(); k++) {
Container cont = pageImage.getContainers()[k];
Vector<LowVisionProblem> tmpProblemVec = new Vector<LowVisionProblem>();
// Problem -> ProblemGroup
for (int l = 0; l < cont.getNumSSCharacters(); l++) {
LowVisionProblem prob = checkOneSSCharacter(cont
.getSsCharacters()[l], _lvType);
if (prob != null) {
tmpProblemVec.addElement(prob);
}
}
for (int l = 0; l < cont.getNumMSCharacters(); l++) {
LowVisionProblem prob = checkOneMSCharacter(cont
.getMsCharacters()[l], _lvType);
if (prob != null) {
tmpProblemVec.addElement(prob);
}
}
for (int l = 0; l < cont.getNumSMCharacters(); l++) {
LowVisionProblem prob = checkOneSMCharacter(cont
.getSmCharacters()[l], _lvType);
if (prob != null) {
tmpProblemVec.addElement(prob);
}
}
Vector curProblemVec = collectProblems(tmpProblemVec);
if (curProblemVec == null) {
continue;
}
int curSize = curProblemVec.size();
for (int m = 0; m < curSize; m++) {
problemVec.addElement((LowVisionProblem) (curProblemVec
.elementAt(m)));
}
// /* generate problems for each character*/
// for( int l=0; l<cont.numSSCharacters; l++ ){
// Problem prob = checkOneSSCharacter( cont.ssCharacters[l], _lvType
// );
// if( prob != null ){
// problemVec.addElement( prob );
// }
// }
// for( int l=0; l<cont.numMSCharacters; l++ ){
// Problem prob = checkOneMSCharacter( cont.msCharacters[l], _lvType
// );
// if( prob != null ){
// problemVec.addElement( prob );
// }
// }
// for( int l=0; l<cont.numSMCharacters; l++ ){
// Problem prob = checkOneSMCharacter( cont.smCharacters[l], _lvType
// );
// if( prob != null ){
// problemVec.addElement( prob );
// }
// }
}
// SM Character (outside container)
Vector<LowVisionProblem> tmpProblemVec = new Vector<LowVisionProblem>();
for (int k = 0; k < pageImage.getNumNonContainedCharacters(); k++) {
LowVisionProblem prob = checkOneSMCharacter(pageImage
.getNonContainedCharacters()[k], _lvType);
if (prob != null) {
tmpProblemVec.addElement(prob);
}
}
Vector curProblemVec = collectProblems(tmpProblemVec);
if (curProblemVec != null) {
int curSize = curProblemVec.size();
for (int m = 0; m < curSize; m++) {
problemVec.addElement((LowVisionProblem) (curProblemVec
.elementAt(m)));
}
}
Collections.sort(problemVec, new CompareByPriority());
LowVisionProblemGroup[] problemArray = null;
int problemVecSize = problemVec.size();
if (problemVecSize > 0) {
problemArray = new LowVisionProblemGroup[problemVecSize];
for (int k = 0; k < problemVecSize; k++) {
problemArray[k] = (LowVisionProblemGroup) (problemVec
.elementAt(k));
}
problemVec.removeAllElements();
return (problemArray);
} else {
return (new LowVisionProblemGroup[0]);
}
}
// Simulate and check (MS Char)
private LowVisionProblem simulateAndCheckMSCharacter(CharacterMS _msc,
LowVisionType _lvType, int _bg) throws ImageException,
LowVisionProblemException {
/* Color change-> already simulated & checked*/
if (!_lvType.doBlur()) {
return (null);
}
// add margin (for Blur filter)
int margin = 0;
if (_lvType.getEyesight()) {
margin = _lvType.getEyesightRadius();
}
Int2D beforeI2d = _msc.makeMarginedImage(margin * 2);
// simulation
LowVisionFilter lvFilter = new LowVisionFilter(_lvType);
Int2D afterI2d = null;
try {
afterI2d = new Int2D(lvFilter.filter(beforeI2d.toBufferedImage(),
null));
} catch (Exception e) {
// e.printStackTrace();
throw new ImageException(
"Error occurred while simulating an MSCharacter.");
}
/*
* Create BinaryImage from simulated image (with 1 margin)
* (cut most outside margin <- blacken by BlurOp)
*/
int afterWidth = afterI2d.width - 2 * margin;
int afterHeight = afterI2d.height - 2 * margin;
BinaryImage bin = new BinaryImage(afterWidth, afterHeight);
HashMap<Integer, Boolean> answerMap = new HashMap<Integer, Boolean>();
try {
for (int j = 0; j < afterHeight; j++) {
for (int i = 0; i < afterWidth; i++) {
int pixel = afterI2d.data[j + margin][i + margin];
if (pixel == _bg) {
continue;
} else {
Integer pixelInt = new Integer(pixel);
Boolean answerBool = (Boolean) (answerMap.get(pixelInt));
if (answerBool == null) {
if (DecisionMaker.distinguishableTextColors(pixel,
_bg)) {
bin.data[j][i] = 1;
answerMap.put(pixelInt, new Boolean(true));
} else {
answerMap.put(pixelInt, new Boolean(false));
}
} else {
if (answerBool.booleanValue()) {
bin.data[j][i] = 1;
}
}
}
}
}
} catch (Exception e) {
// e.printStackTrace();
throw new ImageException(
"Error occurred while making binary image.");
}
int ccLeft = _msc.cc.getLeft() - margin;
if (ccLeft < 0)
ccLeft = 0;
int ccTop = _msc.cc.getTop() - margin;
if (ccTop < 0)
ccTop = 0;
ConnectedComponent cc = new ConnectedComponent(ccLeft, ccTop, bin,
ConnectedComponent.CONNECTIVITY_8);
Topology simTopo = cc.thinning().calcTopology();
if (simTopo.match(_msc.getTopology())) {
return (null);
} else {
double probability = 1.0;
if (_msc.getTopology().getCount() > 0) {
probability = (double) (Math.abs(_msc.getTopology().getCount()
- simTopo.getCount()))
/ (double) (_msc.getTopology().getCount());
if (probability > 1.0) {
probability = 1.0;
}
}
return (new BlurProblem(_msc, _lvType, probability));
}
}
// Simulate and check (SM Char)
private LowVisionProblem simulateAndCheckSMCharacter(CharacterSM _smc,
LowVisionType _lvType, int _fg) throws ImageException,
LowVisionProblemException {
/* Color change-> already simulated & checked*/
if (!_lvType.doBlur()) {
return (null);
}
// add margin (for Blur filter)
int margin = 0;
if (_lvType.getEyesight()) {
margin = _lvType.getEyesightRadius();
}
Int2D beforeI2d = _smc.makeMarginedImage(margin * 2);
// simulation
LowVisionFilter lvFilter = new LowVisionFilter(_lvType);
Int2D afterI2d = null;
try {
afterI2d = new Int2D(lvFilter.filter(beforeI2d.toBufferedImage(),
null));
} catch (Exception e) {
// e.printStackTrace();
throw new ImageException(
"Error occurred while simulating an SMCharacter.");
}
/*
* Create BinaryImage from simulated image (with 1 margin)
* (cut most outside margin <- blacken by BlurOp)
*/
int afterWidth = afterI2d.width - 2 * margin;
int afterHeight = afterI2d.height - 2 * margin;
BinaryImage bin = new BinaryImage(afterWidth, afterHeight);
HashMap<Integer, Boolean> answerMap = new HashMap<Integer, Boolean>();
try {
for (int j = 0; j < afterHeight; j++) {
for (int i = 0; i < afterWidth; i++) {
int pixel = afterI2d.data[j + margin][i + margin];
if (pixel == _fg) {
bin.data[j][i] = 1;
continue;
} else {
Integer pixelInt = new Integer(pixel);
Boolean answerBool = (Boolean) (answerMap.get(pixelInt));
if (answerBool == null) {
if (!(DecisionMaker.distinguishableTextColors(
pixel, _fg))) {
bin.data[j][i] = 1;
answerMap.put(pixelInt, new Boolean(false));
} else {
answerMap.put(pixelInt, new Boolean(true));
}
} else {
if (!(answerBool.booleanValue())) {
bin.data[j][i] = 1;
}
}
}
}
}
} catch (Exception e) {
// e.printStackTrace();
throw new ImageException(
"Error occurred while making binary image.");
}
int ccLeft = _smc.cc.getLeft() - margin;
if (ccLeft < 0)
ccLeft = 0;
int ccTop = _smc.cc.getTop() - margin;
if (ccTop < 0)
ccTop = 0;
ConnectedComponent cc = new ConnectedComponent(ccLeft, ccTop, bin,
ConnectedComponent.CONNECTIVITY_8);
Topology simTopo = cc.thinning().calcTopology();
if (simTopo.match(_smc.getTopology())) {
return (null);
} else {
double probability = 1.0;
if (_smc.getTopology().getCount() > 0) {
probability = (double) (Math.abs(_smc.getTopology().getCount()
- simTopo.getCount()))
/ (double) (_smc.getTopology().getCount());
if (probability > 1.0) {
probability = 1.0;
}
}
return (new BlurProblem(_smc, _lvType, probability));
}
}
// Simulate and check (SS Char)
private LowVisionProblem simulateAndCheckSSCharacter(CharacterSS _ssc,
LowVisionType _lvType, int _bg) throws ImageException,
LowVisionProblemException {
// add margin
int margin = 0;
if (_lvType.getEyesight()) {
margin = _lvType.getEyesightRadius();
}
Int2D beforeI2d = _ssc.makeMarginedImage(margin * 2);
// simulation
LowVisionFilter lvFilter = new LowVisionFilter(_lvType);
Int2D afterI2d = null;
try {
afterI2d = new Int2D(lvFilter.filter(beforeI2d.toBufferedImage(),
null));
} catch (Exception e) {
// e.printStackTrace();
throw new ImageException(
"Error occurred while simulating an SSCharacter.");
}
/*
* Create BinaryImage from simulated image (with 1 margin) (cut most
* outside margin <- blacken by BlurOp)
*/
int afterWidth = afterI2d.width - 2 * margin;
int afterHeight = afterI2d.height - 2 * margin;
BinaryImage bin = new BinaryImage(afterWidth, afterHeight);
HashMap<Integer, Boolean> answerMap = new HashMap<Integer, Boolean>();
try {
for (int j = 0; j < afterHeight; j++) {
for (int i = 0; i < afterWidth; i++) {
int pixel = afterI2d.data[j + margin][i + margin];
if (pixel == _bg) {
continue;
} else {
Integer pixelInt = new Integer(pixel);
Boolean answerBool = (Boolean) (answerMap.get(pixelInt));
if (answerBool == null) {
if (DecisionMaker.distinguishableTextColors(pixel,
_bg)) {
bin.data[j][i] = 1;
answerMap.put(pixelInt, new Boolean(true));
} else {
answerMap.put(pixelInt, new Boolean(false));
}
} else if (answerBool.booleanValue()) {
bin.data[j][i] = 1;
}
}
}
}
} catch (Exception e) {
// e.printStackTrace();
throw new ImageException(
"Error occurred while making binary image.");
}
int ccLeft = _ssc.cc.getLeft() - margin;
if (ccLeft < 0)
ccLeft = 0;
int ccTop = _ssc.cc.getTop() - margin;
if (ccTop < 0)
ccTop = 0;
ConnectedComponent cc = new ConnectedComponent(ccLeft, ccTop, bin,
ConnectedComponent.CONNECTIVITY_8);
Topology simTopo = cc.thinning().calcTopology();
if (simTopo.match(_ssc.getTopology())) {
return (null);
} else {
double probability = 1.0;
if (_ssc.getTopology().getCount() > 0) {
probability = (double) (Math.abs(_ssc.getTopology().getCount()
- simTopo.getCount()))
/ (double) (_ssc.getTopology().getCount());
if (probability > 1.0) {
probability = 1.0;
}
}
return (new BlurProblem(_ssc, _lvType, probability));
}
}
private LowVisionProblem checkOneMSCharacter(CharacterMS _msc,
LowVisionType _lvType) throws ImageException,
LowVisionProblemException {
int simBgColor = _msc.getBackgroundColor();
// Int2D i2d = _msc.getInt2D();
if (_lvType.doChangeColors()) {
try {
simBgColor = _lvType.convertColor(_msc.getBackgroundColor());
int simFgColor = _lvType
.convertColor(_msc.getForegroundColor());
double sev = W3CColorChecker.calcSeverity(new ColorIRGB(
simFgColor), new ColorIRGB(simBgColor));
if (sev > 0.0) {
return (new ColorProblem(_msc, _lvType, sev));
}
} catch (Exception e) {
// e.printStackTrace();
}
}
if (_lvType.doBlur()) {
return (simulateAndCheckMSCharacter(_msc, _lvType, simBgColor));
}
return (null);
}
private LowVisionProblem checkOneSMCharacter(CharacterSM _smc,
LowVisionType _lvType) throws ImageException,
LowVisionProblemException {
int simFgColor = _smc.getForegroundColor();
// Int2D i2d = _smc.getInt2D();
if (_lvType.doChangeColors()) {
try {
simFgColor = _lvType.convertColor(_smc.getForegroundColor());
// count bad bg pixels
int badCount = 0;
int w = _smc.getWidth();
int h = _smc.getHeight();
int[][] im = _smc.getImage();
byte[][] data = _smc.cc.getShape().getData();
HashMap<Integer, Boolean> map = new HashMap<Integer, Boolean>();
for (int j = 0; j < h; j++) {
for (int i = 0; i < w; i++) {
if (data[j][i] == 0) {
int simBg = _lvType.convertColor(im[j][i]);
Integer bgInt = new Integer(simBg);
Boolean bgBool = (Boolean) (map.get(bgInt));
if (bgBool == null) { // first time
if (!(DecisionMaker.distinguishableTextColors(
simFgColor, simBg))) {
badCount++;
map.put(bgInt, new Boolean(false));
} else {
map.put(bgInt, new Boolean(true));
}
} else {
if (!(bgBool.booleanValue())) {
badCount++;
}
}
}
}
}
double badRatio = (double) badCount
/ (double) (w * h - _smc.cc.getCount());
if (badRatio >= THRESHOLD_MIN_SM_COLOR_PROBLEM_RATIO) {
double probability = (badRatio - THRESHOLD_MIN_SM_COLOR_PROBLEM_RATIO)
/ (1.0 - THRESHOLD_MIN_SM_COLOR_PROBLEM_RATIO);
return (new ColorProblem(_smc, _lvType, probability));
}
} catch (Exception e) {
// e.printStackTrace();
}
}
if (_lvType.doBlur()) {
return (simulateAndCheckSMCharacter(_smc, _lvType, simFgColor));
}
return (null);
}
private LowVisionProblem checkOneSSCharacter(CharacterSS _ssc,
LowVisionType _lvType) throws ImageException,
LowVisionProblemException {
int simBgColor = _ssc.getBackgroundColor();
if (_lvType.doChangeColors()) {
try {
int simFgColor = _lvType
.convertColor(_ssc.getForegroundColor());
simBgColor = _lvType.convertColor(_ssc.getBackgroundColor());
double sev = W3CColorChecker.calcSeverity(new ColorIRGB(
simFgColor), new ColorIRGB(simBgColor));
if (sev > 0.0) {
return (new ColorProblem(_ssc, _lvType, sev));
}
} catch (Exception e) {
// e.printStackTrace();
throw new ImageException(
"An error occurred while checking colors of an SSCharacter.");
}
}
if (_lvType.doBlur()) {
return (simulateAndCheckSSCharacter(_ssc, _lvType, simBgColor));
}
return (null);
}
/* problem grouping */
private Vector collectProblems(Vector<LowVisionProblem> _tmpVec)
throws ImageException {
int size = _tmpVec.size();
if (size == 0) {
return (null);
}
int[] idMap = new int[size];
int id = 1;
for (int i = 0; i < size; i++) {
LowVisionProblem curProb = _tmpVec.elementAt(i);
if (idMap[i] == 0) { // not yet
idMap[i] = id;
assignProblemGroupID(curProb, id, _tmpVec, size, idMap);
id++;
}
}
Vector<LowVisionProblemGroup> resultVec = new Vector<LowVisionProblemGroup>();
for (int i = 1; i < id; i++) {
makeProblemGroupByID(resultVec, i, _tmpVec, size, idMap);
}
return (resultVec);
}
private void assignProblemGroupID(LowVisionProblem _curProb, int _id,
Vector<LowVisionProblem> _tmpVec, int _size, int[] _idMap)
throws ImageException {
try {
Stack<LowVisionProblem> searchStack = new Stack<LowVisionProblem>();
searchStack.push(_curProb);
while (!searchStack.empty()) {
LowVisionProblem sProb = searchStack.pop();
for (int i = 0; i < _size; i++) {
if (_idMap[i] == 0) {
LowVisionProblem tmpProb = _tmpVec.elementAt(i);
if (DecisionMaker.areSameGroupProblems(sProb, tmpProb)) {
_idMap[i] = _id;
searchStack.push(tmpProb);
}
}
}
}
} catch (LowVisionProblemException lvpe) {
// lvpe.printStackTrace();
throw new ImageException(
"Error occurred while making problem group.");
}
}
private void makeProblemGroupByID(Vector<LowVisionProblemGroup> _resultVec,
int _id, Vector<LowVisionProblem> _tmpVec, int _size, int[] _idMap)
throws ImageException {
Vector<LowVisionProblem> groupVector = new Vector<LowVisionProblem>();
for (int i = 0; i < _size; i++) {
if (_idMap[i] == _id) {
LowVisionProblem curProb = _tmpVec.elementAt(i);
groupVector.addElement(curProb);
}
}
int groupSize = groupVector.size();
if (groupSize <= 0) {
throw new ImageException("No instance belongs to the group. id = "
+ _id);
}
LowVisionProblemGroup pg = null;
try {
pg = new LowVisionProblemGroup(groupVector);
} catch (LowVisionProblemException lvpe) {
// lvpe.printStackTrace();
throw new ImageException(
"LowVisionProblemGroup cannot be constracted.");
}
_resultVec.addElement(pg);
}
}