1+ import type { Mat } from "./types/opencv/Mat" ;
2+ import type { int } from "./types/opencv/_types" ;
3+
4+ declare global {
5+ interface Mat {
6+ reshape ( cn : int , rows ?: int ) : Mat ;
7+ }
8+ }
9+
10+ // Extend Mat prototype with reshape method
11+ export function extendMatWithReshape ( ) {
12+ if ( typeof global !== 'undefined' && global . cv && global . cv . Mat ) {
13+ const MatPrototype = global . cv . Mat . prototype ;
14+
15+ if ( ! MatPrototype . reshape ) {
16+ MatPrototype . reshape = function ( cn : int , rows ?: int ) : Mat {
17+ // Get current matrix properties
18+ const currentRows = this . rows ;
19+ const currentCols = this . cols ;
20+ const currentChannels = this . channels ( ) ;
21+ const currentType = this . type ( ) ;
22+ const currentDepth = currentType & 7 ; // Extract depth (CV_8U, CV_16S, etc.)
23+
24+ const totalDataElements = currentRows * currentCols * currentChannels ;
25+
26+ let newChannels : int ;
27+ let newRows : int ;
28+ let newCols : int ;
29+
30+ // OpenCV reshape semantics:
31+ // - cn = -1 means "auto-calculate channels"
32+ // - rows = -1 or undefined means "auto-calculate rows"
33+ // - The total number of elements must remain constant
34+
35+ if ( cn === - 1 ) {
36+ // Auto-calculate channels based on rows
37+ if ( rows === undefined || rows === 0 ) {
38+ throw new Error ( "When cn=-1, rows parameter must be specified" ) ;
39+ }
40+
41+ newRows = rows ;
42+ // Calculate how many elements per row we need
43+ const elementsPerRow = totalDataElements / newRows ;
44+ if ( Math . floor ( elementsPerRow ) !== elementsPerRow ) {
45+ throw new Error ( `Cannot reshape: total elements (${ totalDataElements } ) not evenly divisible by rows (${ newRows } )` ) ;
46+ }
47+
48+ // Try to fit this into a reasonable matrix structure
49+ // First, try to keep channels as 1 (most common case for vectorization)
50+ newChannels = 1 ;
51+ newCols = elementsPerRow ;
52+
53+ // If that creates too many columns, try other channel arrangements
54+ if ( newCols > 10000 ) { // Arbitrary large number check
55+ // Try to use original channels if it makes sense
56+ if ( elementsPerRow % currentChannels === 0 ) {
57+ newChannels = currentChannels ;
58+ newCols = elementsPerRow / currentChannels ;
59+ } else {
60+ // Try common channel counts
61+ for ( const testChannels of [ 3 , 4 , 2 ] ) {
62+ if ( elementsPerRow % testChannels === 0 ) {
63+ newChannels = testChannels ;
64+ newCols = elementsPerRow / testChannels ;
65+ break ;
66+ }
67+ }
68+ }
69+ }
70+ } else {
71+ // Channels specified
72+ newChannels = cn ;
73+
74+ if ( rows === undefined || rows === 0 ) {
75+ // Auto-calculate rows - keep matrix as close to original as possible
76+ const matrixElements = totalDataElements / newChannels ;
77+ if ( Math . floor ( matrixElements ) !== matrixElements ) {
78+ throw new Error ( `Cannot reshape: total elements (${ totalDataElements } ) not evenly divisible by channels (${ newChannels } )` ) ;
79+ }
80+
81+ // Try to keep close to original shape
82+ newRows = currentRows ;
83+ newCols = matrixElements / newRows ;
84+
85+ if ( Math . floor ( newCols ) !== newCols ) {
86+ // Original shape doesn't work, find best factorization
87+ newRows = Math . floor ( Math . sqrt ( matrixElements ) ) ;
88+ newCols = Math . floor ( matrixElements / newRows ) ;
89+
90+ if ( newRows * newCols !== matrixElements ) {
91+ for ( let r = 1 ; r <= matrixElements ; r ++ ) {
92+ if ( matrixElements % r === 0 ) {
93+ newRows = r ;
94+ newCols = matrixElements / r ;
95+ break ;
96+ }
97+ }
98+ }
99+ }
100+ } else {
101+ // Both channels and rows specified
102+ newRows = rows ;
103+ const matrixElements = totalDataElements / newChannels ;
104+ if ( Math . floor ( matrixElements ) !== matrixElements ) {
105+ throw new Error ( `Cannot reshape: total elements (${ totalDataElements } ) not evenly divisible by channels (${ newChannels } )` ) ;
106+ }
107+
108+ newCols = matrixElements / newRows ;
109+ if ( Math . floor ( newCols ) !== newCols ) {
110+ throw new Error ( `Cannot reshape: matrix elements (${ matrixElements } ) not evenly divisible by rows (${ newRows } )` ) ;
111+ }
112+ }
113+ }
114+
115+ // Final validation
116+ if ( newRows * newCols * newChannels !== totalDataElements ) {
117+ throw new Error ( `Reshape validation failed: ${ newRows } × ${ newCols } × ${ newChannels } = ${ newRows * newCols * newChannels } ≠ ${ totalDataElements } ` ) ;
118+ }
119+
120+ // Create the new matrix type
121+ let newType : int ;
122+ switch ( newChannels ) {
123+ case 1 :
124+ newType = currentDepth ;
125+ break ;
126+ case 2 :
127+ newType = currentDepth + 8 ;
128+ break ;
129+ case 3 :
130+ newType = currentDepth + 16 ;
131+ break ;
132+ case 4 :
133+ newType = currentDepth + 24 ;
134+ break ;
135+ default :
136+ newType = currentDepth + ( ( newChannels - 1 ) << 3 ) ;
137+ break ;
138+ }
139+
140+ try {
141+ // Create new matrix with calculated dimensions
142+ const result = new global . cv . Mat ( newRows , newCols , newType ) ;
143+
144+ // Copy all the data (should be same amount, just organized differently)
145+ const srcData = this . data ;
146+ const dstData = result . data ;
147+ const copyLength = Math . min ( srcData . length , dstData . length ) ;
148+
149+ for ( let i = 0 ; i < copyLength ; i ++ ) {
150+ dstData [ i ] = srcData [ i ] ;
151+ }
152+
153+ return result ;
154+ } catch ( error ) {
155+ throw new Error ( `Failed to create reshaped matrix: ${ error instanceof Error ? error . message : String ( error ) } ` ) ;
156+ }
157+ } ;
158+ }
159+ }
160+ }
0 commit comments