Daniel@0: function varargout = mirnovelty(orig,varargin) Daniel@0: % n = mirnovelty(m) evaluates the novelty score from a similarity matrix. Daniel@0: % [n,m] = mirnovelty(m) also return the similarity matrix. Daniel@0: % Optional argument: Daniel@0: % mirnovelty(...,'Distance',f) specifies the name of a dissimilarity Daniel@0: % distance function, from those proposed in the Statistics Toolbox Daniel@0: % (help pdist). Daniel@0: % default value: f = 'cosine' Daniel@0: % mirnovelty(...,'Similarity',f) specifies the name of a similarity Daniel@0: % measure function. This function is applied to the result of the Daniel@0: % distance function. cf. mirsimatrix Daniel@0: % default value: f = 'exponential' Daniel@0: % corresponding to f(x) = exp(-x) Daniel@0: % mirnovelty(...,'KernelSize',s) or more simply mirnovelty(...,s) Daniel@0: % specifies the length of the gaussian kernel, in samples. Daniel@0: % default value: s = 64. Daniel@0: % mirnovelty(...,'Normal',0) does not normalize the novelty curve Daniel@0: % between the values 0 and 1. Daniel@0: % Daniel@0: % Foote, J. & Cooper, M. (2003). Media Segmentation using Self-Similarity Daniel@0: % Decomposition,. In Proc. SPIE Storage and Retrieval for Multimedia Daniel@0: % Databases, Vol. 5021, pp. 167-75. Daniel@0: Daniel@0: dist.key = 'Distance'; Daniel@0: dist.type = 'String'; Daniel@0: dist.default = 'cosine'; Daniel@0: option.dist = dist; Daniel@0: Daniel@0: sm.key = {'Measure','Similarity'}; Daniel@0: sm.type = 'String'; Daniel@0: sm.default = 'exponential'; Daniel@0: option.sm = sm; Daniel@0: Daniel@0: K.key = {'KernelSize','Width'}; Daniel@0: K.type = 'Integer'; Daniel@0: K.default = 64; Daniel@0: option.K = K; Daniel@0: Daniel@0: transf.type = 'String'; Daniel@0: transf.default = 'TimeLag'; Daniel@0: transf.choice = {'Horizontal','TimeLag'}; Daniel@0: option.transf = transf; Daniel@0: Daniel@0: normal.key = 'Normal'; Daniel@0: normal.type = 'Boolean'; Daniel@0: normal.default = 1; Daniel@0: normal.when = 'After'; Daniel@0: option.normal = normal; Daniel@0: Daniel@0: specif.option = option; Daniel@0: specif.combineframes = @combineframes; Daniel@0: specif.nochunk = 1; Daniel@0: varargout = mirfunction(@mirnovelty,orig,varargin,nargout,specif,@init,@main); Daniel@0: Daniel@0: Daniel@0: function [x type] = init(x,option) Daniel@0: type = 'mirscalar'; Daniel@0: if not(isamir(x,'mirscalar') && strcmp(get(x,'Title'),'Novelty')) Daniel@0: x = mirsimatrix(x,'Distance',option.dist,'Similarity',option.sm,... Daniel@0: 'Width',option.K,option.transf); Daniel@0: end Daniel@0: if isa(x,'mirdesign') Daniel@0: x = set(x,'Overlap',ceil(option.K)); Daniel@0: end Daniel@0: Daniel@0: Daniel@0: function y = main(orig,option,postoption) Daniel@0: if iscell(orig) Daniel@0: orig = orig{1}; Daniel@0: end Daniel@0: if not(isa(orig,'mirscalar')) Daniel@0: s = get(orig,'Data'); Daniel@0: dw = get(orig,'DiagWidth'); Daniel@0: for k = 1:length(s) Daniel@0: if isnumeric(dw) Daniel@0: dwk = dw; Daniel@0: else Daniel@0: dwk = dw{k}; Daniel@0: end Daniel@0: if option.K Daniel@0: cgs = min(option.K,dwk); Daniel@0: else Daniel@0: cgs = dwk; Daniel@0: end Daniel@0: cg = checkergauss(cgs,option.transf); Daniel@0: disp('Computing convolution, please wait...') Daniel@0: for z = 1:length(s{k}) Daniel@0: sz = s{k}{z}; Daniel@0: szm = max(max(sz)); Daniel@0: for i = find(isnan(sz)) Daniel@0: sz(i) = szm; Daniel@0: end Daniel@0: cv = convolve2(sz,cg,'same'); Daniel@0: nl = size(cv,1); Daniel@0: nc = size(cv,2); Daniel@0: if nl == 0 Daniel@0: warning('WARNING IN NOVELTY: No frame decomposition. The novelty score cannot be computed.'); Daniel@0: score{k}{z} = []; Daniel@0: else Daniel@0: sco = cv(floor(size(cv,1)/2),:); Daniel@0: incr = find(diff(sco)>=0); Daniel@0: if not(isempty(incr)) Daniel@0: decr = find(diff(sco)<=0); Daniel@0: sco(1:incr(1)-1) = NaN(1,incr(1)-1); Daniel@0: if not(isempty(decr)) Daniel@0: sco(decr(end)+1:end) = NaN(1,length(sco)-decr(end)); Daniel@0: end Daniel@0: incr = find(diff(sco)>=0); Daniel@0: sco2 = sco; Daniel@0: if not(isempty(incr)) Daniel@0: sco2 = sco2(1:incr(end)+1); Daniel@0: end Daniel@0: decr = find(diff(sco)<=0); Daniel@0: if not(isempty(decr)) && decr(1)>2 Daniel@0: sco2 = sco2(decr(1)-1:end); Daniel@0: end Daniel@0: mins = min(sco2); Daniel@0: rang = find(sco>= mins); Daniel@0: if not(isempty(rang)) Daniel@0: sco(1:rang(1)-1) = NaN(1,rang(1)-1); Daniel@0: sco(rang(end)+1:end) = NaN(1,length(sco)-rang(end)); Daniel@0: end Daniel@0: end Daniel@0: score{k}{z} = sco; Daniel@0: end Daniel@0: end Daniel@0: end Daniel@0: else Daniel@0: score = get(orig,'Data'); Daniel@0: end Daniel@0: if not(isempty(postoption)) && postoption.normal Daniel@0: for k = 1:length(score) Daniel@0: for l = 1:length(score{k}) Daniel@0: sco = score{k}{l}; Daniel@0: sco = (sco-min(sco))/(max(sco)-min(sco)); Daniel@0: score{k}{l} = sco; Daniel@0: end Daniel@0: end Daniel@0: end Daniel@0: n = mirscalar(orig,'Data',score,'Title','Novelty'); Daniel@0: y = {n orig}; Daniel@0: Daniel@0: Daniel@0: function old = combineframes(old,new) Daniel@0: if not(iscell(old)) Daniel@0: old = {old}; Daniel@0: end Daniel@0: if not(iscell(new)) Daniel@0: new = {new}; Daniel@0: end Daniel@0: for var = 1:length(new) Daniel@0: ov = old{var}; Daniel@0: nv = new{var}; Daniel@0: ofp = get(ov,'FramePos'); Daniel@0: ofp = ofp{1}{1}; Daniel@0: nfp = get(nv,'FramePos'); Daniel@0: nfp = nfp{1}{1}; Daniel@0: od = get(ov,'Data'); Daniel@0: od = od{1}{1}; Daniel@0: onan = find(isnan(od)); Daniel@0: od(onan) = []; Daniel@0: ofp(:,onan) = []; Daniel@0: nd = get(nv,'Data'); Daniel@0: nd = nd{1}{1}; Daniel@0: nnan = find(isnan(nd)); Daniel@0: nd(nnan) = []; Daniel@0: nfp(:,nnan) = []; Daniel@0: [unused omatch nmatch] = intersect(ofp(1,:),nfp(1,:)); Daniel@0: if isempty(omatch) Daniel@0: ov = set(ov,'FramePos',{{[ofp nfp]}},'Data',{{[od nd]}}); Daniel@0: else Daniel@0: lm = length(omatch); Daniel@0: ov = set(ov,'FramePos',{{[ofp(:,1:omatch(1)-1) nfp]}},... Daniel@0: 'Data',{{[od(1:omatch(1)-1),... Daniel@0: (od(omatch).*(lm:-1:1) + nd(nmatch).*(1:lm))/(lm+1),... Daniel@0: nd(nmatch(end)+1:end)]}}); Daniel@0: end Daniel@0: old{var} = ov; Daniel@0: end Daniel@0: Daniel@0: Daniel@0: function y = checkergauss(N,transf) Daniel@0: hN = ceil(N/2); Daniel@0: if strcmpi(transf,'TimeLag') Daniel@0: y = zeros(hN,N); Daniel@0: for j = 1:N Daniel@0: for i = 1:hN Daniel@0: g = exp(-((i/hN)^2 + (((j-hN)/hN)^2))*4); Daniel@0: if j>hN && ji,j>N-i) Daniel@0: y(i,j) = -g; Daniel@0: else Daniel@0: y(i,j) = g; Daniel@0: end Daniel@0: end Daniel@0: end Daniel@0: end