package dokumenty; import java.io.File; import java.io.IOException; 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); for (int i = 0; i < obrazy.size(); i++) { File plik = obrazy.get(i); 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()); } } } }