package dokumenty; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import pl.wroc.pwr.file.ImageFolderScanner; import pl.wroc.pwr.image.IGSImage; import pl.wroc.pwr.image.IRGBImage; import pl.wroc.pwr.image.converter.RGBtoGSConverter; import pl.wroc.pwr.image.io.GenericLoader; import pl.wroc.pwr.image.io.GenericSaver; import pl.wroc.pwr.image.model.RGBImage; import pl.wroc.pwr.imagechannel.IImageChannel; import pl.wroc.pwr.imagechannel.basic.AddChannel; import pl.wroc.pwr.imagechannel.basic.Invert; import pl.wroc.pwr.imagechannel.basic.Limit; import pl.wroc.pwr.imagechannel.basic.MultiplyValue; import pl.wroc.pwr.imagechannel.basic.SubImageChannel; import pl.wroc.pwr.imagechannel.basic.SubtractChannel; import pl.wroc.pwr.imagechannel.basic.Threshold; import pl.wroc.pwr.imagechannel.basic.Maximize; import pl.wroc.pwr.imagechannel.draw.DrawRectangle; import pl.wroc.pwr.imagechannel.edges.Sobel; import pl.wroc.pwr.imagechannel.filter.ConvolutionOperator; import pl.wroc.pwr.imagechannel.filter.Mask; import pl.wroc.pwr.imagechannel.model.ImageChannel; import pl.wroc.pwr.imagechannel.morphology.BinaryKernel; import pl.wroc.pwr.imagechannel.morphology.GrayscaleDilation; import pl.wroc.pwr.imagechannel.roi.AddROIToChannel; import pl.wroc.pwr.imagechannel.segmentation.SplitSeparate; import pl.wroc.pwr.imagechannel.skeletonization.FastParallelThinning; import pl.wroc.pwr.imagechannel.validation.Compare; import pl.wroc.pwr.roi.IROI; import pl.wroc.pwr.roi.analysis.FindROIBoundingBox; import pl.wroc.pwr.roi.basic.TranslateROI; import pl.wroc.pwr.roi.model.ROIPixels; import pl.wroc.pwr.statistic.Quantile; public class DetektorGrafikiSzkielet { private FindROIBoundingBox bBox = new FindROIBoundingBox(); private AddChannel add = new AddChannel(); private SubtractChannel sub = new SubtractChannel(); private SubImageChannel subC = new SubImageChannel(); private SerializatorElementow serial = new SerializatorElementow(".images"); // private DetektorTabel detektorTabel = new DetektorTabel(); // private float imgThreshold = 0.4f; private int licznik = 0; private IImageChannel przygotujObraz(IImageChannel dataChannel, float imgThreshold) { long point1 = System.currentTimeMillis(); dataChannel = new Invert(dataChannel).apply(); dataChannel = new Threshold(dataChannel, imgThreshold, true).apply(); long point2 = System.currentTimeMillis(); // dataChannel = new FastParallelThinning(dataChannel, 0.1f).apply(); dataChannel = new SzybkiSzkielet(dataChannel, 0.1f).apply(); long point3 = System.currentTimeMillis(); boolean filtrowanie = false; if (filtrowanie) { /* * { IRGBImage test = new RGBImage(dataChannel, dataChannel, * dataChannel); new GenericSaver(test, new * File("/home/mariusz/test1.png")).apply(); } */ IImageChannel filterHChannel = null; IImageChannel filterVChannel = null; int maskSize = 9; /* * Mask maskHorizontal = new Mask(maskSize); Mask maskVertical = new * Mask(maskSize); for (int x = 0; x < maskSize; x++) { for (int y = * 0; y < maskSize; y++) { maskHorizontal.setValue(x, y, y == * maskHorizontal.getXSize() / 2 ? 1 : -1); maskVertical.setValue(x, * y, x == maskHorizontal.getYSize() / 2 ? 1 : -1); } } */ /* * filterHChannel = new * ConvolutionOperator(maskHorizontal).apply(dataChannel); * filterVChannel = new * ConvolutionOperator(maskVertical).apply(dataChannel); * IImageChannel oldChannel = filterVChannel; filterHChannel = new * Threshold(filterHChannel, maskSize - 1, false).apply(); * filterVChannel = new Threshold(filterVChannel, maskSize - 1, * false).apply(); * * { IRGBImage testV = new RGBImage(filterVChannel, filterVChannel, * filterVChannel); new GenericSaver(testV, new * File("/home/mariusz/testOrigV.png")).apply(); IRGBImage testH = * new RGBImage(filterHChannel, filterHChannel, filterHChannel); new * GenericSaver(testH, new * File("/home/mariusz/testOrigH.png")).apply(); } */ filterHChannel = new SzybkiFiltrKrawedziowyPoziomy(maskSize / 2) .apply(dataChannel); filterVChannel = new SzybkiFiltrKrawedziowyPionowy(maskSize / 2) .apply(dataChannel); /* new Compare().apply(oldChannel, filterVChannel); */ filterHChannel = new Threshold(filterHChannel, maskSize - 1, true) .apply(); filterVChannel = new Threshold(filterVChannel, maskSize - 1, true) .apply(); /* * { IRGBImage testV = new RGBImage(filterVChannel, filterVChannel, * filterVChannel); new GenericSaver(testV, new * File("/home/mariusz/testFastV.png")).apply(); IRGBImage testH = * new RGBImage(filterHChannel, filterHChannel, filterHChannel); new * GenericSaver(testH, new * File("/home/mariusz/testFastH.png")).apply(); } */ long point4 = System.currentTimeMillis(); IImageChannel filterChannel = new AddChannel(true).apply( filterHChannel, filterVChannel); filterChannel = new Limit(true).apply(filterChannel); IROI[] fragments = new SplitSeparate(filterChannel).apply(); filterChannel = new ImageChannel(filterChannel.getSize()); for (IROI roi : fragments) { List coords = roi.getROICoordinates(); if (coords.size() > 50) { new AddROIToChannel().apply(filterChannel, roi); } } /* * { IRGBImage test = new RGBImage(filterChannel, filterChannel, * filterChannel); new GenericSaver(test, new * File("/home/mariusz/test2-"+imgThreshold+".png")).apply(); } */ dataChannel = this.sub.apply(dataChannel, filterChannel); } long point5 = System.currentTimeMillis(); // System.out.println("Obcinanie : " + (point2 - point1)); // System.out.println("Szkielet : " + (point3 - point2)); // System.out.println("Filtrowanie: " + (point4 - point3)); // System.out.println("Sumowanie : " + (point5 - point4)); /* * { IRGBImage test = new RGBImage(dataChannel, dataChannel, * dataChannel); new GenericSaver(test, new * File("/home/mariusz/test3.png")).apply(); } */ return dataChannel; } private boolean czyZawiera(int[] box1, int[] box2) { return (box1[0] >= box2[0]) && (box1[1] >= box2[1]) && (box1[0] <= box2[2]) && (box1[1] <= box2[3]); } private boolean czyWewnatrz(int[] box, int x, int y) { return ((x >= box[0]) && (x <= box[2]) && (y >= box[1]) && (y <= box[3])); } private boolean czyPokrywa(int[] box1, int[] box2) { return this.czyWewnatrz(box1, box2[0], box2[1]) || this.czyWewnatrz(box1, box2[0], box2[3]) || this.czyWewnatrz(box1, box2[2], box2[1]) || this.czyWewnatrz(box1, box2[2], box2[3]); } private List filtrujSeparatory(List segmenty) { List filter = new ArrayList(); int filterConst = 5; for (IROI roi : segmenty) { int[] pudelko = this.bBox.apply(roi); float dx = pudelko[2] - pudelko[0]; float dy = pudelko[3] - pudelko[1]; if ((dx / dy < filterConst) && (dy / dx < filterConst)) { filter.add(roi); } } return filter; } private List polaczNachodzace(List segmenty, IImageChannel channel) { int[][] pudelka = new int[segmenty.size()][]; UnionFind union = new UnionFind(segmenty.size()); for (int i = 0; i < segmenty.size(); i++) { pudelka[i] = this.bBox.apply(segmenty.get(i)); } for (int i = 0; i < pudelka.length; i++) { for (int j = i + 1; j < pudelka.length; j++) { if (czyPokrywa(pudelka[i], pudelka[j])) { union.union(j, i); } } } ROIPixels[] rois = new ROIPixels[segmenty.size()]; for (int i = 0; i < pudelka.length; i++) { int number = union.find(i); if (rois[number] == null) { rois[number] = new ROIPixels(channel.getXSize(), channel.getYSize()); } List coords = segmenty.get(i).getROICoordinates(); for (int[] coord : coords) { rois[number].quickAdd(coord[0], coord[1]); } } List finalRois = new ArrayList(); for (ROIPixels roi : rois) { if (roi != null) { finalRois.add(roi); } } return finalRois; } private List odfiltrujPodzbiory(List segmenty) { int[][] pudelka = new int[segmenty.size()][]; boolean[] poprawny = new boolean[segmenty.size()]; for (int i = 0; i < segmenty.size(); i++) { pudelka[i] = this.bBox.apply(segmenty.get(i)); poprawny[i] = true; } for (int i = 0; i < pudelka.length; i++) { int[] pI = pudelka[i]; for (int j = 0; j < pudelka.length; j++) { if (i == j) { continue; } int[] pJ = pudelka[j]; if ((pI[0] <= pJ[0]) && (pI[1] <= pJ[1]) && (pI[2] >= pJ[2]) && (pI[3] >= pJ[3])) { poprawny[j] = false; } } } List wynik = new ArrayList(); for (int i = 0; i < segmenty.size(); i++) { if (poprawny[i]) { wynik.add(segmenty.get(i)); } } return wynik; } public IRGBImage wczytaj(File plik) { if (!plik.exists()) { System.out.println("Plik o zadanej nazwie nie istnieje."); return null; } IRGBImage image = new GenericLoader(plik).apply(); if (image == null) { System.out .println("Błąd odczytu obrazu (plik istnieje). Proszę zmienić format pliku wejściowego."); return null; } return image; } public List przetworz(IRGBImage image, File plik) { if (image == null) { return null; } IGSImage grayImage = new RGBtoGSConverter(image).apply(); this.licznik = 0; return this.przetworz(grayImage.getGrayChannel(), plik, 0, null); } private List przetworz(IImageChannel inputChannel, File plik, int level, float[] stats) { long startPrzyg = System.currentTimeMillis(); // M: tutaj najpierw normalizacja, potem zamiast sta�ych prog�w // wybierzemy progi automatycznie... // IRGBImage test = new RGBImage(inputChannel, inputChannel, // inputChannel); // new GenericSaver(test, new // File("C:\\paradowski\\test1.png")).apply(); // inputChannel = (new Maximize(true)).apply(inputChannel); // test = new RGBImage(inputChannel, inputChannel, inputChannel); // new GenericSaver(test, new // File("C:\\paradowski\\test2.png")).apply(); // System.exit(0); IImageChannel dataChannel1 = this.przygotujObraz(inputChannel, 0.3f); IImageChannel dataChannel2 = this.przygotujObraz(inputChannel, 0.7f); IImageChannel dataChannel = new Limit().apply(this.add.apply( dataChannel1, dataChannel2)); // test = new RGBImage(dataChannel, dataChannel, dataChannel); // new GenericSaver(test, new // File("C:\\paradowski\\test3.png")).apply(); // System.exit(0); long endPrzyg = System.currentTimeMillis(); // System.out.println("Czas przygotowania danych: " + (endPrzyg - // startPrzyg)); /* * { IImageChannel draw = new Invert(dataChannel).apply(); test = new * RGBImage(draw, draw, draw); new GenericSaver(test, new * File("C:\\paradowski\\test4.png")).apply(); } */ // System.exit(0); long startStat = System.currentTimeMillis(); IROI[] segments = new SzybkiSplitSeparate().apply(dataChannel); if (stats == null) { float[] sampleSize = new float[segments.length]; float[] sampleHght = new float[segments.length]; for (int i = 0; i < segments.length; i++) { IROI segment = segments[i]; List coords = segment.getROICoordinates(); int[] box = this.bBox.apply(segment); int height = box[3] - box[1]; sampleSize[i] = coords.size(); sampleHght[i] = height; } float valueSize = new Quantile(0.99f).apply(sampleSize) * 3; float valueHght = new Quantile(0.99f).apply(sampleHght) * 2; stats = new float[2]; stats[0] = valueSize; stats[1] = valueHght; // System.out.println("Size threshold : " + valueSize); // System.out.println("Height threshold: " + valueHght); } long endStat = System.currentTimeMillis(); // System.out.println("Statystyki: " + (endStat - startStat)); long startKlas = System.currentTimeMillis(); List imageROIS = new ArrayList(); List boxes = new ArrayList(); for (IROI segment : segments) { int[] box = this.bBox.apply(segment); int height = box[3] - box[1]; int width = box[2] - box[0]; List coords = segment.getROICoordinates(); if ((coords.size() > stats[0]) && (height > stats[1]) && (width > 4)) { imageROIS.add(segment); boxes.add(box); } } long endKlas = System.currentTimeMillis(); // System.out.println("Klasyfikacja: " + (endKlas - startKlas)); long startDylacja = System.currentTimeMillis(); dataChannel = new GrayscaleDilation(dataChannel, new BinaryKernel(2)) .apply(); long endDylacja = System.currentTimeMillis(); // System.out.println("Dylacja: " + (endDylacja - startDylacja)); dataChannel = new AddChannel().apply( new Sobel(0.1f).apply(inputChannel), dataChannel); dataChannel = new Limit().apply(dataChannel); /* * { IRGBImage test = new RGBImage(dataChannel, dataChannel, * dataChannel); new GenericSaver(test, new * File("/home/mariusz/test4.png")).apply(); } */ long startLaczenie = System.currentTimeMillis(); /* * IImageChannel boxImage = new ImageChannel(dataChannel.getXSize(), * dataChannel.getYSize()); for (int[] box : boxes) { new * DrawRectangle(1, true).apply(boxImage, box); } */ IROI[] dilatedSegments = new SzybkiSplitSeparate().apply(dataChannel); // System.out.println("Liczba segmentow: " + dilatedSegments.length); List filteredROIS = new ArrayList(); for (IROI segment : dilatedSegments) { List coords = segment.getROICoordinates(); boolean hit = false; for (int[] coord : coords) { /* * if (boxImage.getValue(coord) > 0.5f) { hit = true; break; } */ for (int[] box : boxes) { if (czyZawiera(coord, box)) { hit = true; break; } } if (hit) { break; } } if (hit) { filteredROIS.add(segment); } } long endLaczenie = System.currentTimeMillis(); // System.out.println("Laczenie segmentow: " + (endLaczenie - // startLaczenie)); long startFiltrowanie = System.currentTimeMillis(); filteredROIS = this.odfiltrujPodzbiory(filteredROIS); long endFiltrowanie = System.currentTimeMillis(); // System.out.println("Filtrowanie segmentow: " + (endFiltrowanie - // startFiltrowanie)); // int size = 0; // do { // size = filteredROIS.size(); long startNachodzenie = System.currentTimeMillis(); // filteredROIS = this.polaczNachodzace(filteredROIS, dataChannel); // filteredROIS = this.polaczNachodzace(filteredROIS, dataChannel); int ROINumber = 0; do { ROINumber = filteredROIS.size(); System.out.println("Przed laczeniem: " + ROINumber); filteredROIS = this.polaczNachodzace(filteredROIS, dataChannel); System.out.println("Po laczeniu: " + filteredROIS.size()); } while (ROINumber != filteredROIS.size()); filteredROIS = this.filtrujSeparatory(filteredROIS); long endNachodzenie = System.currentTimeMillis(); System.out.println("Laczenie nachodzacych: " + (endNachodzenie - startNachodzenie)); // } while (filteredROIS.size() == size); List wygenerowaneRegiony = new ArrayList(); for (IROI region : filteredROIS) { int[] box = this.bBox.apply(region); boolean zapisany = false; { // detektor odwrocenia /* * if ((box[0] > 2)||(box[1] > 2)||(box[2] < * dataChannel.getXSize() - 2)||(box[3] < dataChannel.getYSize() * - 2)) { int[] mniejszePud = new int[]{box[0] + 2, box[1] + 2, * box[2] - 2, box[3] - 2}; IImageChannel fragment = * this.subC.apply(inputChannel, mniejszePud); IImageChannel * inwersja = new Invert(fragment).apply(); List * subRois = this.przetworz(inwersja, plik, level + 1, stats); * if (subRois.size() > 1) { //wygenerowaneRegiony.add(new * Element("Inversion", box)); for (Element subRoi : subRois) { * subRoi.pobierzPudelko()[0] += mniejszePud[0] - 2; * subRoi.pobierzPudelko()[1] += mniejszePud[1] - 2; * subRoi.pobierzPudelko()[2] += mniejszePud[0] + 2; * subRoi.pobierzPudelko()[3] += mniejszePud[1] + 2; * * wygenerowaneRegiony.add(subRoi); zapisany = true; } continue; * } } */ } if (!zapisany) { // standardowy obraz wygenerowaneRegiony.add(new Element("Image", box)); } } return wygenerowaneRegiony; } public void narysujZaznaczenie(List regionyGrafiki, IRGBImage obraz, File plik) { for (Element segment : regionyGrafiki) { int[] box = segment.pobierzPudelko(); String typ = segment.pobierzTyp(); if (typ.equals("Image")) { new DrawRectangle(0.3f, false) .apply(obraz.getRedChannel(), box); new DrawRectangle(1.0f, false).apply(obraz.getGreenChannel(), box); new DrawRectangle(0.3f, false).apply(obraz.getBlueChannel(), box); } ; if (typ.equals("Inversion")) { new DrawRectangle(1.0f, false) .apply(obraz.getRedChannel(), box); new DrawRectangle(0.3f, false).apply(obraz.getGreenChannel(), box); new DrawRectangle(0.3f, false).apply(obraz.getBlueChannel(), box); } ; } IRGBImage output = obraz; String nazwa = plik.getName(); new GenericSaver(output, new File( "/home/mariusz/dane/nekst/wyniki-obrazy", nazwa)).apply(); } public void wytnijObrazy(List regionyGrafiki, IRGBImage obraz, File plik, File outputFolder) { for (int i = 0; i < regionyGrafiki.size(); i++) { Element segment = regionyGrafiki.get(i); int[] box = segment.pobierzPudelko(); String typ = segment.pobierzTyp(); if (typ.equals("Image")) { IImageChannel red = this.subC.apply(obraz.getRedChannel(), box); IImageChannel green = this.subC.apply(obraz.getGreenChannel(), box); IImageChannel blue = this.subC.apply(obraz.getBlueChannel(), box); IRGBImage output = new RGBImage(red, green, blue); String nazwa = plik.getName().toLowerCase(); nazwa = nazwa.replace(".png", "_" + i + ".png"); File plikWyjsciowy = new File(outputFolder, nazwa); if (!new GenericSaver(output, plikWyjsciowy).apply()) { System.out.println("Blad zapisu wycietego obrazu."); } } } } public static void main(String[] args) { System.out .println("Detektor grafiki (projekt Nekst), wersja: 18.07.2011."); // System.out.println("Autor: Mariusz Paradowski"); if (args.length < 1) { System.out.println(); System.out .println("Brakuje parametrow wywolania aplikacji. Poprawne wywolanie:"); System.out .println("DetektorGrafiki.jar [folder-wejsciowy] [folder-wyjsciowy]"); System.out.println(); System.out .println("Aplikacja wyszukuje wszystkie obrazy w formacie PNG w katalogu [folder-wejsciowy]."); System.out .println("Kazdy z obrazow traktowany jest jako pojedyncza strona dokumentu."); System.out .println("Wszystkie znalezione elementy graficzne sa zapisywanie w katalogu [folder-wyjsciowy]."); System.out .println("Dodatkowo tworzony jest plik tekstowy okreslajacy lokalizacje elementow graficznych"); System.out.println("w oryginalnym dokumencie."); return; } String pathIn = args[0]; String pathOut = pathIn; if (args.length > 1) { pathOut = args[1]; } File folderIn = new File(pathIn); File folderOut = new File(pathOut); if (!folderIn.exists()) { System.out.println("Wskazana sciezka wejsciowa (" + pathIn + ") nie istnieje."); return; } if (!folderOut.exists()) { System.out.println("Wskazana sciezka wyjsciowa (" + pathOut + ") nie istnieje."); return; } DetektorGrafikiSzkielet detektor = new DetektorGrafikiSzkielet(); List obrazy = null; if (folderIn.isDirectory()) { obrazy = new ImageFolderScanner().apply(folderIn); } else { obrazy = new ArrayList(); obrazy.add(folderIn); } System.out.println("Odnaleziono " + obrazy.size() + " obrazow"); boolean pomijanie = true; System.out.println("Pomijanie przetworzonych plikow: " + pomijanie); File log = new File("DetectingImage.log"); PrintWriter logPW = null; try { logPW = new PrintWriter(new FileOutputStream(log,true)); } catch (FileNotFoundException e) { e.printStackTrace(); } for (int i = 0; i < obrazy.size(); i++) { File plik = obrazy.get(i); try { int procent = (100 * i) / obrazy.size(); System.out.println("Przetwarzam " + plik.getName() + " (" + procent + "%)"); File wyjscie = new File(folderOut, plik.getName()); if (detektor.serial.czyIstnieje(wyjscie) && pomijanie) { System.out.println("Plik wyjsciowy juz istnieje. Pomijam."); continue; } long fileLoadStart = System.currentTimeMillis(); IRGBImage obraz = detektor.wczytaj(plik); long fileLoadEnd = System.currentTimeMillis(); // System.out.println("Wczytywanie pliku : " + (fileLoadEnd - // fileLoadStart)); long start = System.currentTimeMillis(); List regiony = detektor.przetworz(obraz, plik); System.out.println(" Znalazlem " + regiony.size() + " obrazow."); long end = System.currentTimeMillis(); // System.out.println("Czas przetwarzania: " + (end - start)); if (regiony != null) { detektor.serial.zapisz(regiony, new File(folderOut, plik.getName())); // detektor.wytnijObrazy(regiony, obraz, plik, folderOut); // detektor.narysujZaznaczenie(regiony, obraz, plik); } else { System.out.println("Nastapil blad detekcji dla obrazu " + plik.getPath()); } } catch (Exception ex) { System.out.println("Nie udalo sie przetworzyc pliku. Sprawdz logi"); logPW.write("Nie udalo sie przetworzyc pliku: " + plik.getPath() +"\t" +ex.getMessage() + " \n"); } } logPW.close(); } }