Wednesday, February 27, 2013

Searching in indexed image by histogram

The histogram calculated previously are extracted in file with the format following
126094.jpg;0.002390544;0.0012207029;0.010274278;0.00600177;8.9518283E-4....

The code source to calculate the distance between the two image is


private static float distanceL2(float d1[], float d2[]) {
        float computation = 0.0f;
        for (int i = 0; i < ((d1.length < d2.length) ? d1.length : d2.length); i++) {
            float diff = d1[i] - d2[i];
            computation += diff * diff;
        }
        return (float) Math.sqrt(computation);
    }

The code source to search on the image list and to sort by k number of the more near image:

public static void main(String[] args) {
        String listeImagesDB = args[0];
        String descripteurs = args[1];
        String imagerequete = args[2];
        int nombreResultat = Integer.parseInt(args[3]);
        String resultFile = "";
        if (args.length > 4) {
            resultFile = args[4];
        }

        //Load the descriptors and search for requested image : imagerequete
        File databaseFile = new File(descripteurs);
        FileReader datareader = null;
        float[] imageRequete = null;
     
        try {
            datareader = new FileReader(databaseFile);

            BufferedReader databuffer = new BufferedReader(datareader);
            String s;
            while ((s = databuffer.readLine()) != null) {
                String valuesStrList[] = s.split(";");
                float[] values = new float[valuesStrList.length - 1];
                for (int i = 1; i < valuesStrList.length; i++) {
                    values[i - 1] = Float.parseFloat(valuesStrList[i]);
                }
                if (valuesStrList[0].equals(imagerequete)) {
                    imageRequete = values;
                    break;
                }
             

            }
            datareader.close();
        } catch (Exception ex) {
            Logger.getLogger(QBE.class.getName()).log(Level.SEVERE, null, ex);
            return;
        }

        //Search best results
        //saved result
        String[] resultImage = new String[nombreResultat];
        float[] resultDistance = new float[nombreResultat];
        int maxIndex = 0;
        //Iterator it = savedImgList.keySet().iterator();
        try {
            datareader = new FileReader(databaseFile);


            BufferedReader databuffer = new BufferedReader(datareader);
            String s;

            while ((s = databuffer.readLine()) != null) {
                String valuesStrList[] = s.split(";");
                float[] values = new float[valuesStrList.length - 1];
                for (int i = 1; i < valuesStrList.length; i++) {
                    values[i - 1] = Float.parseFloat(valuesStrList[i]);
                }

                String filename = valuesStrList[0];
                float imgvalues[] = values;
                float distance = distanceL2(imageRequete, imgvalues);

                int keyBefore = -1;
                for (int i = 0; i < nombreResultat; i++) {
                    String filenameResult = resultImage[i];
                    if (filenameResult == null || filenameResult.isEmpty()) {
                        break; //empty list
                    }
                    float distanRes = resultDistance[i];
                    if (distance < distanRes) {
                        keyBefore = i;
                        break; //fisrt occurence trouver
                    }

                }

                if (keyBefore == -1) {
                    if (maxIndex < nombreResultat) {
                        resultImage[maxIndex] = filename;
                        resultDistance[maxIndex] = distance;
                        maxIndex++;
                    }
                } else {

                    for (int i = nombreResultat - 1; i > keyBefore; i--) {
                        resultImage[i] = resultImage[i - 1];
                        resultDistance[i] = resultDistance[i - 1];
                        if (resultImage[i] == null || resultImage[i].isEmpty()) {
                            maxIndex = i; //retrieve empty index
                        }
                    }

                    resultImage[keyBefore] = filename;
                    resultDistance[keyBefore] = distance;
                }


            }
        } catch (IOException ex) {
            Logger.getLogger(QBE.class.getName()).log(Level.SEVERE, null, ex);
        }

        //Produce Result
        File dbFile = new File(listeImagesDB);

        String imagePath = dbFile.getParent() + File.separatorChar + "images" + File.separatorChar;

        StringBuffer buff = new StringBuffer();
        buff.append("<html><body>");
        for (int i = 0; i < nombreResultat; i++) {
            buff.append("<img src=\"" + imagePath + resultImage[i] + "\" alt=\"" + resultDistance[i] + "\"/>");
            buff.append(" Distance="+resultDistance[i]+" ");
        }
        buff.append("</body></html>");
        if (resultFile.isEmpty()) {
            System.out.println(buff.toString());
        } else {
            try {
                FileWriter indexedDB = new FileWriter(resultFile, false);
                indexedDB.write(buff.toString());
                indexedDB.close();
            } catch (IOException ex) {
                Logger.getLogger(QBE.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

    }

After indexed 10000 images following the previous code sample based on histogram , I have tested some image request with the previous algorithm.
Here I show some results:
The requested image is the first on the left and top of each image. Nice result !






Saturday, February 23, 2013

Image Quantization

The quantization is the idea of minimizing the color of an image to simplify the appearance.
A simplified image permit some compression or some futur works as the image identification.
For begin I propose the image loading that is particular to JPEG and pure RGB format , other aspect could have more or less than 3 colors(transparence, gray,...).


public class FakeJpicture {
    public byte[] pixels;
    public int width;
    public int height;
    public FakeJpicture(File file) throws IOException {
        BufferedImage img;
        img = ImageIO.read(file);
        width=img.getWidth();
        height=img.getHeight();
        pixels = ((DataBufferByte)img.getRaster().getDataBuffer()).getData();
    }

This lines permit to obtain an image names pixels with the size width*height*3 . 3 for RGB.

First work, how to obtain the color:

private int calculateIndex(int x, int y){
        return x+y*width*3;
    }
 
Second work , how to calculate a gray level on this pixel:

public int getGray(int x, int y) {
        int index=calculateIndex(x, y);
        return (int) (0.299*(128+pixels[index])+0.587*(128+pixels[index+1])+0.114*(128+pixels[index+2]));
    }

It s the time to talk about bins. It s easy to calculate some gray level with 256 level.
But our target is to decrease the number of gray level. Exemple 256 -> 8
The most we can do is to cut our level. For exemple the compressed gray level 0 could contain the original gray level from 0 to 7.

I propose an algorithm to calculate the histogram that uses a bins parameter :
public float [] ComputeGrayLevelHistogram(int bins){
        float []Npg=new float[bins];
        for (int i=0;i<bins;i++)
            Npg[i]=0.0f;
        float MN=(float)width*height;
        float NpgSURMN=1.0f/MN; //<- count each time we sea the gray level
        for (int x=0;x<width;x++)
            for (int y=0;y<height;y++){
                int gray=getGray(x,y); // <- get the gray level
                int binsIndex=(int)((gray/256.0)*bins); // <- calculate the compressed level
                Npg[binsIndex]+=NpgSURMN; // <- increment the histo on this value
            }
        return Npg;
    }


Now the colors. To manage RGB colors we need to do the same things coded for the gray value. The difference is to manage 3 colors and not just one value.

For simplify this, I calculate an index managing each bins colors for each colors:
private int calculateRGBIndex(int R, int G,int B,int Rb,int Gb,int Bb){
        return R+G*Rb+B*Rb*Gb;
   }

My algorithm for RGB colors is :

public float [] ComputeRGBHistogram(int binsR,int binsG,int binsB){
        int totalmaxbins=binsR*binsG*binsB;
        float []Npg=new float[totalmaxbins];
        for (int i=0;i<totalmaxbins;i++)
            Npg[i]=0.0f;
        float MN=(float)width*height;
        float NpgSURMN=1.0f/MN;
        for (int x=0;x<width;x++)
            for (int y=0;y<height;y++){
                int index=calculateIndex(x, y);
                int binsRIndex=(int)(((128+pixels[index])/256.0)*binsR);
                int binsGIndex=(int)(((128+pixels[index+1])/256.0)*binsG);
                int binsBIndex=(int)(((128+pixels[index+2])/256.0)*binsB);
                int binsIndex=calculateRGBIndex(binsRIndex, binsGIndex,binsBIndex,binsR,binsG,binsB);
                Npg[binsIndex]+=NpgSURMN;
            }
        return Npg;
    }

The conclusion is that this code can be reused to generate a compressed image.

Result exemple


   Gray histogram on 64 bins

Color 6x6x6 bins