Eu tenho um bitmap grande (digamos 3888x2592) em um arquivo. Agora, quero redimensionar esse bitmap para 800x533 e salvá-lo em outro arquivo. Normalmente, eu escalaria o bitmap chamando o Bitmap.createBitmap
método, mas ele precisa de um bitmap de origem como o primeiro argumento, o que não posso fornecer, pois carregar a imagem original em um objeto Bitmap excederia naturalmente a memória (veja aqui , por exemplo).
Também não consigo ler o bitmap, por exemplo, BitmapFactory.decodeFile(file, options)
fornecendo um BitmapFactory.Options.inSampleSize
, porque quero redimensioná-lo para uma largura e altura exatas. Usar inSampleSize
redimensionaria o bitmap para 972x648 (se eu usar inSampleSize=4
) ou para 778x518 (se eu usar inSampleSize=5
, o que não é nem uma potência de 2).
Também gostaria de evitar a leitura da imagem usando o inSampleSize com, por exemplo, 972x648 em uma primeira etapa e redimensioná-la para exatamente 800x533 em uma segunda etapa, porque a qualidade seria baixa em comparação com o redimensionamento direto da imagem original.
Para resumir minha pergunta: Existe uma maneira de ler um arquivo de imagem grande com 10MP ou mais e salvá-lo em um novo arquivo de imagem, redimensionado para uma nova largura e altura específica, sem obter uma exceção OutOfMemory?
Também tentei BitmapFactory.decodeFile(file, options)
definir os valores Options.outHeight e Options.outWidth manualmente para 800 e 533, mas não funciona dessa maneira.
Respostas:
Não . Adoraria que alguém me corrigisse, mas aceitei a abordagem de carregamento / redimensionamento que você tentou como um compromisso.
Aqui estão as etapas para quem estiver navegando:
inSampleSize
que ainda produz uma imagem maior que a sua meta.BitmapFactory.decodeFile(file, options)
, passando inSampleSize como uma opção.Bitmap.createScaledBitmap()
.fonte
Justin resposta traduzida em código (funciona perfeito para mim):
fonte
System.gc()
por favorEstas são as soluções de 'Mojo Risin e' Ofir "combinadas". Isso fornecerá uma imagem redimensionada proporcionalmente com os limites de largura e altura máx.
Para mim, ele tem funcionado bem em 5 imagens MegaPixel abaixo.
fonte
Por que não usar a API?
fonte
Reconhecendo a outra excelente resposta até agora, o melhor código que já vi para isso está na documentação da ferramenta de tirar fotos.
Consulte a seção "Decodificar uma imagem em escala".
http://developer.android.com/training/camera/photobasics.html
A solução que ela propõe é uma solução redimensionada e dimensionada como as outras aqui, mas é bem legal.
Copiei o código abaixo como uma função pronta para uso por conveniência.
fonte
Depois de ler essas respostas e a documentação do Android, veja o código para redimensionar o bitmap sem carregá-lo na memória:
fonte
Quando tenho bitmaps grandes e quero decodificá-los redimensionados, uso o seguinte
fonte
Isso pode ser útil para alguém que está analisando essa pergunta. Reescrevi o código de Justin para permitir que o método recebesse também o objeto de tamanho desejado. Isso funciona muito bem ao usar o Canvas. Todo o crédito deve ser atribuído ao JUSTIN pelo seu excelente código inicial.
O código de Justin é MUITO eficaz na redução da sobrecarga de trabalho com bitmaps grandes.
fonte
Não sei se minha solução é a melhor prática, mas consegui carregar um bitmap com o dimensionamento desejado usando as opções
inDensity
einTargetDensity
.inDensity
é0
inicialmente quando não está carregando um recurso extraível, portanto, essa abordagem é para carregar imagens sem recurso.As variáveis
imageUri
,maxImageSideLength
econtext
são parâmetros de meu método. Postei apenas a implementação do método sem o AsyncTask de quebra para maior clareza.fonte
Considerando que você deseja redimensionar para o tamanho exato e manter a qualidade necessária, acho que você deve tentar isso.
Motivação: o dimensionamento de várias etapas pode fornecer uma imagem de qualidade mais alta, no entanto, não há garantia de que funcione melhor do que usar alta inSampleSize. Na verdade, acho que você também pode usar inSampleSize como 5 (não pow de 2) para ter dimensionamento direto em uma operação. Ou basta usar 4 e, em seguida, você pode usar essa imagem na interface do usuário. se você o enviar para o servidor -, poderá dimensionar para o tamanho exato no lado do servidor, o que permite usar técnicas avançadas de dimensionamento.
Notas: se o Bitmap carregado na etapa 3 for pelo menos 4 vezes maior (portanto, 4 * targetWidth <width), você provavelmente poderá usar vários redimensionamentos para obter melhor qualidade. pelo menos, que funciona em java genérico, no android você não tem a opção de especificar a interpolação usada para dimensionar http://today.java.net/pub/a/today/2007/04/03/perils-of- image-getscaledinstance.html
fonte
Eu usei código como este:
Eu tentei a imagem original é 1230 x 1230 e o bitmap diz que é 330 x 330.
E se tentei 2590 x 3849, obterei o OutOfMemoryError.
Eu rastreei, ele ainda joga OutOfMemoryError na linha "BitmapFactory.decodeStream (is, null, options);", se o bitmap original é muito grande ...
fonte
O código acima tornou um pouco mais limpo. Os InputStreams finalmente fecharam o invólucro para garantir que também fiquem fechados:
* Nota
Input: InputStream é, int w, int h
Saída: Bitmap
fonte
Para dimensionar a imagem da maneira "correta", sem pular nenhum pixel, você teria que se conectar ao decodificador de imagem para realizar a amostragem descendente linha por linha. O Android (e a biblioteca Skia que o sustenta) não fornece esses ganchos, então você teria que usar o seu. Supondo que você esteja falando de imagens jpeg, sua melhor aposta seria usar a libjpeg diretamente, em C.
Dadas as complexidades envolvidas, o uso da subamostra de duas etapas e depois da nova escala é provavelmente o melhor para aplicativos do tipo visualização de imagem.
fonte
Aqui está um artigo que tem uma abordagem diferente para redimensionar. Ele tentará carregar o maior bitmap possível na memória com base na memória disponível no processo e, em seguida, realizará as transformações.
http://bricolsoftconsulting.com/2012/12/07/handling-large-images-on-android/
fonte
Se você realmente quiser redimensionar uma etapa, provavelmente poderá carregar bitmap inteiro se android: largeHeap = true, mas como você pode ver, isso não é realmente recomendável.
Do docs: android: largeHeap Se os processos do seu aplicativo devem ser criados com um grande heap Dalvik. Isso se aplica a todos os processos criados para o aplicativo. Aplica-se apenas ao primeiro aplicativo carregado em um processo; se você estiver usando um ID de usuário compartilhado para permitir que vários aplicativos usem um processo, todos deverão usar essa opção de forma consistente ou terão resultados imprevisíveis. A maioria dos aplicativos não precisa disso e deve se concentrar em reduzir o uso geral de memória para melhorar o desempenho. Habilitar isso também não garante um aumento fixo na memória disponível, porque alguns dispositivos são limitados por sua memória total disponível.
fonte
Há um ótimo artigo sobre esse problema exato no site do desenvolvedor do Android: Carregando bitmaps grandes com eficiência
fonte
Isso funcionou para mim. A função obtém um caminho para um arquivo no cartão SD e retorna um Bitmap no tamanho máximo de exibição. O código é do Ofir com algumas alterações, como o arquivo de imagem no sd, em vez de um Ressource e a witdth e heigth são obtidas do objeto de exibição.
fonte
Aqui está o código que uso, sem problemas na decodificação de imagens grandes na memória do Android. Consegui decodificar imagens maiores que 20 MB, desde que meus parâmetros de entrada estejam em torno de 1024x1024. Você pode salvar o bitmap retornado em outro arquivo. Abaixo desse método, há outro método que também uso para dimensionar imagens para um novo bitmap. Sinta-se livre para usar este código como desejar.
NOTA: Os métodos não têm nada a ver um com o outro, exceto o método createScaledBitmap chama o método de decodificação acima. Observe que a largura e a altura podem mudar da imagem original.
fonte
ou:
fonte
eu uso
Integer.numberOfLeadingZeros
para calcular o melhor tamanho da amostra, melhor desempenho.Código completo no kotlin:
fonte
Redimensione o bitmap usando o seguinte código
O mesmo também é explicado na seguinte dica / truque
http://www.codeproject.com/Tips/625810/Android-Image-Operations-Using-BitmapFactory
fonte