انواع داده های تصویری

Image data types and what they mean

در skimage، تصاویر به سادگی با آرایه قابل استفاده هستند و انواع مختلفی از داده ها[1]، یعنی "dtypes" را پشتیبانی می کنند. برای اجتناب از تحریف شدت تصویر، فرض می کنیم که تصاویر از محدوده dtype زیر استفاده می کنند:

توجه داشته باشید که تصاویر با اعشار شناور باید در محدوده -1 تا 1 محدود شوند حتی اگر نوع داده خودشان از این محدوده فراتر رود. از طرف دیگر، همه انواع عدد صحیح دارای مقدار شدت پیکسل هستند که می تواند کل محدوده نوع داده را پوشش دهد. به جز چند استثنا، تصاویر integet از نوع 64 بیتی پشتیبانی نمی شوند.

توابع در skimage به گونه ای طراحی شده اند که هر یک از این نوع داده ای را می پذیرند، اما ، برای کارآیی، ممکن است تصویری از یک نوع دیگر را بازگردانند. اگر به نوع خاصی نیاز دارید، skimage عملکردهای مفیدی را ارائه می دهد که نوع dtype را تبدیل کرده و شدت تصویر را مجدداً مجدداً نمایش می دهد. هرگز نباید از astype در تصویر استفاده کنید، زیرا این مفروضات مربوط به محدوده داده ای را نقض می کند:

>>> from skimage.util import img_as_float
>>> image = np.arange(0, 50, 10, dtype=np.uint8)
>>> print(image.astype(float)) # These float values are out of range.
[  0.  10.  20.  30.  40.]
>>> print(img_as_float(image))
[ 0.  0.03921569  0.07843137  0.11764706  0.15686275]

انواع ورودی ها

اگرچه هدف حفظ محدوده داده و نوع تصاویر ورودی است، توابع ممکن است فقط زیرمجموعه ای از این نوع داده ها را پشتیبانی کنند. در چنین حالتی، ورودی به نوع مورد نیاز (در صورت امکان) تبدیل می شود و در صورت نیاز به یک کپی حافظه، یک پیام هشدار بر روی گزارش چاپ می شود. الزامات نوع باید در مستندات پروژه ی شما ذکر شود.

توابع مفید زیر در بسته اصلی در دسترس توسعه دهندگان و کاربران است:

این توابع تصاویر را به نوع مورد نظر شما تبدیل کرده و مقادیر آنها را به طور اعداد صحیح مثبت تبدیل می کند:

>>> from skimage.util import img_as_ubyte
>>> image = np.array([0, 0.5, 1], dtype=float)
>>> img_as_ubyte(image)
array([0, 128, 255], dtype=uint8)

تفاوت uint و int در این است که در uint مقدار منفی نداریم زیرا کاراکتر u به معنی unsigned یا بی علامت می باشد که این به معنی مثبت بودن کل بازه می باشد ولی در int بازه ی عدد شامل اعداد منفی و مثبت می شود.

مراقب باشید! این تبدیل ها می تواند منجر به از دست دادن دقت شود، زیرا 8 بیت نمی تواند به اندازه 64 بیت اطلاعات را در خود نگه دارد:

>>> image = np.array([0, 0.5, 0.503, 1], dtype=float)
>>> image_as_ubyte(image)
array([0, 128, 128, 255], dtype=uint8)

علاوه بر این، برخی از توابع یک آرگومان preserve_range می گیرند که در آن تبدیل محدوده مناسب است اما ضروری نیست. به عنوان مثال، درون یابی در transform.warp به تصویری از نوع float نیاز دارد که باید محدوده ای در [0 ، 1] داشته باشد. بنابراین، به طور پیش فرض، تصاویر ورودی به این محدوده مقیاس بندی می شوند. با این حال، در برخی موارد، مقادیر تصویر نشان دهنده اندازه گیری های فیزیکی، مانند دما یا مقادیر بارندگی است که کاربر نمی خواهد افزایش یابد. با preserve_range = True، محدوده اصلی داده ها حفظ می شود، حتی اگر خروجی یک تصویر با مقادیر float باشد. سپس کاربران باید اطمینان حاصل کنند که این تصویر غیر استاندارد به درستی توسط توابع پایین دست پردازش می شود، که ممکن است تصویری را در [0 ، 1] انتظار داشته باشند.

>>> from skimage import data
>>> from skimage.transform import rescale
>>> image = data.coins()
>>> image.dtype, image.min(), image.max(), image.shape
(dtype('uint8'), 1, 252, (303, 384))
>>> rescaled = rescale(image, 0.5)
>>> (rescaled.dtype, np.round(rescaled.min(), 4),
...  np.round(rescaled.max(), 4), rescaled.shape)
(dtype('float64'), 0.0147, 0.9456, (152, 192))
>>> rescaled = rescale(image, 0.5, preserve_range=True)
>>> (rescaled.dtype, np.round(rescaled.min()),
...  np.round(rescaled.max()), rescaled.shape
(dtype('float64'), 4.0, 241.0, (152, 192))

انواع خروجی ها

نوع خروجی یک تابع توسط نویسنده ی آن تابع تعیین می شود و به نفع کاربر ثبت می شود. در حالی که این امر مستلزم این است که کاربر صراحتاً خروجی را به هر فرمت مورد نیازی که می خواهد تبدیل کند، اما اطمینان حاصل می کند که هیچ کپی داده به صورت غیر ضروری رخ نمی دهد.

کاربری که به نوع خاصی از خروجی نیاز دارد (به عنوان مثال، برای اهداف نمایش)، می تواند بنویسد:

>>> from skimage.util import img_as_uint
>>> out = img_as_uint(sobel(image))
>>> plt.imshow(out)

کار با OpenCV

ممکن است لازم باشد از تصویری که با استفاده از skimage با OpenCV ایجاد شده یا بالعکس استفاده کنید. داده های تصویری OpenCV در NumPy قابل دسترسی است. OpenCV برای تصاویر رنگی از BGR (به جای RGB scikit-image) استفاده می کند و نوع آن به طور پیش فرض uint8 است (انواع داده های تصویر و منظور آنها را ببینید). BGR مخفف Blue Green Red است.

تبدیل BGR به RGB و بلعکس

تصاویر رنگی در skimage و OpenCV دارای 3 بعد هستند: عرض ، ارتفاع و رنگ. RGB و BGR از فضای رنگ یکسانی استفاده می کنند، با این تفاوت که ترتیب رنگ ها در این دو معکوس است.

توجه داشته باشید که در scikit-image ما معمولاً به جای عرض و ارتفاع به سطرها و ستون ها اشاره می کنیم (به قراردادهای مختصات مراجعه کنید).

دستورالعمل زیر به طور موثری ترتیب رنگ ها را برعکس می کند و سطرها و ستون ها را بی تأثیر می گذارد.

>>> image = image[:, :, ::-1]

استفاده یک تصویر از OpenCV همراه با skimage

اگر cv_image آرایه ای از بایت های بدون علامت است، skimage به طور پیش فرض آن را درک می کند. اگر ترجیح می دهید با تصاویر نقطه شناور یا float کار کنید، از img_as_float() می توانید برای تبدیل تصویر استفاده کنید:

>>> from skimage.util import img_as_float
>>> image = img_as_float(any_opencv_image)

استفاده یک تصویر از skimage با OpenCV

برعکس این کار را می توان با img_as_ubyte() به دست آورد:

>>> from skimage.util import img_as_ubyte
>>> cv_image = img_as_ubyte(any_skimage_image)

خط لوله پردازش تصویر

این رفتار انواع داده ای به شما امکان می دهد بدون نگرانی در مورد نوع تصویر، هرگونه عملکرد skimage را به هم متصل کنید. از طرف دیگر، اگر می خواهید از یک تابع سفارشی استفاده کنید که به نوع خاصی نیاز دارد، باید یکی از توابع تبدیل نوع داده را فراخوانی کنید (در اینجا، func1 و func2 توابع skimage هستند):

>>> from skimage.util import img_as_float
>>> image = img_as_float(func1(func2(image)))
>>> processed_image = custom_func(image)

بهتر از این، می توانید تصاویر را به صورت داخلی تبدیل کرده و از یک خط لوله پردازش ساده استفاده کنید:

>>> def custom_func(image):
...     image = img_as_float(image)
...     # do something
...
>>> processed_image = custom_func(func1(func2(image)))

مقیاس بندی مجدد مقدار intensity

در صورت امکان، توابع باید ازblindly stretching image خودداری کنند (به عنوان مثال تغییر مجدد یک تصویر float به طوری که شدت حداقل و حداکثر 0 و 1 باشد)، زیرا این می تواند یک تصویر را به شدت تحریف کند. به عنوان مثال، اگر به دنبال نشانگرهای روشن در تصاویر تیره هستید، ممکن است تصویری وجود داشته باشد که هیچ نشانگری در آن وجود نداشته باشد. blindly stretching آن در محدوده کامل باعث می شود که نویزهای پس زمینه شبیه markerها یا نشانگرها باشد.

با این وجود، گاهی اوقات تصاویری دارید که باید در تمام محدوده شدت قرار داشته باشند اما ندارند. به عنوان مثال، برخی از دوربین ها تصاویری با عمق 10، 12 یا 14 بیت در هر پیکسل ذخیره می کنند. اگر این تصاویر در یک آرایه با نوع داده ای uint16 ذخیره شوند، آنگاه تصویر در دامنه full intensity گسترش نمی یابد و بنابراین، تیره تر از آنچه باید نشان داده می شود. برای تصحیح این امر، می توانید از تابع rescale_intensity برای مجدد مقیاس بندی تصویر استفاده کنید تا از محدوده کامل نوع استفاده کند:

>>> from skimage import exposure
>>> image = exposure.rescale_intensity(img10bit, in_range=(0, 2**10 - 1))

در اینجا، آرگومان in_range برای حداکثر محدوده یک تصویر 10 بیتی تنظیم شده است. به طور پیش فرض، rescale_intensity مقادیر in_range را برای مطابقت با محدوده نوع داده افزایش می دهد. rescale_intensity همچنین رشته هایی را به عنوان ورودی های درون و خارج از محدوده می پذیرد، بنابراین مثال بالا را می توان به صورت زیر نیز نوشت:

>>> image = exposure.rescale_intensity(img10bit, in_range='uint10')

در مورد مقادیر منفی توجه کنید

افراد اغلب تصاویر را با نوع داده ی آن ها نشان می دهند، حتی اگر فقط مقادیر مثبت تصویر را دستکاری کنند (به عنوان مثال، فقط از 0-127 در یک تصویر int8 استفاده می کنند). به همین دلیل، توابع تبدیل فقط مقادیر مثبت یک نوع داده شده را در کل دامنه یک نوع بدون علامت پخش می کند. به عبارت دیگر، مقادیر منفی هنگام تبدیل از نوع های امضا شده به امضا نشده به صفر بریده می شود. (مقادیر منفی هنگام تبدیل بین دو نوع داده ای باید حفظ شود.) برای جلوگیری از این رفتار برش، باید تصویر خود را از قبل دوباره تنظیم کنید:

>>> image = exposure.rescale_intensity(img_int32, out_range=(0, 2**31 - 1))
>>> img_uint8 = img_as_ubyte(image)

این رفتار متقارن است: مقادیر در یک نوع بدون علامت فقط در محدوده مثبت یک نوع داده ای شده است.

منابع

https://docs.scipy.org/doc/numpy/user/basics.types.html

این بخش به پایان رسید اگر سوالی در ارتباط با هر یک از بخش های بالا دارید در بخش issueها از من بپرسید.

Last updated