%% Genetický algoritmus pro výpočet odchylky kvadrátů, založeno na laboratorních cvičeních z předmětu FEAL - evoluční algoritmy
function [posunVyslednySSD, maxKvalitaSSD, iterace] = evoluce(obraz1, obraz2, pocetjedincu, pocetreplikaci, pst_mutace,procento_obrazu,uhel_rotace,maximutaci,kriterium_kvality)
rng('shuffle');                                                          %po kolika iteracích se provede nové roztřídění, není-li výsledek
[row,col] = size(obraz1);
maxShiftCol = round(col*procento_obrazu/100);
maxShiftRow = round(row*procento_obrazu/100); 
maxRotation = deg2rad(uhel_rotace);
%%
jedinec=zeros(pocetjedincu,3);                                              %definování počátečních jedinců
kvalita=zeros(pocetjedincu,1);                                              %definování počáteční kvality
for i=1:pocetjedincu                                                        %pro každého jedince
    jedinec(i,1)=randi([-maxShiftCol,maxShiftCol],1,1);                          %pro jedince je generována dvojice čísel z intervalu
    jedinec(i,2)=randi([-maxShiftRow,maxShiftRow],1,1);                          %pro jedince je generována dvojice čísel z intervalu
    jedinec(i,3)= -maxRotation + (maxRotation - -maxRotation) .* rand(1,1);
    tform = maketform('affine',[0+cos(jedinec(i,3)) 0+sin(jedinec(i,3)) 0;...
        0-sin(jedinec(i,3)) 0+cos(jedinec(i,3)) 0; jedinec(i,1) jedinec(i,2) 1]);
    obr_temp = imtransform(obraz2,tform,'XData',[1, col],'YData',[1, row]);
    kvalita(i,1)= ( (sum(sum(obraz1.*obr_temp)))/(sqrt(sum(sum(obraz1.^2)))*sqrt(sum(sum(obr_temp.^2)))) );    %výpočet kosinového kritéria + normalizace
end
%% BEZ TRANSFORMACE
kvalitaBezTransformace = ( (sum(sum(obraz1.*obraz2)))/(sqrt(sum(sum(obraz1.^2)))*sqrt(sum(sum(obraz2.^2)))) );    %výpočet kosinového kritéria + normalizace
%%
staraelita=max(kvalita);                                                    %určena nejvyšší kvalita
iterace=1;                                                                  %definováni první iterace
while iterace<=maximutaci && max(kvalita) < kriterium_kvality               %pokud nebylo dosaženo maxima mutací
    [elita_kvalita, elita_index] = max(kvalita);                               %je vybrán nejlepší jedinec
    elita_hodnoty = jedinec(elita_index,:);                                   %nejlepší jedince postupuje do další generace
    jedinec = soubojSSD(jedinec,kvalita);                                     %je provedena selekce souboje, volání funkce souboj, jejímž výstupem je úspěšnější jedinec
    jedinec = mutaceSSD(jedinec,pst_mutace,maxShiftRow,maxShiftCol,maxRotation);          %provedení mutace, volání funkce mutace
    tform = maketform('affine',[0+cos(elita_hodnoty(1,3)) 0+sin(elita_hodnoty(1,3)) 0;...
        0-sin(elita_hodnoty(1,3)) 0+cos(elita_hodnoty(1,3)) 0; elita_hodnoty(1,1) elita_hodnoty(1,2) 1]);
    obr_temp = imtransform(obraz2,tform,'XData',[1, col],'YData',[1, row]);
    kvalita_temp = ( (sum(sum(obraz1.*obr_temp)))/(sqrt(sum(sum(obraz1.^2)))*sqrt(sum(sum(obr_temp.^2)))) );
    % Přenos elitního jedince
    for i=1:min(pocetjedincu,pocetreplikaci)                                %předání elitního jedince, dle počtu replikací, ten je standardně roven jedné
        jedinec(i,:) = elita_hodnoty;                                         %předání elitního jedince do další generace
        kvalita(i,1) = kvalita_temp;
    end
    % Výpočet kvalit aktuální populaci
    for i=min(pocetjedincu,pocetreplikaci)+1:pocetjedincu                                                   %pro každého jedince
        tform = maketform('affine',[0+cos(jedinec(i,3)) 0+sin(jedinec(i,3)) 0;...
            0-sin(jedinec(i,3)) 0+cos(jedinec(i,3)) 0; jedinec(i,1) jedinec(i,2) 1]);
        obr_temp = imtransform(obraz2,tform,'XData',[1, col],'YData',[1, row]);
        kvalita(i,1)=( (sum(sum(obraz1.*obr_temp)))/(sqrt(sum(sum(obraz1.^2)))*sqrt(sum(sum(obr_temp.^2)))) );    %výpočet kosinového kritéria + normalizace
    end
    % Pokud se nezlepšila kvalita nejlepšího jedince, je započtena další iterace
    if staraelita==elita_kvalita                                            %pokud se nejepší jedinci rovnají, nedošlo tedy ke zlepšení
        iterace=iterace+1;                                                  %je navýšena iterace
    end
    staraelita=elita_kvalita;                                               %uložení elity
end
[maxKvalitaSSD,maxIndex] =max(kvalita);                                             %nalezení nejlepšího jedince ve výsledné populace
if(maxKvalitaSSD < kvalitaBezTransformace)
    maxKvalitaSSD=kvalitaBezTransformace;                                %hodnota nejvyšší kvality
    posunVyslednySSD=[0,0,0];                                               %získání nejlepšího jedince
else
    posunVyslednySSD=jedinec(maxIndex,:);                                 %získání nejlepšího jedince
end
end

%%
function jedinciPred = mutaceSSD(jedinciPred,pst_mutace,maxShiftRow,maxShiftCol,maxRotation)                        %do funkce vstupují jedinci, kteří budou mutováni a pst mutace
rng('shuffle');
[pocetjedincu, pocetgenu]=size(jedinciPred);                                %určení počtu jedinců, kteř se vyskytují v populaci a kolik genu každý nese
nahodnacisla=rand(pocetjedincu,1);                                          %pro každého jedince je generováno náhodné číslo
for i=1:pocetjedincu                                                        %pro každého jedince
    if nahodnacisla(i)<pst_mutace                                           %náhodné číslo, které bylo každému generováno je zde porovnánváo s pstí mutace, pokud je menší, provede se
        nahodnyvyber=randi([1,pocetgenu]);                                  %volba genu, který je mutován
        mutace=[randi([-maxShiftCol,maxShiftCol],1,1),...
            randi([-maxShiftRow,maxShiftRow],1,1),...
            -maxRotation + (maxRotation - -maxRotation) .* rand(1,1)];   %generování náhodné hodnoty o kterou bude původní hodnota změněna
        jedinciPred(i,nahodnyvyber)= mutace(1,nahodnyvyber);        %realizace mutace, k hodnotě je přičetena náhodně generovaná hodnota v rozsahu -5 až 5
    end
end
end

%%
function jedinciPo = soubojSSD(jedinciPred,kvalitaPred)                       %funkce souboj, vstupují jedinci a jejich kvality
jedinciPo=zeros(size(jedinciPred));                                         %definice proměnné do které budou uložni jedinci po souboji
[pocetjedincu,~]=size(jedinciPred);                                         %zjištění počtu jedinců
for j=1:pocetjedincu                                                        %pro každého jedince proběhne souboj
    nahodnyvyber=zeros(1,2);                                                %definice proměnné do které budou ukládání jedinci pro souboj
    for i=[1, 2]                                                             %souboj probíha pro dva jedince
        nahodnyvyber(i)=randi([1,pocetjedincu]);                            %náhodný výběr dvojice do souboje
    end
    while nahodnyvyber(1)==nahodnyvyber(2)                                  %pokud se vygenerovala stejná dvojice
        nahodnyvyber(2)=randi([1,pocetjedincu]);                            %je druhý jedinec změněn
    end
    if kvalitaPred(nahodnyvyber(1))>kvalitaPred(nahodnyvyber(2))            %pokud je kvalita druhého jedince vyšší(pozn. zde je hledáno minimum)
        jedinciPo(j,:)=jedinciPred(nahodnyvyber(1),:);                      %vítězí první jedinec a jeho hodnota je oběma přiřazena
    else
        jedinciPo(j,:)=jedinciPred(nahodnyvyber(2),:);                      %jinak vítězí druhý jedinec a jeho hodnota je oběma přiřazena
    end
end
end
