Uma das subtarefas importantes da análise de vídeo é rastrear objetos em um vídeo. Não é tão primitivo que eu precisei descer para o nível pixel por pixel, mas não é tão complexo que exija inequivocamente uma rede neural multicamada para a solução. O rastreamento pode ser usado como um fim em si mesmo e como parte de outros algoritmos:
- Contando pessoas únicas que entraram em uma determinada zona ou cruzaram a fronteira em um quadro
- Identificação de rotas típicas de carros em um estacionamento e pessoas em uma loja
- Rotação automática da câmera de vigilância quando o objeto é deslocado
Mesmo sem olhar para a literatura, posso dizer com confiança que a melhor maneira de resolver o problema é usar redes neurais. Em geral, você não pode escrever mais nada, mas nem sempre é possível se apressar em uma tarefa com um par de GTX 1080Ti. Quem se importa em rastrear objetos no vídeo em tais casos, por favor, em cat. Vou tentar não apenas explicar como os rastreadores ASEF e MOSSE funcionam, mas levá-lo à solução para que as fórmulas pareçam óbvias.
Para começar, responderemos a uma pergunta preliminar: por que inventar algo quando você pode alimentar uma pilha de vídeos com fluxo tensor e deixar o computador por algumas semanas? As abordagens de redes neurais têm uma séria desvantagem: mesmo nas placas de vídeo modernas, é difícil obter uma boa taxa de incêndio nas redes. Isso não é um problema se analisarmos o vídeo gravado, mas ele insere um bastão nas rodas se você deseja trabalhar em tempo real. Suponha que desejemos processar vídeo de cinco câmeras a 10 FPS. Mesmo com condições relativamente moderadas, uma rede neural deve ter um tempo de inferência inferior a
frac10005 times10=$2 milissegundos (em condições de não paralelismo completo). Para comparação, o YoloV3, uma rede classificadora com uma arquitetura relativamente simples, pode revelar uma imagem em
50 milissegundos. Além disso, soluções baseadas em poderosas placas gráficas podem ser muito caras.
Este artigo pressupõe que você já tenha lidado com o processamento de imagens de máquina, esteja familiarizado com as operações básicas da álgebra linear (convolução, norma, inversão de matriz) e, em geral, entenda a transformação de Fourier.
Daqui em diante:
- A odotB significa multiplicação de matrizes elementares A e B
- A/vezesB denota convolução de matrizes A e B
- hatA( omega, nu)= mathcalF(A(x,y)) significa que hatA( omega, nu) - matriz de frequências da transformada rápida de Fourier aplicada à imagem A .
- paraleloA parallel2 - indica a soma dos quadrados dos elementos da matriz A
Decisão trivial
À primeira vista, a tarefa de rastrear um assunto específico não parece tão complicada.
Que possamos ter
T quadros consecutivos de vídeo
It o tamanho
w em
h pixels. No quadro inicial do vídeo
I0 um retângulo é circulado em torno de algum objeto
F0m em
n . É necessário encontrar a localização desse corpo em todos os outros quadros
It .
Removemos o ruído nas imagens e normalizamos cada uma delas na faixa de -1 a 1, para que as alterações gerais no brilho não afetem a detecção. Tire o primeiro quadro do vídeo sem marcação
I1 . Se
I0 e
I1 - quadros vizinhos de vídeo com bom FPS, é improvável que o objeto desejado esteja longe da sua posição original. Aproveite isso. Cortar
I1 retângulo
F1 do local onde o corpo desejado estava localizado anteriormente. "Arrastar"
F0 através de
F1 e em cada ponto calculamos o quadrado da soma das diferenças
GL2(i,j)= paraleloF1(i,j)−F0 paralelo2,i em[0,m],j em[0,n]
onde calcular a diferença de
GL2(i,j) precisa combinar o centro
F0 com elemento
(i,j) em
F1 e os valores ausentes para zero. Depois disso na matriz
GL2 o mínimo é procurado; sua localização menos as coordenadas do centro
F1 e será o deslocamento do objeto desejado por
I1 .
Para que uma transição nítida para zero não "toque" durante a detecção, é melhor inicialmente levar o retângulo um pouco mais do que o necessário e reduzir suavemente para zerar os valores mais próximos das bordas
F0 e
F1 . Para isso, cada um
F precisa ser multiplicado pela máscara. Para objetos quadrados, o bom e velho expositor fará.
A= exp frac(x−i)2+(y−j)2 sigma2 (onde
(x,y) É o centro da janela), mas, no caso geral, é melhor usar uma janela Hanning bidimensional.
Para
I2 a janela
F2 tirada da posição prevista no quadro
I1 e assim por diante.
Em vez disso
L2 normas podem ser usadas
L1 (
GL1(i,j)=|F1(i,j)−F0| ) e menos a correlação cruzada de matrizes (
GnCC(i,j)=− sumklF1,kl(i,j)F0,kl,k em[0,m],l in[0,n] ) Ambos são considerados um pouco mais rápidos que
L2 mas eles têm suas próprias características.
L1 não diferenciável e menos sensível a grandes diferenças nos valores de pixel. A correlação cruzada pode produzir falsos positivos se a amostra tiver baixo contraste e a imagem tiver áreas muito claras ou muito escuras.
L2 -versão da métrica não tem essa desvantagem:
GL2= sum(F1,kl(i,j)−F0,kl)2
= sum(F1,kl(i,j))2−2F1,kl(i,j)F0,kl+(F0,kl)2
= sum(F1,kl(i,j))2− sum2F1,kl(i,j)F0,kl+ sum(F0,kl)2
=EF1(i,j)+2GnCC(i,j)+EF0
EF1(i,j) , A "energia" do site selecionado em
It atua como um fator de equilíbrio (
EF0 , a soma dos quadrados dos valores de pixel da amostra é a mesma para todas as posições da janela e não tem significado prático aqui).
Mesmo esse algoritmo primitivo lida muito bem no caso de movimento linear de objetos (por exemplo, uma câmera olhando para o transportador). No entanto, devido à simplicidade do modelo e execução, esse método de rastreamento tem várias desvantagens:
- Um movimento linear simples de um objeto sem mudanças na natureza é raro. Como regra, os corpos no campo de visão da câmera podem sofrer algumas classes de alterações. Em ordem crescente de complexidade: aumento / diminuição de tamanho, voltas, transformações afins, transformações projetivas, transformações não lineares, alterações em um objeto. Mesmo se omitirmos mudanças de objeto e transformações não lineares, gostaríamos que o algoritmo pudesse se recuperar de rotações e alterações de tamanho relativamente simples. Obviamente, o procedimento acima não possui essa propriedade. Provavelmente F0 ainda dará uma resposta perceptível no objeto, mas será difícil determinar a localização exata da amostra e a faixa será descontínua.
- Mostramos ao algoritmo apenas uma amostra positiva, é difícil dizer qual resposta dará F0 se outro objeto semelhante entrar na janela. Bem, se o objeto desejado estiver contrastando e tiver uma estrutura rara, mas e se quisermos monitorar uma máquina em um fluxo de outras máquinas? Um rastreador pode pular imprevisivelmente de um carro para outro.
- Em cada quadro, descartamos toda a história de fundo. Provavelmente, também deve ser usado de alguma forma.
- Além disso, aprendemos apenas em um ponto da imagem. Será melhor que, perto da localização correta do objeto, o rastreador também dê uma boa resposta. Um pouco contra-intuitivo, mas pense: se o filtro estiver no local exato do objeto na imagem (x,y) dá o melhor valor e, em (x+1,y+1) - Algo aleatório, o que significa que ele é sensível demais a pequenos detalhes que podem mudar facilmente. Por outro lado, se em (x,y) e em (x+1,y+1) aproximadamente os mesmos bons valores, o filtro “enganchou” em sinais maiores e, esperamos, mais permanentes.
- Com a implementação ingênua do procedimento de rastreamento, para cada pixel do quadro, multiplicamos a janela inteira pelo item selecionado pela parte correspondente desse quadro. A complexidade desta operação é O(m2n2) . Nesses casos, não é muito bom rastrear até 50 a 50 objetos de pixel. Esse problema é parcialmente resolvido com a redução do tamanho do vídeo, mas ao reduzir a imagem para menos de 240 pixels de largura, até grandes detalhes significativos começam a ser perdidos, o que torna o algoritmo sem sentido.
ASEF, MOSSE
Abordagem trivial ++?
Arregace as mangas e tente resolver os problemas acima.
Aumente a imagem original. Aplicamos a ele várias transformações afins leves. Você também pode adicionar ruído ou alterar a gama. Assim, em vez de uma única imagem, um conjunto de microdados de
P fotos. Havia muitas imagens, mas uma janela permaneceu. Então agora não vamos apenas cortar um retângulo da imagem, mas procurar um filtro
W o que dará uma boa resposta para todos
Ip . Transformamos o problema em um problema de minimização:
W: minW paraleloFp−W paralelo2,p em[1,P]
onde
paraleloFp−W paralelo2 - a soma dos quadrados das diferenças de pixel entre
W e a seção correspondente da localização exata do objeto no
p essa imagem sintética criada a partir de um quadro com marcação verdadeira.
Além disso, você pode amostrar retângulos
longe da localização do objeto rastreado e
maximizar a diferença mostrada acima.
É mais difícil sugerir que o filtro dê uma boa resposta em pontos próximos à localização exata do objeto. Sabemos que em
(x,y) aplicação de filtro com
L2 -metric deve dar 0, próximo - mais, longe - ainda mais. Além disso, não temos nenhuma direção preferida, a resposta deve ser centralmente simétrica em relação a
(x,y) . Parece que podemos expressar matematicamente como deve ser a resposta de um filtro aplicado às imagens de referência! A aparência exata pode variar dependendo da função de atenuação da resposta específica, mas todos amam os gaussianos? Portanto, assumimos que
W aplicado a
Fp idealmente deve dar um resultado
Gp=1− exp frac(x−i)2+(y−j)2 sigma2 . Portanto, o problema de minimização se transforma em:
Dp(i,j)= paraleloFp(i,j)−W paralelo2
W: minW paraleloDp(i,j)−Gp(i,j) paralelo2,p em[1,P
Agora, não minimizamos a resposta em um ponto, mas minimizamos o desvio da resposta da desejada.
Espere um segundo ... Conseguimos
P vezesm vezesn equações com
m vezesn variáveis a serem minimizadas. Parece que nós exageramos. Vamos voltar um pouco.
Truque principal
De todos os problemas, a maior dificuldade é a complexidade.
O(m2n2) . É possível criar algo além da divisão complicada de uma caixa de pesquisa em várias pequenas ou pesquisar na imagem em pequena resolução e ajustar a alta precisão?
Acontece que você pode! A análise analítica nos diz que o dobramento de funções no espaço comum é uma multiplicação de suas imagens de Fourier. Podemos aplicar a transformação rápida de Fourier às imagens, multiplicar suas frequências elemento por elemento e depois transformar o resultado novamente em uma matriz para
O(mn logmn) , que é muito mais rápido do que minimizar a matriz honestamente. Fourier! Quem teria pensado! Na era do fluxo tensor, ele ainda pode nos ajudar com a visão computacional.
(A propósito, isso demonstra o princípio matemático geral: se você não deseja resolver o problema no espaço
X mova-o para o espaço
Y , decida lá e transfira a decisão de volta. A solução alternativa é geralmente mais curta que a direta.)
Como mostrado acima, podemos usar correlação cruzada para localizar a amostra na imagem. Mas a correlação cruzada é uma convolução com reflexos horizontal e verticalmente
W . A análise analítica sugere que, neste caso, será necessário multiplicar as frequências
F em uma matriz conjugada complexa a uma matriz de frequência
W :
hatW( omega, nu)= mathcalF(W(x,y))
hatF( omega, nu)= mathcalF(F(x,y))
hatGconv( omega, nu)= mathcalF(Gconv(x,y))
Gconv=ÀsvezesW rightarrow hatGconv= hatF odot hatW∗
onde
Gconv= exp frac(x−i)2+(y−j)2 sigma2 - função de resposta perfeita na imagem de referência. Por favor note que
L2 Minimizamos a métrica e maximizamos a métrica de convolução; agora, quanto maior a resposta, melhor.
Se tivéssemos uma imagem, encontraríamos a matriz exata de frequência do filtro:
hatW∗= frac hatGconv hatF
onde o lado direito se refere à divisão elemento a elemento. Mas um pouco antes geramos
P imagens da fonte. Podemos aplicá-los com a abordagem de Fourier. Não há filtro com essas frequências que satisfaça idealmente todas as imagens, mas você pode obter algo bom o suficiente. Existem duas maneiras de resolver o problema:
- Você pode encontrar um conjunto de filtros ideais e depois fazer a média deles em um. É assim que os autores de Média de filtros exatos sintéticos (ASEF) seguem:
hatW∗= frac1P sumPp=1 hatW∗p= frac1P sumPp=1 frac hatGp hatFp
Aqui usamos a propriedade de linearidade das imagens de Fourier. Adicionando frequências, como mostrado acima, parecemos calcular vários pesos de filtro. - Você pode encontrar frequências de filtro que satisfazem todas as imagens em média, aproximadamente como L2 :
hatW∗: min hatW∗ sumPp=1 paralelo hatFp odot hatW∗− hatGp parallel2
Para encontrar o mínimo, você precisa derivar os elementos de filtro: frac delta delta hatW∗ sumPp=1 paralelo hatFp odot hatW∗− hatGp parallel2=0
Uma captura honesta dessa derivada pode ser encontrada no Visual Object Tracking usando os filtros de correlação adaptativos , que oferecem filtros mínimos de soma quadrada de erro quadrático (filtros MOSSE). O resultado é este: hatW∗= frac sumPp=1 hatGp odot hatFp∗ sumPp=1 hatFp odot hatFp∗
Hmm, como se elementos semelhantes estivessem envolvidos nas fórmulas. At
P=1 as fórmulas para ASEF e MOSSE são exatamente as mesmas.
hatW∗ para uma imagem pode ser representada como
hatW∗= frac hatGp hatFp= frac hatGp odot hatFp∗ hatFp odot hatFp∗
Substitua na fórmula pelo ASEF e obtenha
hatW∗= sumPp=1 frac hatGp odot hatFp∗ hatFp odot hatFp∗
Sim! Agora é muito melhor ver que ASEF e MOSSE diferem apenas no método de média dos filtros! Argumenta-se que o MOSSE produz filtros melhores que o ASEF. Parece lógico: ajustar melhor o conjunto de imagens como um todo é melhor do que calcular a média dos filtros.
Depois que chegamos
hatW∗ , calculamos a resposta no domínio da frequência
hatGconv= hatF odot hatW∗ , traduzimos para o domínio espacial e procuramos o máximo na matriz resultante
G . Onde o máximo é, há a nova posição do objeto.
Pontos adicionais
- Os termos nos denominadores das fórmulas têm um significado físico interessante. hatFp odot hatFp∗ É o espectro de energia de um retângulo com p essa imagem.
- Preste atenção à simetria interessante. Era necessário multiplicar as frequências do filtro pelas frequências da imagem para obter uma resposta. Agora você precisa multiplicar as frequências de resposta pelas frequências de imagem (e normalizar) para obter as frequências de filtro.
- Na vida real, a divisão elemento a elemento pode causar divisão por zero; portanto, uma constante de regularização geralmente é adicionada ao denominador epsilon . Argumenta-se que essa regularização faz com que o filtro preste mais atenção às baixas frequências, o que melhora a capacidade de generalização.
- Ao processar vídeo real, geralmente você deseja salvar informações sobre o objeto rastreado obtido de quadros anteriores. Ao passar para o próximo quadro, você não pode calcular hatW do zero e atualize o anterior. Fórmula de atualização para ASEF:
hatW∗i= frac etaP sumPp=1 frac hatGp hatFp+(1− eta) hatW∗i−1
Para o MOSSE, você precisa acumular o numerador e o denominador separadamente:Ai= eta sumPp=1 hatGp odot hatFp∗+(1− eta)Ai−1
Bi= eta sumPp=1 hatFp odot hatFp∗+(1− eta)Bi−1
hatW∗i= fracAiBi
onde eta - velocidade de aprendizado. - É importante lembrar que a transformação de Fourier não é exatamente a mesma que o cálculo G honestamente, conforme descrito no início do artigo. Ao calcular a FFT, os elementos ausentes não desaparecem, mas são substituídos no verso, como se a imagem estivesse em loop da direita para a esquerda e de baixo para cima. Mas no começo do artigo, já decidimos escurecer as bordas F , portanto, esse problema não terá um efeito perceptível.
- Como mencionado acima, a correlação cruzada tem uma característica desagradável: em geral, um filtro de luz pode dar uma forte resposta em áreas brancas da imagem, mesmo que não coincidam em áreas contrastantes. Os problemas não se limitam a isso. Mesmo um pixel correspondente com um valor fortemente positivo ou muito negativo pode interferir no filtro se a amostra como um todo tiver baixo contraste. Para suavizar esse efeito, uma transformação não linear de pixels da imagem deve ser incluída no pré-processamento, o que “pressionará” áreas muito claras e muito escuras no meio. Devido a isso, a coincidência dessas áreas contrastantes contribui mais fortemente para a métrica. Os artigos ASEF e MOSSE usam o logaritmo:
I= logI+1
onde estão os pixels I de 0 a 255. Na minha opinião, isso é muito severo e ignora o problema de forte resposta do filtro escuro em áreas pretas . Esse esquema funciona melhor:I=sinal(I−127) sqrt|I−127|
Depois vem a normalização, e acontece que a maioria dos elementos está centrada em torno de zero.
- Como esse algoritmo pode determinar que o objeto rastreado desapareceu do quadro? Uma análise mais detalhada da resposta recebida do próximo quadro ajudará aqui. Os criadores do MOSSE oferecem um indicador PSR - Peak to Sidelobe Ratio. Vamos gmax - elemento máximo G correspondente à nova posição do objeto (x,y) . Excluímos o quadrado da consideração 11 vezes11 em torno deste máximo. Calculamos a média e o desvio padrão para os pixels restantes ( musl, sigmasl ) Então
PSR= fracgmax− musl sigmasl
Se esse valor estiver acima de um determinado limite, a detecção será considerada bem-sucedida. O limite geralmente é calculado na região entre 3 e 10. Para detecções confiáveis, o PSR geralmente é mantido acima de 20.
(observe que aqui PSR não significa em absoluto o que geralmente significa na teoria do processamento de sinais; portanto, não pesquise no Google, nada de bom sairá) - O algoritmo é extremamente simples. O procedimento de rastreamento no Core-i7 em imagens com 320x400 usando a implementação OpenCV leva de 0,5 a 2 milissegundos, dependendo do tamanho do objeto rastreado.
Algoritmo MOSSE
Juntando tudo.
Condição geral:Matriz de frequência do filtro:
hatWMatrizes auxiliares para o cálculo de frequências de filtro:
A,BMatriz de frequência da resposta ideal desejada:
hatGVelocidade de treinamento durante o rastreamento:
etaO retângulo da posição atual do objeto:
RNúmero de transformações:
PLimiar de resposta:
PSRthrFunção auxiliar:
Treinamento . Entrada: Imagem
I velocidade atual de aprendizado
etacurrent- Anovo:=0,Bnovo:=0
- Até eu conseguir P transformações:
- Aplique uma pequena transformação afim aleatória centralizada no centro da imagem. R
- Cortar da imagem retangular com objeto F
- Aplique uma máscara para anular suavemente as bordas
- Traduzir F no domínio da frequência: hatF
- Anew=Anew+ hatG odot hatF∗
- Bnovo=Bnovo+ hatF odot hatF∗
- Se etacurrent geq1,0 então substitua A e B em Anew e Bnovo . Caso contrário:
B:= etaBnovo+(1− eta)B
A:= etaAnovo+(1− eta)A
- Calcular frequências de filtro:
hatW∗= fracAB
Inicialização . Entrada: Imagem
I retângulo em torno da posição do objeto
Rinit- R:=Rinit
- Prepare a resposta desejada G . Normalmente, essa é uma matriz completamente zero, com um pequeno gaussiano no centro.
- Treinamento : I 1,0
- Traduzir G no domínio da frequência: hatG
Rastreamento : Entrada: Imagem
I- Cortar retângulo F de I para a posição anterior existente do objeto R
- Aplique uma máscara para anular suavemente as bordas
- Traduzir F no domínio da frequência: hatF
- hatGresponse= hatW odot hatF∗
- Traduzir hatGresponse para o domínio espacial: Gresponse
- Encontre o máximo em Gresponse : gmax,(x,y)
- Calcular o poder de resposta PSR:= fracgmax− musl sigmasl
- Se PSR<PSRthr falha na saída
- Atualizar posição R . Ajuste R se ultrapassar a imagem ou foi decidido que o objeto aumentou / diminuiu.
- Treinamento : I , eta
- Retorno R
Os detalhes da implementação podem variar. Por exemplo
- Somente pode pré-processar F , não a imagem inteira.
- G pode ser recriado para cada transformação de imagem com função variável e largura de resposta.
- Você pode treinar vários filtros diferentes ao mesmo tempo em várias escalas do objeto para detectar movimentos à distância e nas proximidades.
Como é isso?
Para iniciantes, alguns exemplos da transformação bidimensional de Fourier.
Alguns exemplos simplesDeixe-me lembrá-lo de que o resultado da transformação é de valor complexo. As figuras abaixo mostram os grupos “imagem - valores absolutos do domínio da frequência em uma escala normal - valores absolutos do domínio da frequência em uma escala logarítmica”.
Linhas verticais:



A imagem muda da esquerda para a direita da mesma forma em qualquer posição vertical. Além disso, a mudança é periódica, com um período claro e um padrão claro. Portanto, nas imagens de frequências, você vê apenas frequências ao longo do eixo
x=0 .
Gaiola:



Observe que existem séries de frequências esperadas ao longo dos eixos
x=0 e
y=0 e frequências espúrias estranhas. Eles apareceram devido ao fato de que, em primeiro lugar, a imagem é finita, enquanto a imagem de Fourier é decomposta em uma quantidade bonita apenas para um sinal periódico infinito. Em segundo lugar, você pode ver que a imagem não forma um período exato nas bordas.
Linhas inclinadas:



Novamente, as duas frequências correspondentes à direção principal e as frequências espúrias são visíveis.
Linhas inclinadas mais distorção:



A imagem das frequências mostra várias direções características, mas já está se tornando difícil apresentar intuitivamente uma imagem nelas.
Para imagens do mundo real, é ainda mais difícil imaginar uma imagem na cabeça por suas frequências:



(as frequências no centro são fechadas para que não “iluminem” o restante do espectro)
Agora vamos para um exemplo de trabalho real:
Pacote de fotosImagem com objeto marcado:

Objeto cortado e pré-processado, seu espectro na escala usual e logarítmica (
F,| hatF|, log| hatF| ):



Resposta desejada (
G ):

Filtre as frequências em escala regular e logarítmica (
W,| hatW| ):


Pesos de filtro explícitos (sem transformações)
F ):

Observe que eles não participam do algoritmo em nenhum lugar - eles podem ser contados apenas por uma questão de interesse. Observe também que o
filtro se parece com isso . Seria de esperar que o filtro fosse algo semelhante à imagem original, mas isso nem sempre é verdade. Um filtro semelhante à própria imagem dificilmente daria a resposta gaussiana desejada.
A resposta do próximo quadro:

Embora não seja tão limpo quanto a resposta desejada, é fácil determinar o máximo nela.
O mesmo exemplo com uma resposta desejada mais estreita:
Pacote de fotosJá:
W :

Mais já:
W :

Com um valor máximo muito estreito no filtro, em vez de uma mancha preta, o olho se torna claramente visível.
W para os três casos anteriores
G quando usado para treinar 16 transformações da imagem de entrada:
Outro monte de fotosAmplo máximo:

Média máxima:

Máximo estreito:

Quanto mais transformações, menos o filtro se apega a elementos aleatórios. É especialmente visível que manchas aleatórias em preto e branco do meio desapareceram
W . Por outro lado, para um gaussiano estreito, o treinamento em várias figuras pode representar um sinal negativo: observe o "toque" formado no filtro ao redor do olho.
Se você quiser ver como fica ao vivo, baixe
aqui meu repositório de teste com a implementação do MOSSE com a saída de imagens de depuração. Você pode encontrar mais
opções MOSSE
no github. Além disso, está em
OpenCV .
Conclusão
Obrigado pela atenção, Habrovsk. O rastreamento MOSSE e ASEF não são os algoritmos mais complexos do mundo. O mais fácil é não apenas aplicá-las efetivamente, mas também entender como seus criadores foram orientados. Espero que minha explicação tenha ajudado você a entrar na cabeça dos pesquisadores para seguir o curso de seus pensamentos. Isso pode ser útil: o aprendizado de máquina não é um campo estático do conhecimento; há um lugar para criatividade e pesquisa. Tente se aprofundar em algum algoritmo estabelecido: descubra membros desnecessários para acelerar ou adicione alguns para fazê-lo funcionar melhor no seu caso particular. Você vai gostar!
Este artigo foi escrito com o suporte do DSSL.