Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
E
elphel-web-393
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Elphel
elphel-web-393
Commits
4ff8c216
Commit
4ff8c216
authored
Nov 16, 2016
by
Oleg Dzhimiev
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
moved to lib
parent
129d9d9a
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
871 additions
and
344 deletions
+871
-344
Makefile
src/jp4-canvas/Makefile
+2
-1
elphel.js
src/jp4-canvas/elphel.js
+602
-0
get-image.php
src/jp4-canvas/get-image.php
+8
-1
jp4-canvas.html
src/jp4-canvas/jp4-canvas.html
+4
-3
jp4-canvas.js
src/jp4-canvas/jp4-canvas.js
+13
-4
jquery-jp4.js
src/jp4-canvas/jquery-jp4.js
+242
-335
No files found.
src/jp4-canvas/Makefile
View file @
4ff8c216
...
...
@@ -5,7 +5,8 @@ INSTALL = install
DOCS
=
jp4-canvas.html
\
jp4-canvas.js
LIBS
=
exif.js
\
LIBS
=
elphel.js
\
exif.js
\
jquery-jp4.js
\
jcanvas.min.js
...
...
src/jp4-canvas/elphel.js
0 → 100644
View file @
4ff8c216
/*
* FILE NAME : jp4-canvas.js
* DESCRIPTION: Converts jp4/jp46 files into human perceivable format in html5 canvas.
* VERSION: 1.0
* AUTHOR: Oleg K Dzhimiev <oleg@elphel.com>
* LICENSE: AGPL, see http://www.gnu.org/licenses/agpl.txt
* Copyright (C) 2016 Elphel, Inc.
*/
var
Elphel
=
{
/* Name: reorderJP4Blocks
* Description: clear from the function's name
*
* ctx - canvas 2D context
* format:
* 'jpeg' - skip reordering
* 'jp4' - jp4 reordeing
* 'jp46' - jp46 reordering
* mosaic - [["Gr","R"],["B","Gb"]] =
* odd lines: Gr,R,Gr,R
* even lines: B,Gb,B,Gb
* nwd - true/false - demosaicing: 'Nearest Neighbor' with 1/2 scale -
* put mosaic (different channels) for GrRBGb into one pixel for
* faster performance
*/
reorderJP4Blocks
:
function
(
ctx
,
format
=
"JP4"
,
mosaic
=
[[
"Gr"
,
"R"
],[
"B"
,
"Gb"
]],
nwd
=
false
)
{
var
t0
=
Date
.
now
();
var
width
=
ctx
.
canvas
.
width
;
var
height
=
ctx
.
canvas
.
height
;
var
inputImage
=
ctx
.
getImageData
(
0
,
0
,
width
,
height
);
var
iPixels
=
inputImage
.
data
;
var
outputImage
=
ctx
.
createImageData
((
nwd
)?
width
/
2
:
width
,(
nwd
)?
height
/
2
:
height
);
var
oPixels
=
outputImage
.
data
;
// img.data is a long 1-D array with the following structure:
// pix[i+0] - red
// pix[i+1] - green
// pix[i+2] - blue
// pix[i+3] - alpha
// buffer for reordering pixels
var
macroblock
=
new
Array
();
//16x16
for
(
var
y
=
0
;
y
<
16
;
y
++
)
macroblock
[
y
]
=
new
Array
();
// in JP4 format the 16x16 block is 32x8 (GRBG)
// the 1st line of 32x8 blocks is the left half of the image
// the 2nd line of 32x8 blocks is the right half
// vertical step = 16 pixels
for
(
yb
=
0
;
yb
<
(
height
>>
4
);
yb
++
){
// horizontal step = 16 pixels
for
(
xb
=
0
;
xb
<
(
width
>>
4
);
xb
++
)
{
if
(
format
==
"JP4"
)
{
// 32x8 block reorder into 16x16
for
(
nb
=
0
;
nb
<
4
;
nb
++
)
{
xbyr
=
nb
&
1
;
// horizontal odd-even
ybyr
=
(
nb
>>
1
)
&
1
;
// vertical odd-even
for
(
y
=
0
;
y
<
8
;
y
++
)
{
// xb < half image -> 1st line of 32x8
// xb >= half image -> 2nd line of 32x8
//offset=(((yb<<4)+y)*width)+(nb<<3)+((xb>=(width>>5))?(((xb<<5)-width)+(width<<3)):(xb<<5));
offset
=
(((
yb
<<
4
)
+
y
)
*
width
)
+
(
nb
<<
3
)
+
(
xb
<<
5
)
+
((
xb
>=
(
width
>>
5
))?((
width
<<
3
)
-
width
):
0
);
for
(
x
=
0
;
x
<
8
;
x
++
)
{
macroblock
[(
y
<<
1
)
|
ybyr
][(
x
<<
1
)
|
xbyr
]
=
iPixels
[
4
*
(
offset
+
x
)];
}
}
}
}
if
(
format
==
"JP46"
)
{
for
(
y
=
0
;
y
<
16
;
y
++
)
{
offset
=
((
yb
<<
4
)
+
y
)
*
width
+
(
xb
<<
4
);
for
(
x
=
0
;
x
<
16
;
x
++
)
{
//Red value only
macroblock
[((
y
<<
1
)
&
0xe
)
|
((
y
>>
3
)
&
0x1
)][((
x
<<
1
)
&
0xe
)
|
((
x
>>
3
)
&
0x1
)]
=
iPixels
[
4
*
(
offset
+
x
)];
}
}
}
if
(
nwd
){
for
(
y
=
0
;
y
<
8
;
y
++
){
offset
=
width
/
2
*
((
yb
<<
4
)
/
2
+
y
)
+
(
xb
<<
4
)
/
2
;
for
(
x
=
0
;
x
<
8
;
x
++
)
{
for
(
k
=
0
;
k
<
4
;
k
++
){
y0
=
2
*
y
+
((
k
>>
1
)
&
1
);
x0
=
2
*
x
+
(
k
&
1
);
if
(
mosaic
[
y0
&
1
][
x0
&
1
]
==
"R"
)
r
=
macroblock
[
y0
][
x0
];
else
if
(
mosaic
[
y0
&
1
][
x0
&
1
]
==
"Gr"
)
gr
=
macroblock
[
y0
][
x0
];
else
if
(
mosaic
[
y0
&
1
][
x0
&
1
]
==
"Gb"
)
gb
=
macroblock
[
y0
][
x0
];
else
if
(
mosaic
[
y0
&
1
][
x0
&
1
]
==
"B"
)
b
=
macroblock
[
y0
][
x0
];
}
g
=
(
gr
+
gb
)
>>
1
;
oPixels
[
4
*
(
offset
+
x
)
+
0
]
=
r
;
//4*(8*y+x);
oPixels
[
4
*
(
offset
+
x
)
+
1
]
=
g
;
//4*(8*y+x);
oPixels
[
4
*
(
offset
+
x
)
+
2
]
=
b
;
//4*(8*y+x);
oPixels
[
4
*
(
offset
+
x
)
+
3
]
=
255
;
}
}
}
else
{
for
(
y
=
0
;
y
<
16
;
y
++
){
offset
=
width
*
((
yb
<<
4
)
+
y
)
+
(
xb
<<
4
);
for
(
x
=
0
;
x
<
16
;
x
++
)
{
//red +0, green +1, blue +2, alpha +3
// thinking: GRBG
oPixels
[
4
*
(
offset
+
x
)
+
0
]
=
((
mosaic
[
y
&
1
][
x
&
1
]
==
"R"
)
)?
macroblock
[
y
][
x
]:
0
;
oPixels
[
4
*
(
offset
+
x
)
+
1
]
=
((
mosaic
[
y
&
1
][
x
&
1
]
==
"Gr"
)
||
(
mosaic
[
y
%
2
][
x
%
2
]
==
"Gb"
))?
macroblock
[
y
][
x
]:
0
;
oPixels
[
4
*
(
offset
+
x
)
+
2
]
=
((
mosaic
[
y
&
1
][
x
&
1
]
==
"B"
)
)?
macroblock
[
y
][
x
]:
0
;
oPixels
[
4
*
(
offset
+
x
)
+
3
]
=
255
;
}
}
}
}
}
if
(
nwd
)
{
ctx
.
canvas
.
width
=
width
/
2
;
ctx
.
canvas
.
height
=
height
/
2
;
}
ctx
.
putImageData
(
outputImage
,
0
,
0
);
console
.
log
(
"reorderJP4Blocks(): "
+
(
Date
.
now
()
-
t0
)
/
1000
+
" s"
);
},
/* Name: demosaicNearestNeighbor
* Description: A separate function, just in case, same as in
* reorderJP4Blocks
*
* ctx - canvas 2D context
* mosaic - [["Gr","R"],["B","Gb"]] =
* odd lines: Gr,R,Gr,R
* even lines: B,Gb,B,Gb
*/
demosaicNearestNeighbor
:
function
(
ctx
,
mosaic
=
[[
"Gr"
,
"R"
],[
"B"
,
"Gb"
]]){
var
t0
=
Date
.
now
();
var
width
=
ctx
.
canvas
.
width
;
var
height
=
ctx
.
canvas
.
height
;
var
inputImage
=
ctx
.
getImageData
(
0
,
0
,
width
,
height
);
var
iPixels
=
inputImage
.
data
;
var
outputImage
=
ctx
.
createImageData
(
width
/
2
,
height
/
2
);
var
oPixels
=
outputImage
.
data
;
for
(
var
y
=
0
;
y
<
height
/
2
;
y
++
){
for
(
var
x
=
0
;
x
<
width
/
2
;
x
++
){
for
(
var
k
=
0
;
k
<
4
;
k
++
){
y0
=
(
k
>>
1
)
&
1
;
x0
=
k
&
1
;
if
(
mosaic
[
y0
&
1
][
x0
&
1
]
==
"R"
)
r
=
iPixels
[
4
*
(
width
*
(
2
*
y
+
y0
)
+
2
*
x
+
x0
)
+
0
];
else
if
(
mosaic
[
y0
&
1
][
x0
&
1
]
==
"Gr"
)
gr
=
iPixels
[
4
*
(
width
*
(
2
*
y
+
y0
)
+
2
*
x
+
x0
)
+
1
];
else
if
(
mosaic
[
y0
&
1
][
x0
&
1
]
==
"Gb"
)
gb
=
iPixels
[
4
*
(
width
*
(
2
*
y
+
y0
)
+
2
*
x
+
x0
)
+
1
];
else
if
(
mosaic
[
y0
&
1
][
x0
&
1
]
==
"B"
)
b
=
iPixels
[
4
*
(
width
*
(
2
*
y
+
y0
)
+
2
*
x
+
x0
)
+
2
];
}
g
=
(
gr
+
gb
)
>>
1
;
oPixels
[
4
*
(
width
/
2
*
y
+
x
)
+
0
]
=
r
;
oPixels
[
4
*
(
width
/
2
*
y
+
x
)
+
1
]
=
g
;
oPixels
[
4
*
(
width
/
2
*
y
+
x
)
+
2
]
=
b
;
oPixels
[
4
*
(
width
/
2
*
y
+
x
)
+
3
]
=
255
;
}
}
ctx
.
canvas
.
width
=
width
/
2
;
ctx
.
canvas
.
height
=
height
/
2
;
ctx
.
putImageData
(
outputImage
,
0
,
0
);
console
.
log
(
"demosaicNearestNeighbor(): "
+
(
Date
.
now
()
-
t0
)
/
1000
+
" s"
);
},
/* Name: demosaicBilinear
* Description: a simple bilinear demosaicing
*
* ctx - canvas 2D context
* px - pixel array as in "ctx.getImageData(0,0,width,height)"
* or use pixelsToArray(ctx,true) to get linear array from canvas
* mosaic - [["Gr","R"],["B","Gb"]] =
* odd lines: Gr,R,Gr,R
* even lines: B,Gb,B,Gb
* precise - true/false:
* true - calculate values then apply gamma - provide linear pixel array
* px_linear (8bit) = px^gamma (32bit), gamma=2
* false - calculate values from gamma encoded pixels - 2-4x times faster
*/
demosaicBilinear
:
function
(
ctx
,
px
,
mosaic
=
[[
"Gr"
,
"R"
],[
"B"
,
"Gb"
]],
precise
=
false
){
var
t0
=
Date
.
now
();
var
width
=
ctx
.
canvas
.
width
;
var
height
=
ctx
.
canvas
.
height
;
var
outputImage
=
ctx
.
getImageData
(
0
,
0
,
width
,
height
);
var
oPixels
=
outputImage
.
data
;
var
x_l
=
0
,
x_r
=
0
;
var
y_t
=
0
,
y_b
=
0
;
var
x
=
width
;
var
y
=
height
;
for
(
var
y
=
0
;
y
<
height
;
y
++
){
for
(
var
x
=
0
;
x
<
width
;
x
++
){
x_l
=
(
x
==
0
)?
1
:(
x
-
1
);
x_r
=
(
x
==
(
width
-
1
))?(
width
-
2
):(
x
+
1
);
y_t
=
(
y
==
0
)?
1
:(
y
-
1
);
y_b
=
(
y
==
(
height
-
1
))?(
height
-
2
):(
y
+
1
);
//Gr
if
(
mosaic
[
y
%
2
][
x
%
2
]
==
"Gr"
){
Pr_y0xl
=
px
[
4
*
(
width
*
(
y
+
0
)
+
(
x_l
))
+
0
];
Pr_y0xr
=
px
[
4
*
(
width
*
(
y
+
0
)
+
(
x_r
))
+
0
];
Pb_ybx0
=
px
[
4
*
(
width
*
(
y_b
)
+
(
x
+
0
))
+
2
];
Pb_ytx0
=
px
[
4
*
(
width
*
(
y_t
)
+
(
x
+
0
))
+
2
];
if
(
precise
){
//Pr = pixel_l2g(1/2*(pixel_g2l(Pr_y0xl)+pixel_g2l(Pr_y0xr)));
//Pb = pixel_l2g(1/2*(pixel_g2l(Pb_ybx0)+pixel_g2l(Pb_ytx0)));
Pr
=
this
.
l2g
(
1
/
2
*
(
Pr_y0xl
+
Pr_y0xr
));
Pb
=
this
.
l2g
(
1
/
2
*
(
Pb_ybx0
+
Pb_ytx0
));
}
else
{
Pr
=
1
/
2
*
(
Pr_y0xl
+
Pr_y0xr
);
Pb
=
1
/
2
*
(
Pb_ybx0
+
Pb_ytx0
);
}
oPixels
[
4
*
(
width
*
y
+
x
)
+
0
]
=
Math
.
round
(
Pr
);
oPixels
[
4
*
(
width
*
y
+
x
)
+
2
]
=
Math
.
round
(
Pb
);
}
//R
if
(
mosaic
[
y
%
2
][
x
%
2
]
==
"R"
){
Pg_ytx0
=
px
[
4
*
(
width
*
(
y_t
)
+
(
x
+
0
))
+
1
];
Pg_y0xl
=
px
[
4
*
(
width
*
(
y
+
0
)
+
(
x_l
))
+
1
];
Pg_y0xr
=
px
[
4
*
(
width
*
(
y
+
0
)
+
(
x_r
))
+
1
];
Pg_ybx0
=
px
[
4
*
(
width
*
(
y_b
)
+
(
x
+
0
))
+
1
];
Pb_ytxl
=
px
[
4
*
(
width
*
(
y_t
)
+
(
x_l
))
+
2
];
Pb_ytxr
=
px
[
4
*
(
width
*
(
y_t
)
+
(
x_r
))
+
2
];
Pb_ybxl
=
px
[
4
*
(
width
*
(
y_b
)
+
(
x_l
))
+
2
];
Pb_ybxr
=
px
[
4
*
(
width
*
(
y_b
)
+
(
x_r
))
+
2
];
if
(
precise
){
Pg
=
this
.
l2g
(
1
/
4
*
(
Pg_ytx0
+
Pg_y0xl
+
Pg_y0xr
+
Pg_ybx0
));
Pb
=
this
.
l2g
(
1
/
4
*
(
Pb_ytxl
+
Pb_ytxr
+
Pb_ybxl
+
Pb_ybxr
));
}
else
{
Pg
=
1
/
4
*
(
Pg_ytx0
+
Pg_y0xl
+
Pg_y0xr
+
Pg_ybx0
);
Pb
=
1
/
4
*
(
Pb_ytxl
+
Pb_ytxr
+
Pb_ybxl
+
Pb_ybxr
);
}
oPixels
[
4
*
(
width
*
y
+
x
)
+
1
]
=
Math
.
round
(
Pg
);
oPixels
[
4
*
(
width
*
y
+
x
)
+
2
]
=
Math
.
round
(
Pb
);
}
//B
if
(
mosaic
[
y
%
2
][
x
%
2
]
==
"B"
){
Pr_ytxl
=
px
[
4
*
(
width
*
(
y_t
)
+
(
x_l
))
+
0
];
Pr_ytxr
=
px
[
4
*
(
width
*
(
y_t
)
+
(
x_r
))
+
0
];
Pr_ybxl
=
px
[
4
*
(
width
*
(
y_b
)
+
(
x_l
))
+
0
];
Pr_ybxr
=
px
[
4
*
(
width
*
(
y_b
)
+
(
x_r
))
+
0
];
Pg_ytx0
=
px
[
4
*
(
width
*
(
y_t
)
+
(
x
+
0
))
+
1
];
Pg_y0xl
=
px
[
4
*
(
width
*
(
y
+
0
)
+
(
x_l
))
+
1
];
Pg_y0xr
=
px
[
4
*
(
width
*
(
y
+
0
)
+
(
x_r
))
+
1
];
Pg_ybx0
=
px
[
4
*
(
width
*
(
y_b
)
+
(
x
+
0
))
+
1
];
if
(
precise
){
Pr
=
this
.
l2g
(
1
/
4
*
(
Pr_ytxl
+
Pr_ytxr
+
Pr_ybxl
+
Pr_ybxr
));
Pg
=
this
.
l2g
(
1
/
4
*
(
Pg_ytx0
+
Pg_y0xl
+
Pg_y0xr
+
Pg_ybx0
));
}
else
{
Pr
=
1
/
4
*
(
Pr_ytxl
+
Pr_ytxr
+
Pr_ybxl
+
Pr_ybxr
);
Pg
=
1
/
4
*
(
Pg_ytx0
+
Pg_y0xl
+
Pg_y0xr
+
Pg_ybx0
);
}
oPixels
[
4
*
(
width
*
y
+
x
)
+
0
]
=
Math
.
round
(
Pr
);
oPixels
[
4
*
(
width
*
y
+
x
)
+
1
]
=
Math
.
round
(
Pg
);
}
//Gb
if
(
mosaic
[
y
%
2
][
x
%
2
]
==
"Gb"
){
Pr_ytx0
=
px
[
4
*
(
width
*
(
y_t
)
+
(
x
+
0
))
+
0
];
Pr_ybx0
=
px
[
4
*
(
width
*
(
y_b
)
+
(
x
+
0
))
+
0
];
Pb_y0xl
=
px
[
4
*
(
width
*
(
y
+
0
)
+
(
x_l
))
+
2
];
Pb_y0xr
=
px
[
4
*
(
width
*
(
y
+
0
)
+
(
x_r
))
+
2
];
if
(
precise
){
Pr
=
this
.
l2g
(
1
/
2
*
(
Pr_ytx0
+
Pr_ybx0
));
Pb
=
this
.
l2g
(
1
/
2
*
(
Pb_y0xl
+
Pb_y0xr
));
}
else
{
Pr
=
1
/
2
*
(
Pr_ytx0
+
Pr_ybx0
);
Pb
=
1
/
2
*
(
Pb_y0xl
+
Pb_y0xr
);
}
oPixels
[
4
*
(
width
*
y
+
x
)
+
0
]
=
Math
.
round
(
Pr
);
oPixels
[
4
*
(
width
*
y
+
x
)
+
2
]
=
Math
.
round
(
Pb
);
}
}
}
ctx
.
putImageData
(
outputImage
,
0
,
0
);
console
.
log
(
"demosaicBilinear(): "
+
(
Date
.
now
()
-
t0
)
/
1000
+
" s"
);
},
/* Name: showSingleColorChannel
* Description: make a BW from selected channel
*
* ctx - canvas 2D context
* channel - "red","green","blue"
*/
showSingleColorChannel
:
function
(
ctx
,
channel
){
var
width
=
ctx
.
canvas
.
width
;
var
height
=
ctx
.
canvas
.
height
;
var
inputImage
=
ctx
.
getImageData
(
0
,
0
,
width
,
height
);
var
iPixels
=
inputImage
.
data
;
for
(
var
y
=
0
;
y
<
height
;
y
++
){
for
(
var
x
=
0
;
x
<
width
;
x
++
){
r
=
iPixels
[
4
*
(
width
*
y
+
x
)
+
0
];
g
=
iPixels
[
4
*
(
width
*
y
+
x
)
+
1
];
b
=
iPixels
[
4
*
(
width
*
y
+
x
)
+
2
];
if
(
channel
==
"red"
){
iPixels
[
4
*
(
width
*
y
+
x
)
+
0
]
=
r
;
iPixels
[
4
*
(
width
*
y
+
x
)
+
1
]
=
r
;
iPixels
[
4
*
(
width
*
y
+
x
)
+
2
]
=
r
;
}
else
if
(
channel
==
"green"
){
iPixels
[
4
*
(
width
*
y
+
x
)
+
0
]
=
g
;
iPixels
[
4
*
(
width
*
y
+
x
)
+
1
]
=
g
;
iPixels
[
4
*
(
width
*
y
+
x
)
+
2
]
=
g
;
}
else
if
(
channel
==
"blue"
){
iPixels
[
4
*
(
width
*
y
+
x
)
+
0
]
=
b
;
iPixels
[
4
*
(
width
*
y
+
x
)
+
1
]
=
b
;
iPixels
[
4
*
(
width
*
y
+
x
)
+
2
]
=
b
;
}
}
}
ctx
.
putImageData
(
inputImage
,
0
,
0
);
},
/* Name: applySaturation
* Description: get data from ctx saturate and redraw
*
* ctx - canvas 2D context
* s - value
*/
applySaturation
:
function
(
ctx
,
s
){
var
width
=
ctx
.
canvas
.
width
;
var
height
=
ctx
.
canvas
.
height
;
var
inputImage
=
ctx
.
getImageData
(
0
,
0
,
width
,
height
);
var
iPixels
=
inputImage
.
data
;
var
r
,
g
,
b
;
var
Y
,
Cb
,
Cr
;
for
(
var
y
=
0
;
y
<
height
;
y
++
){
for
(
var
x
=
0
;
x
<
width
;
x
++
){
r
=
iPixels
[
4
*
(
width
*
y
+
x
)
+
0
];
g
=
iPixels
[
4
*
(
width
*
y
+
x
)
+
1
];
b
=
iPixels
[
4
*
(
width
*
y
+
x
)
+
2
];
Y
=
0.299
*
r
+
0.5870
*
g
+
0.144
*
b
;
Cb
=
128
+
s
*
(
-
0.1687
*
r
-
0.3313
*
g
+
0.500
*
b
);
Cr
=
128
+
s
*
(
0.5
*
r
-
0.4187
*
g
-
0.0813
*
b
);
if
(
Cb
<
0
)
Cb
=
0
;
if
(
Cb
>
255
)
Cb
=
255
;
if
(
Cr
<
0
)
Cr
=
0
;
if
(
Cr
>
255
)
Cr
=
255
;
r
=
Y
+
1.402
*
(
Cr
-
128
);
g
=
Y
-
0.34414
*
(
Cb
-
128
)
-
0.71414
*
(
Cr
-
128
);
b
=
Y
+
1.772
*
(
Cb
-
128
);
if
(
r
<
0
)
r
=
0
;
if
(
r
>
255
)
r
=
255
;
if
(
g
<
0
)
g
=
0
;
if
(
g
>
255
)
g
=
255
;
if
(
b
<
0
)
b
=
0
;
if
(
b
>
255
)
b
=
255
;
iPixels
[
4
*
(
width
*
y
+
x
)
+
0
]
=
r
;
iPixels
[
4
*
(
width
*
y
+
x
)
+
1
]
=
g
;
iPixels
[
4
*
(
width
*
y
+
x
)
+
2
]
=
b
;
iPixels
[
4
*
(
width
*
y
+
x
)
+
3
]
=
255
;
}
}
ctx
.
putImageData
(
inputImage
,
0
,
0
);
},
/* Name: l2g
* Desctiptin: convert a value from linear to gamma encoded,
* close to square root
*/
l2g
:
function
(
pv
){
// assuming gamma=2
var
tmp
=
Math
.
sqrt
(
pv
);
if
(
tmp
>
255
)
tmp
=
255
;
return
tmp
;
},
/* Name: g2l
* Desctiptin: convert a value gamma encoded to linear,
* close to square
*/
g2l
:
function
(
pv
){
// assuming gamma = 2
var
tmp
=
pv
*
pv
;
return
tmp
;
},
/* Name: pixelsToArray
* Desctiptin: get pixel data from canvas context and copy to
* an array. The array will be Uint8ClampedArray
*/
pixelsToArray
:
function
(
ctx
){
t0
=
Date
.
now
();
var
px
=
[];
var
width
=
ctx
.
canvas
.
width
;
var
height
=
ctx
.
canvas
.
height
;
var
image
=
ctx
.
getImageData
(
0
,
0
,
width
,
height
);
var
pixels
=
image
.
data
;
px
=
pixels
.
slice
(
0
);
console
.
log
(
"pixelsToArray(): "
+
(
Date
.
now
()
-
t0
)
/
1000
+
" s"
);
return
px
;
},
/* Name: pixelsToArrayLinear
* Desctiptin: get pixel data from canvas context, convert to linear,
* then copy to an array.
*/
pixelsToArrayLinear
:
function
(
ctx
){
t0
=
Date
.
now
();
var
px
=
[];
var
width
=
ctx
.
canvas
.
width
;
var
height
=
ctx
.
canvas
.
height
;
var
image
=
ctx
.
getImageData
(
0
,
0
,
width
,
height
);
var
pixels
=
image
.
data
;
for
(
var
i
=
0
;
i
<
pixels
.
length
;
i
+=
4
){
px
[
i
+
0
]
=
this
.
g2l
(
pixels
[
i
+
0
]);
px
[
i
+
1
]
=
this
.
g2l
(
pixels
[
i
+
1
]);
px
[
i
+
2
]
=
this
.
g2l
(
pixels
[
i
+
2
]);
px
[
i
+
3
]
=
pixels
[
i
+
3
];
//alpha
}
console
.
log
(
"pixelsToArrayLinear(): "
+
(
Date
.
now
()
-
t0
)
/
1000
+
" s"
);
return
px
;
},
/* Name: drawScaled
* Description: Plugin specific. Takes source canvas - draws a scaled
* version on destination canvas
*/
drawScaled
:
function
(
cnv_src
,
cnv_dst
,
width
){
var
t0
=
Date
.
now
();
var
ctx
=
cnv_src
[
0
].
getContext
(
'2d'
);
var
tw
=
ctx
.
canvas
.
width
;
var
th
=
ctx
.
canvas
.
height
;
var
tr
=
tw
/
th
;
var
sctx
=
cnv_dst
[
0
].
getContext
(
'2d'
);
var
w
=
Math
.
round
(
width
);
var
h
=
Math
.
round
(
w
/
tr
);
sctx
.
canvas
.
width
=
w
;
sctx
.
canvas
.
height
=
h
;
cscale
=
Math
.
round
(
w
/
tw
*
100
)
/
100
;
sctx
.
scale
(
cscale
,
cscale
);
sctx
.
drawImage
(
cnv_src
[
0
],
0
,
0
);
console
.
log
(
"drawScaled(): "
+
(
Date
.
now
()
-
t0
)
/
1000
+
" s"
);
},
/* Name: diffColorChannels
* Description: color channel difference
*
*/
diffColorChannels
:
function
(
px
,
chn1
,
chn2
,
k
=
1
){
var
t0
=
Date
.
now
();
var
i1
=
0
;
if
(
chn1
==
"green"
)
i1
=
1
;
if
(
chn1
==
"blue"
)
i1
=
2
;
var
i2
=
0
;
if
(
chn2
==
"green"
)
i2
=
1
;
if
(
chn2
==
"blue"
)
i2
=
2
;
for
(
var
i
=
0
;
i
<
(
px
.
length
>>
2
);
i
++
){
diff
=
k
*
px
[
4
*
i
+
i1
]
-
px
[
4
*
i
+
i2
];
px
[
4
*
i
+
0
]
=
diff
;
px
[
4
*
i
+
1
]
=
diff
;
px
[
4
*
i
+
2
]
=
diff
;
px
[
4
*
i
+
3
]
=
255
;
}
console
.
log
(
"diffColorChannels(): "
+
(
Date
.
now
()
-
t0
)
/
1000
+
" s"
);
return
px
;
},
/* Name: ndvi
* Description: get ndvi
*/
ndvi
:
function
(
px
){
var
r
,
g
,
b
;
var
NIR
,
RED
,
NDVI
;
for
(
var
i
=
0
;
i
<
(
px
.
length
>>
2
);
i
++
){
r
=
px
[
4
*
i
+
0
];
g
=
px
[
4
*
i
+
1
];
b
=
px
[
4
*
i
+
2
];
if
((
b
>
r
)
||
(
g
>
r
))
{
console
.
log
(
"Color error!"
);
NIR
=
0
;
RED
=
1
;
//if (b>r) px[4*(width*y+x)+2]=255;
if
(
b
>
r
)
px
[
4
*
i
+
2
]
=
0
;
if
(
g
>
r
)
px
[
4
*
i
+
1
]
=
0
;
px
[
4
*
i
+
0
]
=
200
;
}
else
{
if
((
r
-
b
)
<
20
)
b
=
0
;
k
=
0.6
;
RED
=
(
r
-
g
)
/
(
1
-
k
);
NIR
=
(
r
-
RED
)
*
2.5
;
NDVI
=
(
NIR
-
RED
)
/
(
NIR
+
RED
);
//color
if
(
NDVI
<
0.00
){
r
=
0
;
g
=
0
;
b
=
0
;}
else
if
(
NDVI
<
0.50
){
r
=
200
;
g
=
400
*
(
0.5
-
NDVI
);
b
=
0
;}
else
if
(
NDVI
<
1.00
){
r
=
400
*
(
1
-
NDVI
);
g
=
200
;
b
=
0
;}
else
{
r
=
150
;
g
=
150
;
b
=
150
;
}
//console.log("RED="+RED+" NIR="+NIR);
px
[
4
*
i
+
0
]
=
r
;
px
[
4
*
i
+
1
]
=
g
;
px
[
4
*
i
+
2
]
=
b
;
}
}
return
px
;
},
/* Name: gammaEncode
* Description: -
*/
gammaEncode
:
function
(
px
){
for
(
var
i
=
0
;
i
<
(
px
.
length
>>
2
);
i
++
){
px
[
4
*
i
+
0
]
=
this
.
l2g
(
px
[
4
*
i
+
0
]);
px
[
4
*
i
+
1
]
=
this
.
l2g
(
px
[
4
*
i
+
1
]);
px
[
4
*
i
+
2
]
=
this
.
l2g
(
px
[
4
*
i
+
2
]);
}
return
px
;
},
/* Name: gammaDecode
* Description: -
*/
gammaDecode
:
function
(
px
){
for
(
var
i
=
0
;
i
<
(
px
.
length
>>
2
);
i
++
){
px
[
4
*
i
+
0
]
=
this
.
g2l
(
px
[
4
*
i
+
0
]);
px
[
4
*
i
+
1
]
=
this
.
g2l
(
px
[
4
*
i
+
1
]);
px
[
4
*
i
+
2
]
=
this
.
g2l
(
px
[
4
*
i
+
2
]);
}
return
px
;
},
/* Name: drawImageData
* Description: -
*/
drawImageData
:
function
(
ctx
,
px
){
var
t0
=
Date
.
now
();
var
img
=
ctx
.
createImageData
(
ctx
.
canvas
.
width
,
ctx
.
canvas
.
height
);
var
imgdata
=
img
.
data
;
for
(
var
i
=
0
;
i
<
(
imgdata
.
length
>>
2
);
i
++
){
imgdata
[
4
*
i
+
0
]
=
px
[
4
*
i
+
0
];
imgdata
[
4
*
i
+
1
]
=
px
[
4
*
i
+
1
];
imgdata
[
4
*
i
+
2
]
=
px
[
4
*
i
+
2
];
imgdata
[
4
*
i
+
3
]
=
px
[
4
*
i
+
3
];
}
ctx
.
putImageData
(
img
,
0
,
0
);
console
.
log
(
"drawImageData(): "
+
(
Date
.
now
()
-
t0
)
/
1000
+
" s"
);
}
};
\ No newline at end of file
src/jp4-canvas/get-image.php
View file @
4ff8c216
...
...
@@ -10,7 +10,14 @@ if (isset($_GET['rel']))
else
die
();
/*
header("Location: http://{$_SERVER['HTTP_HOST']}:$port/$rel");
die();
*/
header
(
'Content-type:image/jpeg'
);
echo
file_get_contents
(
"http://localhost:
$port
/
$rel
"
);
die
();
?>
src/jp4-canvas/jp4-canvas.html
View file @
4ff8c216
...
...
@@ -7,18 +7,19 @@
<body>
<table>
<tr>
<td><div
id=
'test1'
></div></td>
<td><div
id=
'test2'
></div></td>
<td><div
id=
'test4'
></div></td>
</tr>
<tr>
<td><div
id=
'test1'
></div></td>
<td><div
id=
'test3'
></div></td>
<td><div
id=
'test4'
></div></td>
</tr>
</table>
<script
src=
"js/elphel.js"
></script>
<script
src=
"js/jquery-2.2.3.min.js"
></script>
<script
src=
"js/jcanvas.min.js"
></script>
<script
src=
"js/exif.js"
></script>
<script
src=
"jp4-canvas.js"
></script>
<script
src=
"js/jquery-jp4.js"
></script>
<script
src=
"jp4-canvas.js"
></script>
</body>
</html>
src/jp4-canvas/jp4-canvas.js
View file @
4ff8c216
...
...
@@ -8,8 +8,17 @@ Copyright (C) 2016 Elphel, Inc.
*/
$
(
function
(){
$
(
"#test1"
).
jp4
({
port
:
2323
,
width
:
600
});
$
(
"#test2"
).
jp4
({
port
:
2324
,
width
:
600
});
$
(
"#test3"
).
jp4
({
port
:
2325
,
width
:
600
});
$
(
"#test4"
).
jp4
({
port
:
2326
,
width
:
600
});
var
t1
=
$
(
"#test1"
).
jp4
({
port
:
2323
,
width
:
600
,
image
:
"images/elphelimg_lowpass_1.jp4"
,
fast
:
true
});
var
t2
=
$
(
"#test2"
).
jp4
({
port
:
2323
,
width
:
600
,
image
:
"images/elphelimg_lowpass_1.jp4"
,
precise
:
true
});
t1
.
cnv
.
on
(
"canvas_ready"
,
function
(){
console
.
log
(
"canvas1 ready"
);
});
t2
.
cnv
.
on
(
"canvas_ready"
,
function
(){
console
.
log
(
"canvas2 ready"
);
});
});
src/jp4-canvas/jquery-jp4.js
View file @
4ff8c216
(
function
(
$
)
{
$
.
fn
.
jp4
=
function
(
options
)
{
var
settings
=
$
.
extend
({
port
:
2323
,
rel
:
"bimg"
,
mosaic
:
[[
"Gr"
,
"R"
],[
"B"
,
"Gb"
]],
width
:
600
},
options
);
var
BayerMosaic
=
settings
.
mosaic
;
var
FLIPV
=
0
;
var
FLIPH
=
0
;
//https://gist.github.com/leolux/c794fc63d9c362013448
var
JP4
=
function
(
element
,
options
){
var
elem
=
$
(
element
);
var
obj
=
this
;
var
settings
=
$
.
extend
({
port
:
""
,
image
:
"test.jp4"
,
mosaic
:
[[
"Gr"
,
"R"
],[
"B"
,
"Gb"
]],
fast
:
false
,
precise
:
false
,
width
:
600
,
channel
:
"all"
,
diff
:
false
,
chn1
:
"red"
,
chn2
:
"green"
,
ndvi
:
false
,
callback
:
function
(){
console
.
log
(
"callback"
);
}
},
options
);
var
COLOR_MODE
=
0
;
var
IS_JP4
=
false
;
var
IS_JP46
=
false
;
var
SATURATION
=
[
0
,
0
,
0
,
0
];
var
cnv_working
=
$
(
"<canvas>"
).
css
({
display
:
"none"
});
var
cnv_display
=
$
(
"<canvas>"
);
this
.
append
(
cnv_working
).
append
(
cnv_display
);
get_image
();
//end
var
BAYER
=
settings
.
mosaic
;
var
FLIPV
=
0
;
var
FLIPH
=
0
;
var
IMAGE_FORMAT
=
"JPEG"
;
var
SATURATION
=
[
0
,
0
,
0
,
0
];
var
PIXELS
=
[];
var
cnv_working
=
$
(
"<canvas>"
,{
id
:
"result"
}).
css
({
display
:
"none"
});
var
cnv_display
=
$
(
"<canvas>"
,{
id
:
"scaled"
});
elem
.
append
(
cnv_working
).
append
(
cnv_display
);
function
get_image
(){
get_image
();
//end
function
get_image
(){
var
ts
=
new
Date
().
getTime
();
var
canvas
=
cnv_working
;
var
scaled_canvas
=
cnv_display
;
IS_JP4
=
false
;
I
S_JP46
=
false
;
//reset format
I
MAGE_FORMAT
=
"JPEG"
;
var
http
=
new
XMLHttpRequest
();
http
.
open
(
"GET"
,
"get-image.php?port="
+
settings
.
port
+
"&rel="
+
settings
.
rel
+
"&ts="
+
ts
,
true
);
var
rq
=
""
;
if
(
settings
.
port
!=
""
){
rq
=
"get-image.php?port="
+
settings
.
port
+
"&rel=bimg&ts="
+
Date
.
now
();
}
else
{
rq
=
settings
.
image
;
}
console
.
log
(
"my rq is "
+
rq
);
http
.
open
(
"GET"
,
rq
,
true
);
http
.
responseType
=
"blob"
;
http
.
onload
=
function
(
e
)
{
if
(
this
.
status
===
200
)
{
var
heavyImage
=
new
Image
();
heavyImage
.
onload
=
function
(){
EXIF
.
getData
(
this
,
function
()
{
//update canvas size
canvas
.
attr
(
"width"
,
this
.
width
);
canvas
.
attr
(
"height"
,
this
.
height
);
if
(
this
.
status
===
200
)
{
var
heavyImage
=
new
Image
();
heavyImage
.
onload
=
function
(){
EXIF
.
getData
(
this
,
function
()
{
//update canvas size
canvas
.
attr
(
"width"
,
this
.
width
);
canvas
.
attr
(
"height"
,
this
.
height
);
parseEXIFMakerNote
(
this
);
canvas
.
drawImage
({
x
:
0
,
y
:
0
,
source
:
heavyImage
,
load
:
redraw
,
//scale: 1,
fromCenter
:
false
});
var
tw
=
this
.
width
;
var
th
=
this
.
height
;
var
tr
=
tw
/
th
;
var
sctx
=
scaled_canvas
[
0
].
getContext
(
'2d'
);
var
w
=
Math
.
round
(
settings
.
width
);
var
h
=
Math
.
round
(
w
/
tr
);
sctx
.
canvas
.
width
=
w
;
sctx
.
canvas
.
height
=
h
;
cscale
=
Math
.
round
(
w
/
this
.
width
*
100
)
/
100
;
//console.log("cscale is "+cscale);
sctx
.
scale
(
cscale
,
cscale
);
sctx
.
drawImage
(
canvas
[
0
],
0
,
0
);
});
};
heavyImage
.
src
=
URL
.
createObjectURL
(
http
.
response
);
}
parseEXIFMakerNote
(
this
);
canvas
.
drawImage
({
x
:
0
,
y
:
0
,
source
:
heavyImage
,
load
:
redraw
,
//scale: 1,
fromCenter
:
false
});
});
};
heavyImage
.
src
=
URL
.
createObjectURL
(
http
.
response
);
}
};
http
.
send
();
}
function
redraw
(){
$
(
this
).
draw
({
fn
:
function
(
ctx
){
if
(
IS_JP4
||
IS_JP46
){
pixelBlocksReorder
(
ctx
);
demosaic_bilinear
(
ctx
);
// RGB > YCbCr x SATURATION > RGB
// Taking SATURATION[0] = 1/GAMMA[0] (green pixel of GR-line)
saturation
(
ctx
,
SATURATION
[
0
]);
function
redraw
(){
$
(
this
).
draw
({
fn
:
function
(
ctx
){
var
t0
=
Date
.
now
();
console
.
log
(
"Processing "
+
elem
.
attr
(
"id"
));
if
((
IMAGE_FORMAT
==
"JP4"
)
||
(
IMAGE_FORMAT
==
"JP46"
)){
if
(
settings
.
fast
){
quickestPreview
(
ctx
);
}
else
{
Elphel
.
reorderJP4Blocks
(
ctx
,
"JP4"
);
if
(
settings
.
precise
){
PIXELS
=
Elphel
.
pixelsToArrayLinear
(
ctx
);
Elphel
.
demosaicBilinear
(
ctx
,
PIXELS
,
settings
.
mosaic
,
true
);
PIXELS
=
Elphel
.
pixelsToArray
(
ctx
);
}
else
{
PIXELS
=
Elphel
.
pixelsToArray
(
ctx
);
Elphel
.
demosaicBilinear
(
ctx
,
PIXELS
,
settings
.
mosaic
,
false
);
PIXELS
=
Elphel
.
pixelsToArray
(
ctx
);
}
get_image
();
}
});
}
// reorder blocks if needed
function
pixelBlocksReorder
(
ctx
){
var
width
=
ctx
.
canvas
.
width
;
var
height
=
ctx
.
canvas
.
height
;
var
inputImage
=
ctx
.
getImageData
(
0
,
0
,
width
,
height
);
var
iPixels
=
inputImage
.
data
;
var
outputImage
=
ctx
.
createImageData
(
width
,
height
);
var
oPixels
=
outputImage
.
data
;
// img.data is a long 1-D array with the following structure:
// pix[i+0] - red
// pix[i+1] - green
// pix[i+2] - blue
// pix[i+3] - alpha
// buffer for reordering pixels
var
macroblock
=
new
Array
();
//16x16
for
(
var
y
=
0
;
y
<
16
;
y
++
)
macroblock
[
y
]
=
new
Array
();
// in JP4 format the 16x16 block is 32x8 (GRBG)
// the 1st line of 32x8 blocks is the left half of the image
// the 2nd line of 32x8 blocks is the right half
// vertical step = 16 pixels
for
(
yb
=
0
;
yb
<
(
height
>>
4
);
yb
++
){
// horizontal step = 16 pixels
for
(
xb
=
0
;
xb
<
(
width
>>
4
);
xb
++
)
{
if
(
IS_JP4
)
{
// 32x8 block reorder into 16x16
for
(
nb
=
0
;
nb
<
4
;
nb
++
)
{
xbyr
=
nb
&
1
;
// horizontal odd-even
ybyr
=
(
nb
>>
1
)
&
1
;
// vertical odd-even
for
(
y
=
0
;
y
<
8
;
y
++
)
{
// xb < half image -> 1st line of 32x8
// xb >= half image -> 2nd line of 32x8
//offset=(((yb<<4)+y)*width)+(nb<<3)+((xb>=(width>>5))?(((xb<<5)-width)+(width<<3)):(xb<<5));
offset
=
(((
yb
<<
4
)
+
y
)
*
width
)
+
(
nb
<<
3
)
+
(
xb
<<
5
)
+
((
xb
>=
(
width
>>
5
))?((
width
<<
3
)
-
width
):
0
);
for
(
x
=
0
;
x
<
8
;
x
++
)
{
macroblock
[(
y
<<
1
)
|
ybyr
][(
x
<<
1
)
|
xbyr
]
=
iPixels
[
4
*
(
offset
+
x
)];
}
}
}
}
if
(
IS_JP46
)
{
for
(
y
=
0
;
y
<
16
;
y
++
)
{
offset
=
((
yb
<<
4
)
+
y
)
*
width
+
(
xb
<<
4
);
for
(
x
=
0
;
x
<
16
;
x
++
)
{
macroblock
[((
y
<<
1
)
&
0xe
)
|
((
y
>>
3
)
&
0x1
)][((
x
<<
1
)
&
0xe
)
|
((
x
>>
3
)
&
0x1
)]
=
iPixels
[
4
*
(
offset
+
x
)];
}
}
}
for
(
y
=
0
;
y
<
16
;
y
++
)
{
offset
=
width
*
((
yb
<<
4
)
+
y
)
+
(
xb
<<
4
);
for
(
x
=
0
;
x
<
16
;
x
++
)
{
//red +0, green +1, blue +2, alpha +3
// thinking: GRBG
oPixels
[
4
*
(
offset
+
x
)
+
0
]
=
((
BayerMosaic
[
y
%
2
][
x
%
2
]
==
"R"
)
)?
macroblock
[
y
][
x
]:
0
;
oPixels
[
4
*
(
offset
+
x
)
+
1
]
=
((
BayerMosaic
[
y
%
2
][
x
%
2
]
==
"Gr"
)
||
(
BayerMosaic
[
y
%
2
][
x
%
2
]
==
"Gb"
))?
macroblock
[
y
][
x
]:
0
;
oPixels
[
4
*
(
offset
+
x
)
+
2
]
=
((
BayerMosaic
[
y
%
2
][
x
%
2
]
==
"B"
)
)?
macroblock
[
y
][
x
]:
0
;
oPixels
[
4
*
(
offset
+
x
)
+
3
]
=
255
;
}
if
(
settings
.
channel
!=
"all"
){
Elphel
.
showSingleColorChannel
(
ctx
,
settings
.
channel
);
}
}
}
ctx
.
putImageData
(
outputImage
,
0
,
0
);
}
// demosaic GRBG array - bilinear
function
demosaic_bilinear
(
ctx
){
var
width
=
ctx
.
canvas
.
width
;
var
height
=
ctx
.
canvas
.
height
;
var
outputImage
=
ctx
.
getImageData
(
0
,
0
,
width
,
height
);
var
oPixels
=
outputImage
.
data
;
var
x_l
=
0
,
x_r
=
0
;
var
y_t
=
0
,
y_b
=
0
;
for
(
var
y
=
0
;
y
<
height
;
y
++
){
for
(
var
x
=
0
;
x
<
width
;
x
++
){
x_l
=
(
x
==
0
)?
1
:(
x
-
1
);
x_r
=
(
x
==
(
width
-
1
))?(
width
-
2
):(
x
+
1
);
y_t
=
(
y
==
0
)?
1
:(
y
-
1
);
y_b
=
(
y
==
(
height
-
1
))?(
height
-
2
):(
y
+
1
);
//Gr
if
(
BayerMosaic
[
y
%
2
][
x
%
2
]
==
"Gr"
){
oPixels
[
4
*
(
width
*
y
+
x
)
+
0
]
=
1
/
2
*
(
oPixels
[
4
*
(
width
*
(
y
+
0
)
+
(
x_l
))
+
0
]
+
oPixels
[
4
*
(
width
*
(
y
+
0
)
+
(
x_r
))
+
0
]);
oPixels
[
4
*
(
width
*
y
+
x
)
+
2
]
=
1
/
2
*
(
oPixels
[
4
*
(
width
*
(
y_b
)
+
(
x
+
0
))
+
2
]
+
oPixels
[
4
*
(
width
*
(
y_t
)
+
(
x
+
0
))
+
2
]);
}
//R
if
(
BayerMosaic
[
y
%
2
][
x
%
2
]
==
"R"
){
oPixels
[
4
*
(
width
*
y
+
x
)
+
1
]
=
1
/
4
*
(
oPixels
[
4
*
(
width
*
(
y_t
)
+
(
x
+
0
))
+
1
]
+
oPixels
[
4
*
(
width
*
(
y
+
0
)
+
(
x_l
))
+
1
]
+
oPixels
[
4
*
(
width
*
(
y
+
0
)
+
(
x_r
))
+
1
]
+
oPixels
[
4
*
(
width
*
(
y_b
)
+
(
x
+
0
))
+
1
]);
oPixels
[
4
*
(
width
*
y
+
x
)
+
2
]
=
1
/
4
*
(
oPixels
[
4
*
(
width
*
(
y_t
)
+
(
x_l
))
+
2
]
+
oPixels
[
4
*
(
width
*
(
y_t
)
+
(
x_r
))
+
2
]
+
oPixels
[
4
*
(
width
*
(
y_b
)
+
(
x_l
))
+
2
]
+
oPixels
[
4
*
(
width
*
(
y_b
)
+
(
x_r
))
+
2
]);
if
(
settings
.
diff
){
Elphel
.
diffColorChannels
(
PIXELS
,
settings
.
chn1
,
settings
.
chn2
,
1
);
Elphel
.
drawImageData
(
ctx
,
PIXELS
);
}
//B
if
(
BayerMosaic
[
y
%
2
][
x
%
2
]
==
"B"
){
oPixels
[
4
*
(
width
*
y
+
x
)
+
0
]
=
1
/
4
*
(
oPixels
[
4
*
(
width
*
(
y_t
)
+
(
x_l
))
+
0
]
+
oPixels
[
4
*
(
width
*
(
y_t
)
+
(
x_r
))
+
0
]
+
oPixels
[
4
*
(
width
*
(
y_b
)
+
(
x_l
))
+
0
]
+
oPixels
[
4
*
(
width
*
(
y_b
)
+
(
x_r
))
+
0
]);
oPixels
[
4
*
(
width
*
y
+
x
)
+
1
]
=
1
/
4
*
(
oPixels
[
4
*
(
width
*
(
y_t
)
+
(
x
+
0
))
+
1
]
+
oPixels
[
4
*
(
width
*
(
y
+
0
)
+
(
x_l
))
+
1
]
+
oPixels
[
4
*
(
width
*
(
y
+
0
)
+
(
x_r
))
+
1
]
+
oPixels
[
4
*
(
width
*
(
y_b
)
+
(
x
+
0
))
+
1
]);
}
//Gb
if
(
BayerMosaic
[
y
%
2
][
x
%
2
]
==
"Gb"
){
oPixels
[
4
*
(
width
*
y
+
x
)
+
0
]
=
1
/
2
*
(
oPixels
[
4
*
(
width
*
(
y_t
)
+
(
x
+
0
))
+
0
]
+
oPixels
[
4
*
(
width
*
(
y_b
)
+
(
x
+
0
))
+
0
]);
oPixels
[
4
*
(
width
*
y
+
x
)
+
2
]
=
1
/
2
*
(
oPixels
[
4
*
(
width
*
(
y
+
0
)
+
(
x_l
))
+
2
]
+
oPixels
[
4
*
(
width
*
(
y
+
0
)
+
(
x_r
))
+
2
]);
if
(
settings
.
ndvi
){
console
.
log
(
PIXELS
[
0
]
+
" "
+
PIXELS
[
1
]
+
" "
+
PIXELS
[
2
]
+
" "
+
PIXELS
[
3
]
+
" "
);
PIXELS
=
Elphel
.
someIndex
(
PIXELS
);
console
.
log
(
PIXELS
[
0
]
+
" "
+
PIXELS
[
1
]
+
" "
+
PIXELS
[
2
]
+
" "
+
PIXELS
[
3
]
+
" "
);
Elphel
.
drawImageData
(
ctx
,
PIXELS
);
}
}
}
ctx
.
putImageData
(
outputImage
,
0
,
0
);
}
function
saturation
(
ctx
,
s
){
var
width
=
ctx
.
canvas
.
width
;
var
height
=
ctx
.
canvas
.
height
;
var
inputImage
=
ctx
.
getImageData
(
0
,
0
,
width
,
height
);
var
iPixels
=
inputImage
.
data
;
var
r
,
g
,
b
;
var
Y
,
Cb
,
Cr
;
for
(
var
y
=
0
;
y
<
height
;
y
++
){
for
(
var
x
=
0
;
x
<
width
;
x
++
){
r
=
iPixels
[
4
*
(
width
*
y
+
x
)
+
0
];
g
=
iPixels
[
4
*
(
width
*
y
+
x
)
+
1
];
b
=
iPixels
[
4
*
(
width
*
y
+
x
)
+
2
];
Y
=
0.299
*
r
+
0.5870
*
g
+
0.144
*
b
;
Cb
=
128
+
s
*
(
-
0.1687
*
r
-
0.3313
*
g
+
0.500
*
b
);
Cr
=
128
+
s
*
(
0.5
*
r
-
0.4187
*
g
-
0.0813
*
b
);
if
(
Cb
<
0
)
Cb
=
0
;
if
(
Cb
>
255
)
Cb
=
255
;
if
(
Cr
<
0
)
Cr
=
0
;
if
(
Cr
>
255
)
Cr
=
255
;
r
=
Y
+
1.402
*
(
Cr
-
128
);
g
=
Y
-
0.34414
*
(
Cb
-
128
)
-
0.71414
*
(
Cr
-
128
);
b
=
Y
+
1.772
*
(
Cb
-
128
);
if
(
r
<
0
)
r
=
0
;
if
(
r
>
255
)
r
=
255
;
if
(
g
<
0
)
g
=
0
;
if
(
g
>
255
)
g
=
255
;
if
(
b
<
0
)
b
=
0
;
if
(
b
>
255
)
b
=
255
;
iPixels
[
4
*
(
width
*
y
+
x
)
+
0
]
=
r
;
iPixels
[
4
*
(
width
*
y
+
x
)
+
1
]
=
g
;
iPixels
[
4
*
(
width
*
y
+
x
)
+
2
]
=
b
;
iPixels
[
4
*
(
width
*
y
+
x
)
+
3
]
=
255
;
}
// RGB -> YCbCr x SATURATION -> RGB
// Taking SATURATION[0] = 1/GAMMA[0] (green pixel of GR-line)
//saturation(ctx,SATURATION[0]);
}
ctx
.
putImageData
(
inputImage
,
0
,
0
);
Elphel
.
drawScaled
(
cnv_working
,
cnv_display
,
settings
.
width
);
console
.
log
(
elem
.
attr
(
"id"
)
+
" processing finished, total time: "
+
(
Date
.
now
()
-
t0
)
/
1000
+
" s"
);
$
(
this
).
trigger
(
"canvas_ready"
);
//get_image();
}
function
parseEXIFMakerNote
(
src
){
var
exif_orientation
=
EXIF
.
getTag
(
src
,
"Orientation"
);
//console.log("Exif:Orientation: "+exif_orientation);
var
MakerNote
=
EXIF
.
getTag
(
src
,
"MakerNote"
);
//FLIPH & FLIPV
if
(
typeof
MakerNote
!==
'undefined'
){
FLIPH
=
(
MakerNote
[
10
]
)
&
0x1
;
FLIPV
=
(
MakerNote
[
10
]
>>
1
)
&
0x1
;
var
tmpBayerMosaic
=
Array
();
for
(
var
i
=
0
;
i
<
BayerMosaic
.
length
;
i
++
){
tmpBayerMosaic
[
i
]
=
BayerMosaic
[
i
].
slice
();}
});
}
function
quickestPreview
(
ctx
){
Elphel
.
reorderJP4Blocks
(
ctx
,
"JP4"
,
settings
.
mosaic
,
settings
.
fast
);
}
if
(
FLIPV
==
1
){
for
(
i
=
0
;
i
<
4
;
i
++
){
BayerMosaic
[(
i
>>
1
)][(
i
%
2
)]
=
tmpBayerMosaic
[
1
-
(
i
>>
1
)][(
i
%
2
)];}
for
(
i
=
0
;
i
<
BayerMosaic
.
length
;
i
++
){
tmpBayerMosaic
[
i
]
=
BayerMosaic
[
i
].
slice
();}
}
if
(
FLIPH
==
1
){
for
(
i
=
0
;
i
<
4
;
i
++
){
BayerMosaic
[(
i
>>
1
)][(
i
%
2
)]
=
tmpBayerMosaic
[(
i
>>
1
)][
1
-
(
i
%
2
)];}
}
}
//console.log("MakerNote: Flips: V:"+FLIPV+" H:"+FLIPH);
//COLOR_MODE ----------------------------------------------------------------
if
(
typeof
MakerNote
!==
'undefined'
)
COLOR_MODE
=
(
MakerNote
[
10
]
>>
4
)
&
0x0f
;
switch
(
COLOR_MODE
){
case
2
:
IS_JP46
=
true
;
break
;
case
5
:
IS_JP4
=
true
;
break
;
//default:
}
//var gains = Array();
//var blacks = Array();
var
gammas
=
Array
();
//var gamma_scales = Array();
//var blacks256 = Array();
//var rgammas = Array();
//SATURATION
----------------------------------------------------------------
if
(
typeof
MakerNote
!==
'undefined'
){
for
(
i
=
0
;
i
<
4
;
i
++
){
//gains[i]= MakerNote[i]/65536.0;
//blacks[i]=(MakerNote[i+4]>>24)/256.0;
gammas
[
i
]
=
((
MakerNote
[
i
+
4
]
>>
16
)
&
0xff
)
/
100.0
;
//gamma_scales[i]=MakerNote[i+4] & 0xffff
;
}
/*
for (i=0;i<4;i++) {
rgammas[i]=elphel_gamma_calc(gammas[i], blacks[i], gamma_scales[i]);
}
console.log(rgammas
);
//adjusting gains to have the result picture in the range 0..256
min_gain=2.0*gains[0]
;
for (i=0;i<4;i++){
if (min_gain > (gains[i]*(1.0-blacks[i]))) min_gain = gains[i]*(1.0-blacks[i]);
}
for (i=0;i<4;i++) gains[i]/=min_gain;
for (i=0;i<4;i++) blacks256[i]=256.0*blacks[i];
*/
for
(
i
=
0
;
i
<
4
;
i
++
)
SATURATION
[
i
]
=
1
/
gammas
[
i
]
;
//console.log("MakerNote: Saturations: "+SATURATION[0]+" "+SATURATION[1]+" "+SATURATION[2]+" "+SATURATION[3])
;
}
function
parseEXIFMakerNote
(
src
){
var
exif_orientation
=
EXIF
.
getTag
(
src
,
"Orientation"
);
//console.log("Exif:Orientation: "+exif_orientation);
var
MakerNote
=
EXIF
.
getTag
(
src
,
"MakerNote"
);
//FLIPH & FLIPV
if
(
typeof
MakerNote
!==
'undefined'
){
FLIPH
=
(
MakerNote
[
10
]
)
&
0x1
;
FLIPV
=
(
MakerNote
[
10
]
>>
1
)
&
0x1
;
var
tmpBAYER
=
Array
();
for
(
var
i
=
0
;
i
<
BAYER
.
length
;
i
++
){
tmpBAYER
[
i
]
=
BAYER
[
i
].
slice
();}
if
(
FLIPV
==
1
){
for
(
i
=
0
;
i
<
4
;
i
++
){
BAYER
[(
i
>>
1
)][(
i
%
2
)]
=
tmpBAYER
[
1
-
(
i
>>
1
)][(
i
%
2
)];
}
for
(
i
=
0
;
i
<
BAYER
.
length
;
i
++
){
tmpBAYER
[
i
]
=
BAYER
[
i
].
slice
();}
}
if
(
FLIPH
==
1
){
for
(
i
=
0
;
i
<
4
;
i
++
){
BAYER
[(
i
>>
1
)][(
i
%
2
)]
=
tmpBAYER
[(
i
>>
1
)][
1
-
(
i
%
2
)];}
}
}
//console.log("MakerNote: Flips: V:"+FLIPV+" H:"+FLIPH);
//COLOR_MODE
----------------------------------------------------------------
var
color_mode
=
0
;
if
(
typeof
MakerNote
!==
'undefined'
)
color_mode
=
(
MakerNote
[
10
]
>>
4
)
&
0x0f
;
switch
(
color_mode
){
case
2
:
IMAGE_FORMAT
=
"JP46"
;
break
;
case
5
:
IMAGE_FORMAT
=
"JP4"
;
break
;
//default:
}
//var gains = Array();
//var blacks = Array();
var
gammas
=
Array
(
);
//var gamma_scales = Array();
//var blacks256 = Array()
;
//var rgammas = Array();
//SATURATION ----------------------------------------------------------------
if
(
typeof
MakerNote
!==
'undefined'
){
for
(
i
=
0
;
i
<
4
;
i
++
){
//gains[i]= MakerNote[i]/65536.0
;
//blacks[i]=(MakerNote[i+4]>>24)/256.0
;
gammas
[
i
]
=
((
MakerNote
[
i
+
4
]
>>
16
)
&
0xff
)
/
100.0
;
//gamma_scales[i]=MakerNote[i+4] & 0xffff;
}
/*
function elphel_gamma_calc(gamma,black,gamma_scale){
gtable = Array();
rgtable = Array();
black256=black*256.0;
k=1.0/(256.0-black256);
if (gamma < 0.13) gamma=0.13;
if (gamma >10.0) gamma=10.0;
for (var i=0;i<257;i++) {
x=k*(i-black256);
if (x<0.0) x=0.0;
ig = 0.5+65535.0*Math.pow(x,gamma);
ig = (ig*gamma_scale)/0x400;
if (ig>0xffff) ig=0xffff;
gtable[i]=ig;
}
// now gtable[] is the same as was used in the camera
// FPGA was using linear interpolation between elements of the gamma table, so now we'll reverse that process
indx=0;
for (i=0;i<256;i++) {
outValue=128+(i<<8);
while ((gtable[indx+1]<outValue) && (indx<256)) indx++;
if (indx>=256) rgtable[i]=65535.0/256;
else if (gtable[indx+1]==gtable[indx])
rgtable[i]=i;
else
rgtable[i]=indx+(1.0*(outValue-gtable[indx]))/(gtable[indx+1] - gtable[indx]);
}
return rgtable;
for (i=0;i<4;i++) {
rgammas[i]=elphel_gamma_calc(gammas[i], blacks[i], gamma_scales[i]);
}
console.log(rgammas);
//adjusting gains to have the result picture in the range 0..256
min_gain=2.0*gains[0];
for (i=0;i<4;i++){
if (min_gain > (gains[i]*(1.0-blacks[i]))) min_gain = gains[i]*(1.0-blacks[i]);
}
for (i=0;i<4;i++) gains[i]/=min_gain;
for (i=0;i<4;i++) blacks256[i]=256.0*blacks[i];
*/
for
(
i
=
0
;
i
<
4
;
i
++
)
{
//SATURATION[i] = 1/gammas[i];
//SATURATION[i] = 1.75; //nightmate time
SATURATION
[
i
]
=
2
;
}
//console.log("MakerNote: Saturations: "+SATURATION[0]+" "+SATURATION[1]+" "+SATURATION[2]+" "+SATURATION[3]);
}
}
/*
function elphel_gamma_calc(gamma,black,gamma_scale){
gtable = Array();
rgtable = Array();
black256=black*256.0;
k=1.0/(256.0-black256);
if (gamma < 0.13) gamma=0.13;
if (gamma >10.0) gamma=10.0;
for (var i=0;i<257;i++) {
x=k*(i-black256);
if (x<0.0) x=0.0;
ig = 0.5+65535.0*Math.pow(x,gamma);
ig = (ig*gamma_scale)/0x400;
if (ig>0xffff) ig=0xffff;
gtable[i]=ig;
}
// now gtable[] is the same as was used in the camera
// FPGA was using linear interpolation between elements of the gamma table, so now we'll reverse that process
indx=0;
for (i=0;i<256;i++) {
outValue=128+(i<<8);
while ((gtable[indx+1]<outValue) && (indx<256)) indx++;
if (indx>=256) rgtable[i]=65535.0/256;
else if (gtable[indx+1]==gtable[indx])
rgtable[i]=i;
else
rgtable[i]=indx+(1.0*(outValue-gtable[indx]))/(gtable[indx+1] - gtable[indx]);
}
return rgtable;
}
*/
};
$
.
fn
.
jp4
=
function
(
options
){
var
element
=
$
(
this
);
return
this
;
};
}(
jQuery
));
// Return early if this element already has a plugin instance
if
(
element
.
data
(
'jp4'
))
return
element
.
data
(
'jp4'
);
var
jp4
=
new
JP4
(
this
,
options
);
element
.
data
(
'jp4'
,
jp4
);
var
res
=
new
Object
();
res
.
cnv
=
element
;
res
.
data
=
jp4
;
return
res
;
};
}(
jQuery
));
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment