Sunday, June 18, 2017

Java 9'da Görüntü Dosyaları ile Çalışmak


Java 9 ile birlikte görüntü dosyalarına erişim ile ilgili olarak iki önemli yenilik bizi bekliyor:
  • Image IO paketi ile PNG ve JPEG formatlarında görüntü dosyalarını okuyup, yazabiliyorduk. Artık buna çok sayfalı TIFF formatı da eklendi. Bu tür görüntü dosyalarını artık Java Advanced Imaging gibi üçüncü parti bir kütüphane kullanmadan erişebiliyoruz.
  • İkinci yenilik ise bir görüntünün farklı çözünürlükteki kopyaları ile çalışmayı kolaylaştırıyor. Uygulama içinden elimizdeki ekran ya da görüntü işleme algoritması için en uygun çözünürlükteki görüntüye basit bir şekilde ulaşabiliyoruz. 
Örnek uygulamada Roma'nın farklı çözünürlükte çekilmiş görüntüleri ile çalışacağız:
  • rome-1.jpg: 4098 x 4177
  • rome-2.jpg: 2049 x 2089
  • rome-3.jpg: 1025 x 1045
  • rome-4.jpg: 513 x 523

package com.example.java9.features;

import java.awt.Image;
import java.awt.image.BaseMultiResolutionImage;
import java.awt.image.BufferedImage;
import java.awt.image.MultiResolutionImage;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.imageio.ImageIO;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class MultiResolutiomImageExample {
    public static void main(String[] args) {
        List<String> imageFileNames = List.of("rome-4.jpg","rome-3.jpg","rome-2.jpg","rome-1.jpg");
        final Function<String, BufferedImage> mapToBufferedImage = file -> {
            try {
                return ImageIO.read(new File("build/classes",file));
            } catch (IOException e) {
                return null;
            }
        };
        List<BufferedImage> images = imageFileNames.stream()
                                                   .map(mapToBufferedImage)
                                                   .filter(Objects::nonNull)
                                                   .collect(Collectors.toList());
        System.err.println("images.size():"+images.size());
        MultiResolutionImage multiResolutionImage = new BaseMultiResolutionImage(images.toArray(new Image[0]));
        List<Image> imageVariants = multiResolutionImage.getResolutionVariants();
        System.out.println("number of images: " + imageVariants.size());        
        imageVariants.forEach(System.out::println);
        final Image image1 = multiResolutionImage.getResolutionVariant(1024, 768);
        System.out.println(image1);
        final Image image2 = multiResolutionImage.getResolutionVariant(2049, 2089);
        System.out.println(image2);
       
    }
}

Yukarıda örnek uygulamada Roma'nın dört farklı çözünürlükteki görüntüsünü dosyalardan okuduktan sonra MultiResolutionImage sınıfının yönetimine bırakıyoruz. MultiResolutionImage kurucu fonksiyonu bu görüntüleri BufferedImage tipinden bir dizide en düşük çözünürlükten en yüksek çözünürlüğe doğru sıralı olacak şekilde bizden bekliyor. En uygun çözünürlükte görüntüye ulaşmak için ise MultiResolutionImage sınıfının getResolutionVariant() metodunu kullanıyoruz:
  • multiResolutionImage.getResolutionVariant(1024, 768) çağrısı 1025 x 1045 çözünürlüğe sahip görüntüyü dönerken 
  • multiResolutionImage.getResolutionVariant(2049,2089) çağrısı 2049 x 2089 çözünürlüğe sahip görüntüyü döndü.
Şimdi ise çok sayfalı TIFF dokümanı okuyan basit bir uygulama geliştireceğiz. Bu uygulamada kullanacağımız çok sayfalı TIFF, bir önceki örnekte kullandığımız yukarıda değerleri verilen dört farklı çözünürlükteki görüntüyü içeriyor.

package com.example.java9.features;

import java.awt.Image;
import java.awt.image.BaseMultiResolutionImage;
import java.awt.image.BufferedImage;
import java.awt.image.MultiResolutionImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.*;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class MultiPageTiffExample {

    public static ImageReader tiffReader;

    public static Optional<BufferedImage> read(int index) {
        try {
            return Optional.of(tiffReader.read(index));
        } catch (IOException ex) {
            return Optional.empty();
        }
    }

    public static void main(String[] args) throws FileNotFoundException, IOException {
        InputStream stream = new FileInputStream(new File("build/classes", "/image.tif"));
        tiffReader = ImageIO.getImageReadersByFormatName("tiff").next();

        ImageInputStream input = ImageIO.createImageInputStream(stream);

        tiffReader.setInput(input);

        final int numOfImages = tiffReader.getNumImages(true);
        System.out.println("# of pages: " + numOfImages);

        List<BufferedImage> images = IntStream.range(0, numOfImages)
                .mapToObj(MultiPageTiffExample::read)
                .filter(Optional::isPresent)
                .flatMap(Optional::stream)
                .collect(Collectors.toList());
        MultiResolutionImage multiResolutionImage = new BaseMultiResolutionImage(images.toArray(new Image[0]));
        List<Image> imageVariants = multiResolutionImage.getResolutionVariants();
        System.out.println("number of images: " + imageVariants.size());
        imageVariants.forEach(System.out::println);
        final Image image1 = multiResolutionImage.getResolutionVariant(1024, 768);
        System.out.println(image1);
        final Image image2 = multiResolutionImage.getResolutionVariant(2049, 2089);
        System.out.println(image2);
    }
}

TIFF formatında görüntü dosyalarını okumak için kullanacağımız nesneyi ImageIO.getImageReadersByFormatName("tiff").next() ile alıyoruz.  Uygulamaların kodlarına bu bağlantıdan ulaşabilirsiniz.

Sunday, June 11, 2017

Javascript'de FilterMapReduce Kullanımı


Javascript, HTML 5 ile birlikte istemci tarafta, web tarayıcıda uygulama geliştirmek için standard bir dile dönüştü. HTML5 ile gelen WebSocket APILocal Storage APIBattery Status APIFullScreen APICanvas APIGeolocation APIVibration APIMultimedia APILocal Storage APIFile APIWebGL APIDrag-and-Drop API gibi bir yığın API'ye JavaScript'de yazdığımız uygulamadan erişebiliyoruz. Ancak Javascript kullanımı sadece ön uç (=front-end) ile  sınırlı değil. Nodejs ile arka uçta (=back-end) uygulama geliştirmek istediğinizde Javascript kullanmalısınız. JDK 8 ile birlikte gelen Java Sanal Makinası (=Java Virtual Machine), JavaScript ile yazılmış uygulamaları hızlı bir şekilde çalıştırabiliyor. Javascript'i hem ön uçta hem de arka uçta uygulama geliştirmek için kullanıyoruz.

Javascript'de verileri saklamak için dizi kullanıyoruz. Dizi özel bir tür nesne. Dizi, verileri anahtar-değer ikilileri olarak saklıyor. Dizide saklanan tüm veriler üzerinde işlem yapmak için döngü oluşturuyoruz. Döngüyü oluşturmanın çeşitli yöntemleri bulunuyor:

i. for ile döngüyü dizinin dışında oluşturuyoruz. İndis sıfırdan başlamak üzere dizinin son elemanının indis değerini alacak şekilde bir arttırılıyor:

var sum=0;
for (var i=0;i<countries.length;++i){
   var country = countries[i];
   sum += country.population;
}
console.log("Total population : "+ sum);

ii. for-each gösterimi ile döngüyü yine dizinin dışında oluşturuyoruz, ancak bu sefer dizi indisini düzenlemekle uğraşmıyoruz:

var sum=0;
for (var i in countries){
   var country = countries[i];
   sum += country.population;
}
console.log("Total population : "+ sum);

Dizide saklanan verileri işlemek için kullanılabilecek üçüncü ve en güçlü yöntem MapReduce yapısıdır. Şimdi örneklerle Javascript'de MapReduce çatısının kullanımını çalışalım. Önce üzerinde çalışacağımız veri modeline bir göz atalım. Verileri http://www.omegaegitim.com/resources/countries.json URL adresinden çekiyoruz:

let url = 'http://www.omegaegitim.com/resources/countries.json';
fetch( url)
.then( res => res.json() )
.then( ( countries ) => console.log( JSON.stringify(countries[215]) ) )
.catch( err => console.error( err ) );

Dizinin 215 indisli gözünde Türkiye ile ilgili bilgiler var:

{
  "capital": 3358,
  "cities": [
    {
      "district": "Istanbul",
      "_id": 3357,
      "name": "Istanbul",
      "population": 8787958
    },
    {
      "district": "Ankara",
      "_id": 3358,
      "name": "Ankara",
      "population": 3038159
    },
    {
      "district": "Izmir",
      "_id": 3359,
      "name": "Izmir",
      "population": 2130359
    },
    {
      "district": "Adana",
      "_id": 3360,
      "name": "Adana",
      "population": 1131198
    }, 
    . . . ,
    {
      "district": "Balikesir",
      "_id": 3418,
      "name": "Bandirma",
      "population": 90200
    }
  ],
  "_id": "TUR",
  "continent": "Asia",
  "gnp": 20000,
  "governmentForm": "Republic",
  "headOfState": "Ahmet Necdet Sezer",
  "indepYear": 1923,
  "lifeExpectancy": 85,
  "localName": "Türkiye",
  "name": "Turkey",
  "population": 66591000,
  "region": "Middle East",
  "surfaceArea": 774828
}

Listelenen bilgiler arasında ülkenin ISO 2 standardında kodu, ülkenin adı, yüzölçümü, başkenti, nüfusu, yönetim biçimi, yer aldığı kıta, Gayri Safi Milli Hasılası ve illeri gibi bilgiler yer alıyor. Bu veri modeline göre aşağıdaki soruları MapReduce çatısını kullanarak çözmeye çalışalım.

1. Kaç kıta olduğunu bulalım:

let url = 'http://www.omegaegitim.com/resources/countries.json';
var continents = [];
fetch(url)
.then(res => res.json())
.then((countries) => {
 continents= countries.map( country => country.continent ).reduce( 
  (continents,continent) => {
   if ( continents.indexOf(continent) < 0 ) {
    continents.push(continent);
   }
   return continents;
  }, [] );
  continents.sort();
  console.log( JSON.stringify(continents) );
})
.catch(err => console.error(err));

Yukarıdaki kodu çalıştırdığımızda 7 kıtanın listelendiğini görüyoruz:

["Africa","Antarctica","Asia","Europe","North America","Oceania","South America"]

2. Her bir kıtada kaç ülke olduğunu bulalım:

let url = 'http://www.omegaegitim.com/resources/countries.json';
fetch(url)
.then(res => res.json())
.then((countries) => {
 var result= countries.map( ( country ) => { return { name : country.name, continent: country.continent } } ).reduce( 
   (continents, o ) => {
    if (  !continents.hasOwnProperty(o.continent) ) {
     continents[o.continent] = { name: o.continent, countries: [ o.name ] };
    } else {
     continents[o.continent].countries.push( o.name );
    }
    return continents;
   }, {} );
 continents= [];
 for (var o in result) continents.push(result[o]);
 continents.sort( (o1,o2) => o1.name.localeCompare(o2.name) );
 continents.forEach( (continent) => console.log("There are "+continent.countries.length+" countries in "+continent.name) );
})
.catch(err => console.error(err));

Yukarıdaki kodu çalıştırdığımızda her bir kıtada kaç ülke olduğunun listesini görebiliyoruz:

There are 58 countries in Africa
There are 5 countries in Antarctica
There are 51 countries in Asia
There are 46 countries in Europe
There are 37 countries in North America
There are 28 countries in Oceania
There are 14 countries in South America

3. Şimdi her kıtanın GNP (Gross National Product)(=Gayri Safi Milli Hasıla) değerine göre en zengin ülkesini bulalım:

let url = 'http://www.omegaegitim.com/resources/countries.json';
fetch(url)
.then(res => res.json())
.then((countries) => {
 var result= countries.map( ( country ) => { return { name : country.name, continent: country.continent, gnp: country.gnp } } ).reduce( 
   (continents, o ) => {
    if (  !continents.hasOwnProperty(o.continent) ) {
     continents[o.continent] = { name: o.continent, richestCountry:  { name: o.name, gnp : o.gnp }};
    } else {
        var richestCountry= continents[o.continent].richestCountry;
     if ( richestCountry.gnp < o.gnp ){
      richestCountry.gnp= o.gnp;
      richestCountry.name= o.name;
     }     
    }
    return continents;
   }, {} );
 continents= [];
 for (var o in result) continents.push(result[o]);
 continents.sort( (o1,o2) => o1.name.localeCompare(o2.name) );
 continents.forEach( (continent) => console.log("Richest country in "+continent.name+" is " + continent.richestCountry.name + " with " + continent.richestCountry.gnp + " gnp." ) );
})
.catch(err => console.error(err));

Yukarıdaki kodu çalıştırdığımızda her bir kıtadaki en zengin ülkenin listesini görebiliyoruz:

Richest country in Africa is South Africa with 116729 gnp.
Richest country in Antarctica is Antarctica with 0 gnp.
Richest country in Asia is Japan with 3787042 gnp.
Richest country in Europe is Germany with 2133367 gnp.
Richest country in North America is United States with 8510700 gnp.
Richest country in Oceania is Australia with 351182 gnp.
Richest country in South America is Brazil with 776739 gnp.

4. En az bir kişinin yaşadığı dünya ülkelerinin minimum, maksimum ve ortalama nüfus sayılarını hesaplayalım:

let url = 'http://www.omegaegitim.com/resources/countries.json';
fetch(url)
.then(res => res.json())
.then((countries) => {
 var statistics= countries.filter( country => country.population > 0 )
                          .map( ( country ) => { return { name : country.name, population: country.population } } ).reduce( 
   ( statistics, o ) => {
    statistics.counter++;
    statistics.total += o.population;
    if ( isNaN(statistics.minimum)|| statistics.minimum > o.population)   
       statistics.minimum= o.population;
    if ( isNaN(statistics.maximum) || statistics.maximum < o.population)   
       statistics.maximum= o.population;
    statistics.average= statistics.total/statistics.counter;
    return statistics;
   }, { minimum: NaN, maximum: NaN, counter: 0, average: 0, total: 0} );
 console.log(JSON.stringify(statistics));
})
.catch(err => console.error(err));

İşte nüfus ile ilgili istatistikler:

{"minimum":50,"maximum":1277558000,"counter":232,"average":26201506.25,"total":6078749450}

5. Bir önceki hesaplamayı bu sefer kıta özelinde yapalım ve her bir kıta için en az bir kişinin yaşadığı kıta ülkelerinin minimum, maksimum ve ortalama nüfus sayılarını hesaplayalım:

let url = 'http://www.omegaegitim.com/resources/countries.json';
fetch(url)
.then(res => res.json())
.then((countries) => {
 var result= countries.filter( country => country.population > 0 )
                          .map( ( country ) => { return { name : country.name, population: country.population, continent: country.continent } } ).reduce( 
   ( statistics, o ) => {
       if (!statistics.hasOwnProperty(o.continent)){
       statistics[o.continent]= { continent: o.continent, minimum: NaN, maximum: NaN, counter: 0, average: 0, total: 0 };
    }
    var stat= statistics[o.continent];
    stat.counter++;
    stat.total += o.population;
    if ( isNaN(stat.minimum)|| stat.minimum > o.population)   
       stat.minimum= o.population;
    if ( isNaN(stat.maximum) || stat.maximum < o.population)   
       stat.maximum= o.population;
    stat.average= stat.total/stat.counter;
    return statistics;
   }, {} );
 statistics= [];
 for (var o in result) statistics.push(result[o]);
 statistics.sort( (o1,o2) => o1.continent.localeCompare(o2.continent) );
 statistics.forEach( stat => console.log(JSON.stringify(stat)) );
})
.catch(err => console.error(err));

İşte merak ettiğimiz sonuçlar:

{"continent":"Africa","minimum":6000,"maximum":111506000,"counter":57,"average":13762719.298245614,"total":784475000}
{"continent":"Asia","minimum":286000,"maximum":1277558000,"counter":51,"average":72647562.74509804,"total":3705025700}
{"continent":"Europe","minimum":1000,"maximum":146934000,"counter":46,"average":15871186.956521738,"total":730074600}
{"continent":"North America","minimum":7000,"maximum":278357000,"counter":37,"average":13053864.864864865,"total":482993000}
{"continent":"Oceania","minimum":50,"maximum":18886000,"counter":27,"average":1125968.5185185184,"total":30401150}
{"continent":"South America","minimum":2000,"maximum":170115000,"counter":14,"average":24698571.42857143,"total":345780000}

6. Şimdi 4'de yaptığımız analizi biraz değiştireceğiz. Minimum, maksimum ve ortalama nüfusu hesaplamıştık. Üzerinde en az bir kişinin yaşıdığı ülkeler arasında minimum ve maksimum nüfusa sahip ülkeleri bulalım:

let url = 'http://www.omegaegitim.com/resources/countries.json';
fetch(url)
.then(res => res.json())
.then((countries) => {
 var statistics= countries.filter( country => country.population > 0 ).reduce( 
   ( statistics, country ) => {
       if (statistics.lowestPopulatedCountry==null || statistics.lowestPopulatedCountry.population < country.population)
       statistics.lowestPopulatedCountry= country;
       if (statistics.highestPopulatedCountry==null || statistics.highestPopulatedCountry.population > country.population)
       statistics.highestPopulatedCountry= country;
       return statistics;
   }, { lowestPopulatedCountry: null, highestPopulatedCountry: null} );
 console.log("Lowest Populated Country is " 
     + statistics.lowestPopulatedCountry.name 
     + " with " 
     + statistics.lowestPopulatedCountry.population 
     + " population.");
 console.log("Highest Populated Country is " 
     + statistics.highestPopulatedCountry.name 
     + " with " 
     + statistics.highestPopulatedCountry.population 
     + " population.");
})
.catch(err => console.error(err));

İşte en az ve en çok kalabalık ülkeler:

Lowest Populated Country is China with 1277558000 population.
Highest Populated Country is Pitcairn with 50 population.

Sonuçlar şaşırtıcı olmadı ve en kalabalık ülke olarak Çin'i bulduk.

7. Bir önceki incelemeyi bu kez kıta özelinde yapalım ve kıtalardaki en az ve en fazla nüfusa sahip ülkeleri bulalım:

let url = 'http://www.omegaegitim.com/resources/countries.json';
fetch(url)
.then(res => res.json())
.then((countries) => {
 var result= countries.filter( country => country.population > 0 ).reduce( 
   ( statistics, country ) => {
       if (!statistics.hasOwnProperty(country.continent)){
       statistics[country.continent]= { continent: country.continent, lowestPopulatedCountry: country, highestPopulatedCountry: country };
    } else {
     if (statistics[country.continent].lowestPopulatedCountry.population < country.population)
      statistics[country.continent].lowestPopulatedCountry= country;
     if (statistics[country.continent].highestPopulatedCountry.population > country.population)
        statistics[country.continent].highestPopulatedCountry= country;
    }   
    return statistics;
   }, {} );
 statistics= [];
 for (var o in result) statistics.push(result[o]);
 statistics.sort( (o1,o2) => o1.continent.localeCompare(o2.continent) );
 statistics.forEach( stat => {
   console.log("Lowest Populated Country in " + stat.continent + " is " 
        + stat.lowestPopulatedCountry.name 
        + " with " 
        + stat.lowestPopulatedCountry.population 
        + " population.");
    console.log("Highest Populated Country in " + stat.continent + " is " 
        + stat.highestPopulatedCountry.name 
        + " with " 
        + stat.highestPopulatedCountry.population 
        + " population."); 
 } );
})
.catch(err => console.error(err));

İşte kıtalardaki en az ve en çok kalabalık ülkelerin listesi:

Lowest Populated Country in Africa is Nigeria with 111506000 population.
Highest Populated Country in Africa is Saint Helena with 6000 population.
Lowest Populated Country in Asia is China with 1277558000 population.
Highest Populated Country in Asia is Maldives with 286000 population.
Lowest Populated Country in Europe is Russian Federation with 146934000 population.
Highest Populated Country in Europe is Holy See (Vatican City State) with 1000 population.
Lowest Populated Country in North America is United States with 278357000 population.
Highest Populated Country in North America is Saint Pierre and Miquelon with 7000 population.
Lowest Populated Country in Oceania is Australia with 18886000 population.
Highest Populated Country in Oceania is Pitcairn with 50 population.
Lowest Populated Country in South America is Brazil with 170115000 population.
Highest Populated Country in South America is Falkland Islands with 2000 population.

8. Ülkelerin, en az nüfusa ve en çok nüfusa sahip şehirlerini bulalım:

let url = 'http://www.omegaegitim.com/resources/countries.json';
fetch(url)
.then(res => res.json())
.then((countries) => {
 var result= countries.filter( country => country.population > 0 ).reduce( 
   ( statistics, country ) => {
       statistics[country.name]= { name: country.name };
       cityStatistics = country.cities.reduce( 
        ( cityStatistics , city ) => {
      if (cityStatistics.lowestPopulatedCity==null || cityStatistics.lowestPopulatedCity.population < city.population)
       cityStatistics.lowestPopulatedCity= city;
      if (cityStatistics.highestPopulatedCity==null || cityStatistics.highestPopulatedCity.population > city.population)
       cityStatistics.highestPopulatedCity= city;         
      return cityStatistics;
     }     
    , { lowestPopulatedCity: null, highestPopulatedCity: null } ); 
                statistics[country.name].lowestPopulatedCity= cityStatistics.lowestPopulatedCity;
    statistics[country.name].highestPopulatedCity= cityStatistics.highestPopulatedCity;
    return statistics;
   }, {} );
 console.log(result)  ;
 statistics= [];
 for (var o in result) statistics.push(result[o]);
 console.log(statistics.length);
 statistics.sort( (o1,o2) => o1.name.localeCompare(o2.name) );
 statistics.forEach( stat => {
   console.log("Lowest Populated City in " + stat.name + " is " 
        + stat.lowestPopulatedCity.name 
        + " with " 
        + stat.lowestPopulatedCity.population 
        + " population.");
    console.log("Highest Populated City in " + stat.name + " is " 
        + stat.highestPopulatedCity.name 
        + " with " 
        + stat.highestPopulatedCity.population 
        + " population."); 
 } );
})
.catch(err => console.error(err));

İşte ülkelerin şehirlerinin durumu:

Lowest Populated City in Afghanistan is Herat with 186800 population.
Highest Populated City in Afghanistan is Mazar-e-Sharif with 127800 population.
Lowest Populated City in Albania is Tirana with 270000 population.
Highest Populated City in Albania is Tirana with 270000 population.
Lowest Populated City in Algeria is Alger with 2168000 population.
Highest Populated City in Algeria is Ghardaïa with 89415 population.
. . .
. . .
. . .
Lowest Populated City in Zambia is Lusaka with 1317000 population.
Highest Populated City in Zambia is Luanshya with 118100 population.
Lowest Populated City in Zimbabwe is Harare with 1410000 population.
Highest Populated City in Zimbabwe is Gweru with 128037 population.

Şu ana kadar MapReduce kullanarak yaptığımız hesaplamaları şimdi paralelleştirmeye çalışacağız. Bunun için Parallel JS kütüphanesini kullanacağız. Tüm ülkelerin toplam nüfusunu bu kez paralel çalışan MapReduce ile bulmaya çalışalım:

let url = 'countries.json';
  fetch(url)
  .then(res => res.json())
  .then((countries) => {
      var task = new Parallel(countries);
      var async= task.map( country => country.population )
                  .reduce( (population) => { return population[0]+population[1]; } , 0)
      async.then( (result) => console.log(result) );
  });