Mam dużą mapę bitową (powiedzmy 3888 x 2592) w pliku. Teraz chcę zmienić rozmiar tej mapy bitowej na 800 x 533 i zapisać ją w innym pliku. I normalnie skalowania bitmapy poprzez wywołanie Bitmap.createBitmap
metody, ale potrzebuje bitmapę źródłowego jako pierwszy argument, którego nie może zapewnić ponieważ ładuje obraz oryginalny do obiektu Bitmap byłoby oczywiście przekroczyć pamięć (patrz tutaj , na przykład).
Nie mogę również odczytać mapy bitowej za pomocą, na przykład, BitmapFactory.decodeFile(file, options)
podając BitmapFactory.Options.inSampleSize
, ponieważ chcę zmienić rozmiar do dokładnej szerokości i wysokości. Użycie inSampleSize
spowoduje zmianę rozmiaru mapy bitowej na 972 x 648 (jeśli używam inSampleSize=4
) lub na 778 x 518 (jeśli używam inSampleSize=5
, co nie jest nawet potęgą 2).
Chciałbym również uniknąć czytania obrazu przy użyciu inSampleSize z, na przykład, 972 x 648 w pierwszym kroku, a następnie zmiany rozmiaru do dokładnie 800 x 533 w drugim kroku, ponieważ jakość byłaby słaba w porównaniu z bezpośrednią zmianą rozmiaru oryginalnego obrazu.
Podsumowując moje pytanie: Czy istnieje sposób na odczytanie dużego pliku obrazu o rozmiarze 10 MP lub większym i zapisanie go w nowym pliku obrazu, zmienionym na określoną nową szerokość i wysokość, bez uzyskania wyjątku OutOfMemory?
Próbowałem też BitmapFactory.decodeFile(file, options)
ręcznie ustawić wartości Options.outHeight i Options.outWidth ręcznie na 800 i 533, ale to nie działa w ten sposób.
Odpowiedzi:
Nie. Chciałbym, żeby ktoś mnie poprawił, ale zaakceptowałem podejście polegające na obciążeniu / zmianie rozmiaru, które próbowałaś jako kompromis.
Oto kroki dla każdego, kto przegląda:
inSampleSize
które wciąż daje obraz większy niż twój cel.BitmapFactory.decodeFile(file, options)
, przekazując inSampleSize jako opcję.Bitmap.createScaledBitmap()
.źródło
Odpowiedź Justina przetłumaczona na kod (działa dla mnie idealnie):
źródło
System.gc()
proszęTo są „połączone” rozwiązania Mojo Risina i Ofira. To da ci proporcjonalnie zmieniony rozmiar obrazu z granicami maksymalnej szerokości i maksymalnej wysokości.
Dla mnie działa dobrze na 5 obrazach MegaPixel i poniżej.
źródło
Dlaczego nie skorzystać z interfejsu API?
źródło
Biorąc pod uwagę drugą doskonałą do tej pory odpowiedź, najlepszy kod, jaki do tej pory widziałem, znajduje się w dokumentacji narzędzia do robienia zdjęć.
Zobacz sekcję „Dekodowanie skalowanego obrazu”.
http://developer.android.com/training/camera/photobasics.html
Rozwiązaniem, które proponuje, jest rozwiązanie zmiany rozmiaru, a następnie skalowania, jak inne tutaj, ale jest całkiem fajne.
Dla wygody skopiowałem poniższy kod jako funkcję gotową do użycia.
źródło
Po przeczytaniu tych odpowiedzi i dokumentacji systemu Android oto kod zmiany rozmiaru mapy bitowej bez ładowania jej do pamięci:
źródło
Kiedy mam duże bitmapy i chcę je zdekodować, zmieniam ich rozmiar, używam następujących
źródło
Może to być przydatne dla kogoś, kto patrzy na to pytanie. Przepisałem kod Justina, aby umożliwić metodzie otrzymanie wymaganego obiektu rozmiaru docelowego. Działa to bardzo dobrze podczas korzystania z Canvas. Wszystkie zasługi należy się JUSTINOWI za jego świetny kod początkowy.
Kod Justina BARDZO skutecznie zmniejsza obciążenie związane z pracą z dużymi mapami bitowymi.
źródło
Nie wiem, czy moje rozwiązanie jest najlepszą praktyką, ale udało mi się załadować bitmapę z pożądanym skalowaniem za pomocą opcji
inDensity
iinTargetDensity
.inDensity
jest0
początkowo, gdy nie ładuje zasobu, który można wyciągnąć, więc to podejście służy do ładowania obrazów innych niż zasoby.Zmienne
imageUri
,maxImageSideLength
icontext
są parametry mojej metody. Dla przejrzystości opublikowałem tylko implementację metody bez owijania AsyncTask.źródło
Biorąc pod uwagę, że chcesz zmienić rozmiar do dokładnego rozmiaru i chcesz zachować tyle jakości, ile potrzebujesz, myślę, że powinieneś spróbować.
Motywacja: skalowanie wielostopniowe może dać obraz o wyższej jakości, jednak nie ma gwarancji, że będzie działał lepiej niż przy użyciu wysokiej inSampleSize. Właściwie myślę, że można również użyć inSampleSize jak 5 (nie pow 2), aby uzyskać bezpośrednie skalowanie w jednej operacji. Lub po prostu użyj 4, a następnie możesz po prostu użyć tego obrazu w interfejsie użytkownika. jeśli wyślesz go do serwera - możesz wykonać skalowanie do dokładnego rozmiaru po stronie serwera, co pozwala na użycie zaawansowanych technik skalowania.
Uwagi: jeśli bitmapa załadowana w kroku 3 jest co najmniej 4 razy większa (więc 4 * szerokość docelowa <szerokość) prawdopodobnie można użyć kilku zmian rozmiaru, aby uzyskać lepszą jakość. przynajmniej działa to w ogólnej Javie, w Androidzie nie masz opcji określania interpolacji używanej do skalowania http://today.java.net/pub/a/today/2007/04/03/perils-of- image-getscaledinstance.html
źródło
Użyłem takiego kodu:
Próbowałem oryginalnego obrazu to 1230 x 1230, a według mapy bitowej jest to 330 x 330.
A jeśli spróbuję 2590 x 3849, dostanę błąd OutOfMemoryError.
Prześledziłem go, nadal wyrzuca OutOfMemoryError w linii „BitmapFactory.decodeStream (is, null, options);”, jeśli oryginalna mapa bitowa jest zbyt duża ...
źródło
Powyższy kod uczynił trochę czystszym. Strumienie wejściowe w końcu są ściśle zamykane, aby zapewnić, że również zostaną zamknięte:
* Uwaga
Wejście: InputStream jest, int w, int h
Wyjście: Bitmapa
źródło
Aby przeskalować obraz w „prawidłowy” sposób, bez pomijania pikseli, należy podłączyć się do dekodera obrazu, aby wykonać próbkowanie w dół rząd po rzędzie. Android (i leżąca u jego podstaw biblioteka Skia) nie oferuje takich haczyków, więc trzeba by rzucić własną. Zakładając, że mówisz o obrazach JPEG, najlepszym rozwiązaniem byłoby użycie libjpeg bezpośrednio w C.
Biorąc pod uwagę złożoność, korzystanie z dwuetapowej podpróbki, a następnie przeskalowania jest prawdopodobnie najlepsze dla aplikacji typu podgląd obrazu.
źródło
Oto artykuł, który ma inne podejście do zmiany rozmiaru. Podejmie próbę załadowania największej możliwej mapy bitowej do pamięci na podstawie dostępnej pamięci w procesie, a następnie przeprowadzi transformacje.
http://bricolsoftconsulting.com/2012/12/07/handling-large-images-on-android/
źródło
Jeśli absolutnie chcesz zmienić rozmiar o jeden krok, prawdopodobnie możesz załadować całą mapę bitową, jeśli Android: largeHeap = true, ale jak widać, nie jest to zalecane.
Z dokumentacji: android: largeHeap Określa, czy procesy aplikacji powinny być tworzone za pomocą dużej sterty Dalvik. Dotyczy to wszystkich procesów utworzonych dla aplikacji. Dotyczy to tylko pierwszej aplikacji załadowanej do procesu; jeśli używasz wspólnego identyfikatora użytkownika, aby umożliwić wielu aplikacjom korzystanie z procesu, wszystkie one muszą konsekwentnie korzystać z tej opcji lub będą miały nieprzewidywalne wyniki. Większość aplikacji nie powinna tego potrzebować i zamiast tego powinna skupić się na zmniejszeniu całkowitego zużycia pamięci w celu zwiększenia wydajności. Włączenie tego również nie gwarantuje stałego zwiększenia dostępnej pamięci, ponieważ niektóre urządzenia są ograniczone przez całkowitą dostępną pamięć.
źródło
Świetny artykuł na ten temat znajduje się na stronie programisty Androida: Efektywne ładowanie dużych map bitowych
źródło
To zadziałało dla mnie. Funkcja pobiera ścieżkę do pliku na karcie SD i zwraca mapę bitową w maksymalnym możliwym do wyświetlenia rozmiarze. Kod pochodzi z Ofir, z pewnymi zmianami, takimi jak plik obrazu na sd zamiast Ressource, a wysokość i wysokość są pobierane z obiektu wyświetlanego.
źródło
Oto kod, którego używam, który nie ma problemów z dekodowaniem dużych obrazów w pamięci na Androidzie. Byłem w stanie dekodować obrazy większe niż 20 MB, o ile moje parametry wejściowe wynoszą około 1024x1024. Możesz zapisać zwróconą mapę bitową w innym pliku. Poniżej tej metody znajduje się kolejna metoda, której używam również do skalowania obrazów do nowej mapy bitowej. Możesz użyć tego kodu, jak chcesz.
UWAGA: Metody nie mają ze sobą nic wspólnego oprócz metody dekodowania wywołań metody createScaledBitmap powyżej. Uwaga szerokość i wysokość mogą się różnić od oryginalnego obrazu.
źródło
lub:
źródło
używam
Integer.numberOfLeadingZeros
do obliczania najlepszego rozmiaru próbki, lepszej wydajności.Pełny kod w kotlin:
źródło
Zmień rozmiar mapy bitowej za pomocą następującego kodu
To samo wyjaśniono w poniższej wskazówce / sztuczce
http://www.codeproject.com/Tips/625810/Android-Image-Operations-Using-BitmapFactory
źródło