I have a 3d numpy array, I have saved that array using cv2.imwrite function in form of jpg image, after reading that image I am trying to compare it with original array and found that they are not matching. It also doesn't seems to be RGB or BGR issue.
f1 <- Original Array
cv2.imwrite("fm1.jpg", f1)
f1_ = cv2.imread("fm1.jpg")
print (f1[0][:5])
print ("---------------------")
print (f1_[0][:5])
Output:
[[105 115 150]
[ 75 121 139]
[111 116 151]
[110 118 151]
[ 94 119 146]]
---------------------
[[ 90 121 144]
[ 91 116 142]
[107 118 150]
[110 116 151]
[110 115 148]]
First of all, my question is different to How do I convert image to 2-bit per pixel? and unfortunately its solution does not work in my case...
I need to convert images to 2-bit per pixel grayscale BMP format. The sample image has the following properties:
Color Model: RGB
Depth: 4
Is Indexed: 1
Dimension: 800x600
Size: 240,070 bytes (4 bits per pixel but only last 2 bits are used to identify the gray scales as 0/1/2/3 in decimal or 0000/0001/0010/0011 in binary, plus 70 bytes BMP metadata or whatever)
The Hex values of the beginning part of the sample BMP image:
The 3s represent white pixels at the beginning of the image. Further down there are some 0s, 1s and 2s representing black, dark gray and light gray:
With the command below,
convert pic.png -colorspace gray +matte -depth 2 out.bmp
I can get visually correct 4-level grayscale image, but wrong depth or size per pixel:
Color Model: RGB
Depth: 8 (expect 4)
Dimension: 800x504
Size: 1,209,738 bytes (something like 3 bytes per pixel, plus metadata)
(no mention of indexed colour space)
Please help...
OK, I have written a Python script following Mark's hints (see comments under original question) to manually create a 4-level gray scale BMP with 4bpp. This specific BMP format construction is for the 4.3 inch e-paper display module made by WaveShare. Specs can be found here: http://www.waveshare.com/wiki/4.3inch_e-Paper
Here's how to pipe the original image to my code and save the outcome.
convert in.png -colorspace gray +matte -colors 4 -depth 2 -resize '800x600>' pgm:- | ./4_level_gray_4bpp_BMP_converter.py > out.bmp
Contents of 4_level_gray_4bpp_BMP_converter.py:
#!/usr/bin/env python
"""
### Sample BMP header structure, total = 70 bytes
### !!! little-endian !!!
Bitmap file header 14 bytes
42 4D "BM"
C6 A9 03 00 FileSize = 240,070 <= dynamic value
00 00 Reserved
00 00 Reserved
46 00 00 00 Offset = 70 = 14+56
DIB header (bitmap information header)
BITMAPV3INFOHEADER 56 bytes
28 00 00 00 Size = 40
20 03 00 00 Width = 800 <= dynamic value
58 02 00 00 Height = 600 <= dynamic value
01 00 Planes = 1
04 00 BitCount = 4
00 00 00 00 compression
00 00 00 00 SizeImage
00 00 00 00 XPerlPerMeter
00 00 00 00 YPerlPerMeter
04 00 00 00 Colours used = 4
00 00 00 00 ColorImportant
00 00 00 00 Colour definition index 0
55 55 55 00 Colour definition index 1
AA AA AA 00 Colour definition index 2
FF FF FF 00 Colour definition index 3
"""
# to insert File Size, Width and Height with hex strings in order
BMP_HEADER = "42 4D %s 00 00 00 00 46 00 00 00 28 00 00 00 %s %s 01 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 55 55 55 00 AA AA AA 00 FF FF FF 00"
BMP_HEADER_SIZE = 70
BPP = 4
BYTE = 8
ALIGNMENT = 4 # bytes per row
import sys
from re import findall
DIMENTIONS = 1
PIXELS = 3
BLACK = "0"
DARK_GRAY = "1"
GRAY = "2"
WHITE = "3"
# sample data:
# ['P5\n', '610 590\n', '255\n', '<1 byte per pixel for 4 levels of gray>']
# where item 1 is always P5, item 2 is width heigh, item 3 is always 255, items 4 is pixels/colours
data = sys.stdin.readlines()
width = int(data[DIMENTIONS].strip().split(' ')[0])
height = int(data[DIMENTIONS].strip().split(' ')[1])
if not width*height == len(data[PIXELS]):
print "Error: pixel data (%s bytes) and image size (%dx%d pixels) do not match" % (len(data[PIXELS]),width,height)
sys.exit()
colours = [] # enumerate 4 gray levels
for p in data[PIXELS]:
if not p in colours:
colours.append(p)
if len(colours) == 4:
break
# it's possible for the converted pixels to have less than 4 gray levels
colours = sorted(colours) # sort from low to high
# map each colour to e-paper gray indexes
# creates hex string of pixels
# e.g. "0033322222110200....", which is 4 level gray with 4bpp
if len(colours) == 1: # unlikely, but let's have this case here
pixels = data[PIXELS].replace(colours[0],BLACK)
elif len(colours) == 2: # black & white
pixels = data[PIXELS].replace(colours[0],BLACK)\
.replace(colours[1],WHITE)
elif len(colours) == 3:
pixels = data[PIXELS].replace(colours[0],DARK_GRAY)\
.replace(colours[1],GRAY)\
.replace(colours[2],WHITE)
else: # 4 grays as expected
pixels = data[PIXELS].replace(colours[0],BLACK)\
.replace(colours[1],DARK_GRAY)\
.replace(colours[2],GRAY)\
.replace(colours[3],WHITE)
# BMP pixel array starts from last row to first row
# and must be aligned to 4 bytes or 8 pixels
padding = "F" * ((BYTE/BPP) * ALIGNMENT - width % ((BYTE/BPP) * ALIGNMENT))
aligned_pixels = ''.join([pixels[i:i+width]+padding for i in range(0, len(pixels), width)][::-1])
# convert hex string to represented byte values
def Hex2Bytes(hexStr):
hexStr = ''.join(hexStr.split(" "))
bytes = []
for i in range(0, len(hexStr), 2):
byte = int(hexStr[i:i+2],16)
bytes.append(chr(byte))
return ''.join(bytes)
# convert integer to 4-byte little endian hex string
# e.g. 800 => 0x320 => 00000320 (big-endian) =>20030000 (little-endian)
def i2LeHexStr(i):
be_hex = ('0000000'+hex(i)[2:])[-8:]
n = 2 # split every 2 letters
return ''.join([be_hex[i:i+n] for i in range(0, len(be_hex), n)][::-1])
BMP_HEADER = BMP_HEADER % (i2LeHexStr(len(aligned_pixels)/(BYTE/BPP)+BMP_HEADER_SIZE),i2LeHexStr(width),i2LeHexStr(height))
sys.stdout.write(Hex2Bytes(BMP_HEADER+aligned_pixels))
Edit: everything about this e-paper display and my code to display things on it can be found here: https://github.com/yy502/ePaperDisplay
This works for me in Imagemagick 6.9.10.23 Q16 Mac OSX Sierra
Input:
convert logo.png -colorspace gray -depth 2 -type truecolor logo_depth8_gray_rgb.bmp
Adding -type truecolor converts the image to RGB, but in gray tones as per the -colorspace gray. And the depth 2 creates only 4 colors in the histogram.
identify -verbose logo_depth8_gray_rgb.bmp
Image:
Filename: logo_depth8_gray_rgb.bmp
Format: BMP (Microsoft Windows bitmap image)
Class: DirectClass
Geometry: 640x480+0+0
Units: PixelsPerCentimeter
Colorspace: sRGB
Type: Grayscale
Base type: Undefined
Endianness: Undefined
Depth: 8/2-bit
Channel depth:
red: 2-bit
green: 2-bit
blue: 2-bit
Channel statistics:
Pixels: 307200
Red:
min: 0 (0)
max: 255 (1)
mean: 228.414 (0.895742)
standard deviation: 66.9712 (0.262632)
kurtosis: 4.29925
skewness: -2.38354
entropy: 0.417933
Green:
min: 0 (0)
max: 255 (1)
mean: 228.414 (0.895742)
standard deviation: 66.9712 (0.262632)
kurtosis: 4.29925
skewness: -2.38354
entropy: 0.417933
Blue:
min: 0 (0)
max: 255 (1)
mean: 228.414 (0.895742)
standard deviation: 66.9712 (0.262632)
kurtosis: 4.29925
skewness: -2.38354
entropy: 0.417933
Image statistics:
Overall:
min: 0 (0)
max: 255 (1)
mean: 228.414 (0.895742)
standard deviation: 66.9712 (0.262632)
kurtosis: 4.29928
skewness: -2.38355
entropy: 0.417933
Colors: 4 <--------
Histogram: <--------
12730: (0,0,0) #000000 black
24146: (85,85,85) #555555 srgb(85,85,85)
9602: (170,170,170) #AAAAAA srgb(170,170,170)
260722: (255,255,255) #FFFFFF white
Look at https://en.wikipedia.org/wiki/BMP_file_format#File_structure .
The problem is that you do not specify a color table. According to the wikipedia-article, those are mandatory if the bit depth is less than 8 bit.
Well done on solving the problem. You could consider also making a personal delegate, or custom delegate, for ImageMagick to help automate the process. ImageMagick is able to delegate formats it cannot process itself to delegates, or helpers, such as your 2-bit helper ;-)
Rather than interfere with the system-wide delegates, which probably live in /etc/ImageMagick/delegates.xml, you can make your own in $HOME/.magick/delegates.xml. Yours would look something like this:
<?xml version="1.0" encoding="UTF-8"?>
<delegatemap>
<delegate encode="epaper" command="convert "%f" +matte -colors 4 -depth 8 -colorspace gray pgm:- | /usr/local/bin/4_level_gray_4bpp_BMP_converter.py > out.bmp"/>
</delegatemap>
Then if you run:
identify -list delegate
you will see yours listed as a "known" helper.
This all means that you will be able to run commands like:
convert a.png epaper:
and it will do the 2-bit BMP thing automagically.
You can just use this:
convert in.jpg -colorspace gray +matte -colors 2 -depth 1 -resize '640x384>' pgm:- > out.bmp**
I too have this epaper display. After alot of trial and error, I was able to correctly convert the images using ImageMagick using the following command:
convert -verbose INPUT.BMP -resize 300x300 -monochrome -colorspace sRGB -colors 2 -depth 1 BMP3:OUTPUT.BMP
I have some tiff images that have an embedded color profile (the example one is Canon EOS-1Ds flash).
When I convert them to jpeg (gm convert source.tif target.jpeg) the resulting jpeg colours are distorted in browsers (Chrome,Firefox, but not IE) but not in system image viewers.
If I open the source in GIMP Image Editor, and change the colour profile to sRGB, then the image from the above convert command displays correctly in browsers.
So I have this question:
How can I identify the fact that an image has an embedded color
profile using graphicsmagick?
It looks like with this command the profile is removed:
mogrify +profile '*' -define jpeg:preserve-settings
Here is the output from gm identify -verbose source.tif
Image: Di 2007-1337.tif
Format: TIFF (Tagged Image File Format)
Geometry: 4053x2257
Class: DirectClass
Type: true color
Depth: 8 bits-per-pixel component
Channel Depths:
Red: 8 bits
Green: 8 bits
Blue: 8 bits
Channel Statistics:
Red:
Minimum: 0.00 (0.0000)
Maximum: 255.00 (1.0000)
Mean: 135.75 (0.5324)
Standard Deviation: 72.49 (0.2843)
Green:
Minimum: 0.00 (0.0000)
Maximum: 255.00 (1.0000)
Mean: 129.79 (0.5090)
Standard Deviation: 72.87 (0.2858)
Blue:
Minimum: 0.00 (0.0000)
Maximum: 255.00 (1.0000)
Mean: 125.43 (0.4919)
Standard Deviation: 73.42 (0.2879)
Resolution: 400x400 pixels/inch
Filesize: 26.4M
Interlace: No
Orientation: Unknown
Background Color: white
Border Color: #DFDFDF
Matte Color: #BDBDBD
Compose: Over
Dispose: Undefined
Iterations: 0
Compression: No
Signature: bec78e9402b39f6f5539a640715270a651a39c2238846fa42f37c1c7d99af747
Profile-color: 219244 bytes
Profile-iptc: 4358 bytes
unknown:
Byline:
Rod
Source:
Rod
Created Date:
20061006
Created Time:
110014+0000
Originating Program:
Capture One PRO
Program Version:
3.7.1
unknown:
0x00000000: ffffff5c 2c3bff65 2dffff39 ffffff55 ff5bff27 ----\,;-e---9---U-[-
0x00000014: ffffffff ff7affff ffffff48 103a29ff ff17ff58 '-----z-----H-:)----
0x00000028: ff616363 3dffffff ffffffff ff777a63 6537ff19 X-acc=--------wzce7-
0x0000003c: ffffffff 3c57ffff ff3c6674 ffff5f67 ff71ffff -----<W---<
0x00000050: 74ffff5f 67ff71ff ff00 ft--_
Tainted: False
User Time: 0.030u
Elapsed Time: 0:01
Pixels Per Second: 290.8M
The converted image from the following, is broken within OS image viewers as well:
Format: TIFF (Tagged Image File Format)
Geometry: 4992x3328
Class: DirectClass
Type: true color
Depth: 8 bits-per-pixel component
Channel Depths:
Red: 8 bits
Green: 8 bits
Blue: 8 bits
Channel Statistics:
Red:
Minimum: 0.00 (0.0000)
Maximum: 255.00 (1.0000)
Mean: 213.55 (0.8374)
Standard Deviation: 57.03 (0.2237)
Green:
Minimum: 0.00 (0.0000)
Maximum: 255.00 (1.0000)
Mean: 40.76 (0.1598)
Standard Deviation: 92.06 (0.3610)
Blue:
Minimum: 0.00 (0.0000)
Maximum: 255.00 (1.0000)
Mean: 161.49 (0.6333)
Standard Deviation: 121.23 (0.4754)
Resolution: 300x300 pixels/inch
Filesize: 47.6M
Interlace: No
Orientation: TopLeft
Background Color: white
Border Color: #DFDFDF
Matte Color: #BDBDBD
Compose: Over
Dispose: Undefined
Iterations: 0
Compression: No
Artist: Gary
Timestamp: 2006:11:12 12:35:12
Make: Canon
Model: Canon EOS-1Ds Mark II
Software: Adobe Photoshop CS2 Macintosh
Signature: 1b26f2c2122ea6aa391a40c7eb885d0da120x8e9x5bd7ce0dc7cc9038ba99737
Profile-iptc: 12926 bytes
unknown:
Byline:
Gary Ombler
Originating Program:
Capture One PRO
Program Version:
3.7.3
unknown:
#Q�$�'���߸︥�[��Rҝ5��n�b����h�
Profile-XMP: 17293 bytes
Tainted: False
User Time: 0.140u
Elapsed Time: 0:01
Pixels Per Second: 45.3M
gm identify -verbose *.tif | egrep -a '^Image|Profile-'
This will print all matching filenames, followed by the profiles for those files that have them. This would at least let you scan a directory at a time and try to figure out why some files are problematic.
Perhaps you may force the colorspace to RGB:
gm convert +profile "*" -colorspace RGB source destination
Using the Images package, I can open up a color image, convert it to Gray scale and then :
using Images
img_gld = imread("...path to some color jpg...")
img_gld_gs = convert(Image{Gray},img_gld)
#change from floats to Array of values between 0 and 255:
img_gld_gs = reinterpret(Uint8,data(img_gld_gs))
Now I've got a 1920X1080 array of Uint8's:
julia> img_gld_gs
1920x1080 Array{Uint8,2}
Now I want to get a histogram of the 2D array of Uint8 values:
julia> hist(img_gld_gs)
(0.0:50.0:300.0,
6x1080 Array{Int64,2}:
1302 1288 1293 1302 1297 1300 1257 1234 … 12 13 13 12 13 15 14
618 632 627 618 623 620 663 686 189 187 187 188 185 183 183
0 0 0 0 0 0 0 0 9 9 8 7 8 7 7
0 0 0 0 0 0 0 0 10 12 9 7 13 7 9
0 0 0 0 0 0 0 0 1238 1230 1236 1235 1230 1240 1234
0 0 0 0 0 0 0 0 … 462 469 467 471 471 468 473)
But, instead of 6x1080, I'd like 256 slots in the histogram to show total number of times each value has appeared. I tried:
julia> hist(img_gld_gs,256)
But that gives:
(2.0:1.0:252.0,
250x1080 Array{Int64,2}:
So instead of a 256x1080 Array, it's 250x1080. Is there any way to force it to have 256 bins (without resorting to writing my own hist function)? I want to be able to compare different images and I want the histogram for each image to have the same number of bins.
Assuming you want a histogram for the entire image (rather than one per row), you might want
hist(vec(img_gld_gs), -1:255)
which first converts the image to a 1-dimensional vector. (You can also use img_gld_gs[:], but that copies the data.)
Also note the range here: the hist function uses a left-open interval, so it will omit counting zeros unless you use something smaller than 0.
hist also accepts a vector (or range) as an optional argument that specifies the edge boundaries, so
hist(img_gld_gs, 0:256)
should work.
I have JPG images and with inputsvgdraw, a flash tool for image annotation (http://www.mainada.net/inputdraw).
As Gimp or Picasa, when i crop an image and i save as new image only the cropped part. I need a function that take svg path (representing new image bourders) as parameter and then basing on that, create the new image.
svg data:
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 488 325"><g fill="none" stroke-miterlimit="6" stroke-linecap="round" stroke-linejoin="round"><path d="M 307 97 l 0 -1 l -2 -1 l -10 -2 l -20 -1 l -25 5 l -22 9 l -10 9 l 0 9 l 2 12 l 16 18 l 25 11 l 25 5 l 17 -1 l 6 -4 l 3 -7 l -1 -12 l -6 -16 l -7 -13 l -11 -12 l -11 -14 l -9 -5" opacity="1" stroke="rgb(170,37,34)" stroke-width="5"/></g></svg>.
How can i extract and save in a new image inside circle part image? There is any library that handle this?
There is a way convert these datas for a function in a image library in python and cropping image?